diff --git a/backend/common/media_store/store.go b/backend/common/media_store/store.go
index 119f4c8..9b16503 100644
--- a/backend/common/media_store/store.go
+++ b/backend/common/media_store/store.go
@@ -14,7 +14,24 @@ type Store []VideoInfo
type VideoInfo struct {
Hash string
Name string
- Duration uint
+ Duration int64
+}
+
+// Price
+func (info VideoInfo) Price() int64 {
+ min := int64(10)
+ // if duration is less than 300 seconds, return 10
+ if info.Duration < 5*60 {
+ return min * 100
+ }
+
+ // 每多一分钟,价格加 3, 如果余数大于 30 秒,按一分钟计算
+ cells := (info.Duration - 5*30) / 60
+ if info.Duration%60 > 30 {
+ cells++
+ }
+
+ return (min + cells*3) * 100
}
func NewStore(path string) (Store, error) {
diff --git a/backend/database/migrations/20241128075611_init.sql b/backend/database/migrations/20241128075611_init.sql
index 24c1eb0..ecd74d5 100644
--- a/backend/database/migrations/20241128075611_init.sql
+++ b/backend/database/migrations/20241128075611_init.sql
@@ -68,7 +68,7 @@ CREATE INDEX idx_user_balance_histories_tenant_id ON user_balance_histories (ten
CREATE TABLE
medias (
id SERIAL8 PRIMARY KEY,
- uuid uuid NOT NULL,
+ hash VARCHAR(128) NOT NULL UNIQUE,
tenant_id INT8 NOT NULL,
title VARCHAR(198) NOT NULL,
description VARCHAR(198) NOT NULL default '',
@@ -106,6 +106,5 @@ DROP TABLE users_tenants;
DROP TABLE tenant_user_balances;
DROP TABLE user_balance_histories;
DROP TABLE medias;
-DROP TABLE media_resources;
DROP TABLE user_medias;
-- +goose StatementEnd
diff --git a/backend/database/models/qvyun/public/model/medias.go b/backend/database/models/qvyun/public/model/medias.go
index f51ab90..a0dba7c 100644
--- a/backend/database/models/qvyun/public/model/medias.go
+++ b/backend/database/models/qvyun/public/model/medias.go
@@ -9,13 +9,12 @@ package model
import (
"backend/pkg/pg"
- "github.com/google/uuid"
"time"
)
type Medias struct {
ID int64 `sql:"primary_key" json:"id"`
- UUID uuid.UUID `json:"uuid"`
+ Hash string `json:"hash"`
TenantID int64 `json:"tenant_id"`
Title string `json:"title"`
Description string `json:"description"`
diff --git a/backend/database/models/qvyun/public/table/medias.go b/backend/database/models/qvyun/public/table/medias.go
index 5dd9881..c98b6b2 100644
--- a/backend/database/models/qvyun/public/table/medias.go
+++ b/backend/database/models/qvyun/public/table/medias.go
@@ -18,7 +18,7 @@ type mediasTable struct {
// Columns
ID postgres.ColumnInteger
- UUID postgres.ColumnString
+ Hash postgres.ColumnString
TenantID postgres.ColumnInteger
Title postgres.ColumnString
Description postgres.ColumnString
@@ -69,7 +69,7 @@ func newMediasTable(schemaName, tableName, alias string) *MediasTable {
func newMediasTableImpl(schemaName, tableName, alias string) mediasTable {
var (
IDColumn = postgres.IntegerColumn("id")
- UUIDColumn = postgres.StringColumn("uuid")
+ HashColumn = postgres.StringColumn("hash")
TenantIDColumn = postgres.IntegerColumn("tenant_id")
TitleColumn = postgres.StringColumn("title")
DescriptionColumn = postgres.StringColumn("description")
@@ -79,8 +79,8 @@ func newMediasTableImpl(schemaName, tableName, alias string) mediasTable {
ResourcesColumn = postgres.StringColumn("resources")
CreatedAtColumn = postgres.TimestampColumn("created_at")
UpdatedAtColumn = postgres.TimestampColumn("updated_at")
- allColumns = postgres.ColumnList{IDColumn, UUIDColumn, TenantIDColumn, TitleColumn, DescriptionColumn, PriceColumn, DiscountColumn, PublishColumn, ResourcesColumn, CreatedAtColumn, UpdatedAtColumn}
- mutableColumns = postgres.ColumnList{UUIDColumn, TenantIDColumn, TitleColumn, DescriptionColumn, PriceColumn, DiscountColumn, PublishColumn, ResourcesColumn, CreatedAtColumn, UpdatedAtColumn}
+ allColumns = postgres.ColumnList{IDColumn, HashColumn, TenantIDColumn, TitleColumn, DescriptionColumn, PriceColumn, DiscountColumn, PublishColumn, ResourcesColumn, CreatedAtColumn, UpdatedAtColumn}
+ mutableColumns = postgres.ColumnList{HashColumn, TenantIDColumn, TitleColumn, DescriptionColumn, PriceColumn, DiscountColumn, PublishColumn, ResourcesColumn, CreatedAtColumn, UpdatedAtColumn}
)
return mediasTable{
@@ -88,7 +88,7 @@ func newMediasTableImpl(schemaName, tableName, alias string) mediasTable {
//Columns
ID: IDColumn,
- UUID: UUIDColumn,
+ Hash: HashColumn,
TenantID: TenantIDColumn,
Title: TitleColumn,
Description: DescriptionColumn,
diff --git a/backend/fixtures/audio.html b/backend/fixtures/audio.html
new file mode 100644
index 0000000..9294a16
--- /dev/null
+++ b/backend/fixtures/audio.html
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+ Document
+
+
+
+
+
+
+
+
+
+ 0:00
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/fixtures/video.html b/backend/fixtures/video.html
new file mode 100644
index 0000000..8ea6774
--- /dev/null
+++ b/backend/fixtures/video.html
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+ Document
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/modules/medias/service.go b/backend/modules/medias/service.go
index 9e57094..1aa1bf6 100644
--- a/backend/modules/medias/service.go
+++ b/backend/modules/medias/service.go
@@ -5,13 +5,13 @@ import (
"database/sql"
"path/filepath"
+ "backend/common/media_store"
"backend/database/models/qvyun/public/table"
"backend/pkg/path"
"backend/pkg/pg"
"backend/providers/storage"
. "github.com/go-jet/jet/v2/postgres"
- "github.com/google/uuid"
"github.com/pkg/errors"
"github.com/samber/lo"
"github.com/sirupsen/logrus"
@@ -162,75 +162,40 @@ func (svc *Service) HasUserBought(ctx context.Context, tenantId, userId, mediaId
return mediaID > 0, nil
}
-// UnPublishTenantWithNotInUUIDs
-func (svc *Service) UnPublishTenantWithNotInUUIDs(ctx context.Context, tenantId int64, uuids []string) error {
- log := svc.log.WithField("method", "UnPublishTenantWithNotInUUIDs")
-
- tbl := table.Medias
- stmt := tbl.
- UPDATE().
- SET(
- tbl.Publish.SET(Bool(false)),
- ).
- WHERE(
- tbl.TenantID.EQ(Int(tenantId)).AND(
- tbl.UUID.NOT_IN(lo.Map(uuids, func(item string, _ int) Expression {
- return String(item)
- })...),
- ),
- )
- log.Debug(stmt.DebugSql())
-
- if _, err := stmt.ExecContext(ctx, svc.db); err != nil {
- return errors.Wrap(err, "unpublish tenant with not in uuids")
- }
-
- return nil
-}
-
-// GetTenantUUIDs
-func (svc *Service) GetTenantUUIDs(ctx context.Context, tenantId int64) ([]string, error) {
- log := svc.log.WithField("method", "GetTenantUUIDs")
-
- tbl := table.Medias
- stmt := tbl.
- SELECT(tbl.UUID).
- WHERE(tbl.TenantID.EQ(Int(tenantId)))
- log.Debug(stmt.DebugSql())
-
- var uuids []string
- if err := stmt.QueryContext(ctx, svc.db, &uuids); err != nil {
- return nil, errors.Wrap(err, "query tenant uuids")
- }
-
- return uuids, nil
-}
-
-// PublishTenant
-func (svc *Service) PublishTenantMedia(ctx context.Context, tenantId int64, uuid uuid.UUID, name string, price uint) error {
- log := svc.log.WithField("method", "PublishTenant")
+// Upsert
+func (svc *Service) Upsert(ctx context.Context, tenantId int64, item media_store.VideoInfo) error {
+ log := svc.log.WithField("method", "Upsert")
resources := pg.MediaResources{}
- if path.DirExists(filepath.Join(svc.storageConfig.Path, uuid.String(), pg.MediaTypeVideo.String())) {
+ if path.DirExists(filepath.Join(svc.storageConfig.Path, item.Hash, pg.MediaTypeVideo.String())) {
resources = append(resources, pg.MediaTypeVideo)
}
- if path.DirExists(filepath.Join(svc.storageConfig.Path, uuid.String(), pg.MediaTypeAudio.String())) {
+ if path.DirExists(filepath.Join(svc.storageConfig.Path, item.Hash, pg.MediaTypeAudio.String())) {
resources = append(resources, pg.MediaTypeAudio)
}
- if path.DirExists(filepath.Join(svc.storageConfig.Path, uuid.String(), pg.MediaTypePdf.String())) {
+ if path.DirExists(filepath.Join(svc.storageConfig.Path, item.Hash, pg.MediaTypePdf.String())) {
resources = append(resources, pg.MediaTypePdf)
}
tbl := table.Medias
stmt := tbl.
- INSERT(tbl.TenantID, tbl.UUID, tbl.Title, tbl.Price, tbl.Resources, tbl.Publish).
- VALUES(Int(tenantId), UUID(uuid), String(name), Int(int64(price)), Json(resources.MustValue()), Bool(true))
+ INSERT(tbl.TenantID, tbl.Hash, tbl.Title, tbl.Price, tbl.Resources, tbl.Publish).
+ VALUES(Int(tenantId), String(item.Hash), String(item.Name), Int(item.Price()), Json(resources), Bool(true)).
+ ON_CONFLICT(tbl.Hash).
+ DO_UPDATE(
+ SET(
+ tbl.Title.SET(String(item.Name)),
+ tbl.Price.SET(Int(item.Price())),
+ tbl.Resources.SET(Json(resources)),
+ tbl.Publish.SET(Bool(true)),
+ ),
+ )
log.Debug(stmt.DebugSql())
if _, err := stmt.ExecContext(ctx, svc.db); err != nil {
- return errors.Wrap(err, "publish tenant")
+ return errors.Wrapf(err, "upsert media: %s %s", item.Hash, item.Name)
}
return nil
diff --git a/backend/modules/medias/service_test.go b/backend/modules/medias/service_test.go
index 29e18d5..e450198 100644
--- a/backend/modules/medias/service_test.go
+++ b/backend/modules/medias/service_test.go
@@ -8,10 +8,7 @@ import (
"backend/database/models/qvyun/public/table"
"backend/fixtures"
dbUtil "backend/pkg/db"
- "backend/providers/storage"
- . "github.com/go-jet/jet/v2/postgres"
- "github.com/google/uuid"
"github.com/samber/lo"
. "github.com/smartystreets/goconvey/convey"
)
@@ -52,158 +49,3 @@ func TestService_GetUserBoughtMedias(t *testing.T) {
})
})
}
-
-func TestService_List(t *testing.T) {
- Convey("TestService_list", t, func() {
- db, err := fixtures.GetDB()
- So(err, ShouldBeNil)
- defer db.Close()
-
- Convey("truncate all tables", func() {
- So(dbUtil.TruncateAllTables(context.TODO(), db, "medias", "media_resources", "user_medias"), ShouldBeNil)
- })
-
- Convey("insert user_medias data", func() {
- items := []model.UserMedias{
- {UserID: 1, TenantID: 1, MediaID: 1, Price: 10},
- }
-
- tbl := table.UserMedias
- stmt := tbl.INSERT(tbl.UserID, tbl.TenantID, tbl.MediaID, tbl.Price).MODELS(items)
-
- _, err := stmt.Exec(db)
- So(err, ShouldBeNil)
- })
-
- Convey("insert medias data", func() {
- items := []model.Medias{
- {
- UUID: uuid.New(),
- TenantID: 1,
- Title: "title1",
- Description: "hello",
- Price: 100,
- Publish: true,
- },
- {
- UUID: uuid.New(),
- TenantID: 1,
- Title: "title2",
- Description: "hello",
- Price: 100,
- Publish: true,
- },
- {
- UUID: uuid.New(),
- TenantID: 1,
- Title: "title3",
- Description: "hello",
- Price: 100,
- Publish: false,
- },
- }
-
- tbl := table.Medias
- stmt := tbl.INSERT(
- tbl.UUID,
- tbl.TenantID,
- tbl.Title,
- tbl.Description,
- tbl.Price,
- tbl.Publish,
- ).MODELS(items)
- t.Log(stmt.DebugSql())
-
- _, err := stmt.Exec(db)
- So(err, ShouldBeNil)
- })
-
- Convey("get list", func() {
- svc := &Service{db: db}
- So(svc.Prepare(), ShouldBeNil)
-
- items, err := svc.List(context.TODO(), 1, 1, &ListFilter{
- Bought: lo.ToPtr(true),
- })
- So(err, ShouldBeNil)
-
- t.Logf("items: %+v", items)
- So(items, ShouldHaveLength, 1)
- })
- })
-}
-
-func TestService_List1(t *testing.T) {
- Convey("TestService_list", t, func() {
- db, err := fixtures.GetDB()
- So(err, ShouldBeNil)
- defer db.Close()
-
- tbl := table.Medias
- stmt := tbl.
- SELECT(tbl.AllColumns).
- WHERE(
- tbl.Publish.EQ(Bool(true)).AND(
- tbl.TenantID.EQ(Int(1)),
- ),
- ).
- WHERE(tbl.ID.EQ(Int(1))).
- ORDER_BY(tbl.ID.DESC())
-
- t.Log(stmt.DebugSql())
-
- var dest []ListItem
- err = stmt.QueryContext(context.TODO(), db, &dest)
- So(err, ShouldBeNil)
-
- t.Logf("dest: %+v", dest)
- })
-}
-
-func TestService_PublishTenant(t *testing.T) {
- Convey("TestService_PublishTenant", t, func() {
- db, err := fixtures.GetDB()
- So(err, ShouldBeNil)
- defer db.Close()
-
- Convey("truncate all tables", func() {
- So(dbUtil.TruncateAllTables(context.TODO(), db, "medias"), ShouldBeNil)
- })
-
- u1, err := uuid.Parse("6c63c4d8-b2cb-4588-8dd3-9a2276918d17")
- So(err, ShouldBeNil)
-
- u2, err := uuid.Parse("8c183427-02b1-4426-ad65-18c6e7735072")
- So(err, ShouldBeNil)
-
- u3, err := uuid.Parse("24aa56a1-a85b-4a03-9a1b-c39a36103ddf")
- So(err, ShouldBeNil)
-
- Convey("insert some data", func() {
- items := []model.Medias{
- {UUID: u1, TenantID: 1, Title: "title1", Description: "hello", Price: 100, Publish: true},
- {UUID: u2, TenantID: 1, Title: "title2", Description: "hello", Price: 100, Publish: true},
- {UUID: u3, TenantID: 1, Title: "title3", Description: "hello", Price: 100, Publish: true},
- }
-
- Convey("publish tenant", func() {
- svc := &Service{
- db: db,
- storageConfig: &storage.Config{
- Path: "/projects/mp-qvyun/backend/fixtures/medias",
- },
- }
- So(svc.Prepare(), ShouldBeNil)
-
- err := svc.PublishTenantMedia(context.TODO(), 1, items[0].UUID, items[0].Title, 100)
- So(err, ShouldBeNil)
-
- err = svc.PublishTenantMedia(context.TODO(), 1, items[1].UUID, items[1].Title, 200)
- So(err, ShouldBeNil)
-
- err = svc.PublishTenantMedia(context.TODO(), 1, items[2].UUID, items[2].Title, 300)
- So(err, ShouldBeNil)
- })
- })
- })
-}
diff --git a/backend/modules/tasks/discover/discover_medias.go b/backend/modules/tasks/discover/discover_medias.go
index 5e78870..b8e2a82 100644
--- a/backend/modules/tasks/discover/discover_medias.go
+++ b/backend/modules/tasks/discover/discover_medias.go
@@ -5,6 +5,7 @@ import (
"crypto/md5"
"fmt"
"io"
+ "math"
"os"
"os/exec"
"path/filepath"
@@ -108,7 +109,7 @@ func (d *DiscoverMedias) processVideo(video string, to string) (media_store.Vide
if err != nil {
return info, errors.Wrapf(err, "get media duration: %s", video)
}
- info.Duration = uint(duration)
+ info.Duration = duration
return info, nil
}
@@ -295,7 +296,7 @@ func (d *DiscoverMedias) getPrice(dir string) (uint, error) {
}
// getMediaDuration get the duration of a media file
-func (d *DiscoverMedias) getMediaDuration(file string) (float64, error) {
+func (d *DiscoverMedias) getMediaDuration(file string) (int64, error) {
// ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input_video.mp4
args := []string{
"-v", "error",
@@ -315,7 +316,7 @@ func (d *DiscoverMedias) getMediaDuration(file string) (float64, error) {
return 0, errors.Wrapf(err, "get media duration: %s", file)
}
- return duration, nil
+ return int64(math.Floor(duration)), nil
}
// getMedias get the medias in the directory
diff --git a/backend/modules/tasks/store/store_medias.go b/backend/modules/tasks/store/store_medias.go
index 83ae59f..f90378a 100644
--- a/backend/modules/tasks/store/store_medias.go
+++ b/backend/modules/tasks/store/store_medias.go
@@ -1,6 +1,8 @@
package store
import (
+ "context"
+
"backend/common/media_store"
"backend/modules/medias"
@@ -27,7 +29,13 @@ func (d *StoreMedias) RunE(targetPath string) error {
if err != nil {
return errors.Wrapf(err, "new store: %s", targetPath)
}
- _ = store
+
+ for _, item := range store {
+ err := d.mediasSvc.Upsert(context.Background(), 1, item)
+ if err != nil {
+ d.log.WithError(err).Errorf("upsert media: %s - %s", item.Hash, item.Name)
+ }
+ }
return nil
}