724 lines
24 KiB
Go
724 lines
24 KiB
Go
package mediacontentser
|
|
|
|
import (
|
|
"91porn-server/common/log"
|
|
"91porn-server/common/stderr"
|
|
"91porn-server/models/v/mediamod"
|
|
"91porn-server/models/v/vidmod"
|
|
"91porn-server/web/service/updownloadser"
|
|
"errors"
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
|
|
"91porn-server/models/v/mediacontentmod"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
)
|
|
|
|
type WebListReq struct {
|
|
ID *string `form:"id" bson:"id,omitempty"` // ID
|
|
Name *string `form:"name" bson:"name,omitempty"` // 名称
|
|
Type *int `form:"type" bson:"type,omitempty"` // 类型 1:动漫,2:漫画
|
|
Status *int `form:"status" bson:"status,omitempty"` // 状态
|
|
IsActive *bool `form:"isActive" json:"isActive"` // 是否激活
|
|
MID *string `form:"mId" json:"mId" bson:"mId" binding:"required"` // 媒体资源ID
|
|
PageNumber uint64 `form:"pageNumber" json:"pageNumber" binding:"required,min=1"` // 当前页
|
|
PageSize uint64 `form:"pageSize" json:"pageSize" binding:"required,min=1,max=100"` // 每页条数
|
|
}
|
|
type WebListRes struct {
|
|
Total int64 `json:"total"`
|
|
List []*mediacontentmod.MediaContent `json:"list"`
|
|
}
|
|
|
|
func (q *WebListReq) Filter() bson.M {
|
|
var query = bson.M{}
|
|
if q.Name != nil {
|
|
query["name"] = primitive.Regex{Pattern: *q.Name, Options: "i"}
|
|
}
|
|
if q.Status != nil {
|
|
query["status"] = *q.Status
|
|
}
|
|
if q.MID != nil {
|
|
mid, _ := primitive.ObjectIDFromHex(*q.MID)
|
|
query["mediaId"] = mid
|
|
}
|
|
if q.Type != nil {
|
|
query["type"] = *q.Type
|
|
}
|
|
if q.IsActive != nil {
|
|
query["isActive"] = *q.IsActive
|
|
}
|
|
if q.ID != nil {
|
|
id, _ := primitive.ObjectIDFromHex(*q.ID)
|
|
query["_id"] = id
|
|
}
|
|
query["isDelete"] = false
|
|
return query
|
|
}
|
|
func (q *WebListReq) Options() *options.FindOptions {
|
|
return options.Find().SetSkip(int64((q.PageNumber - 1) * q.PageSize)).SetLimit(int64(q.PageSize)).SetSort(bson.M{"episodeNumber": 1})
|
|
}
|
|
|
|
// GetList 获取列表
|
|
func (q *WebListReq) GetList() (res *WebListRes, err error) {
|
|
var out = &WebListRes{}
|
|
count, err := mediacontentmod.QueryAllCount(q.Filter())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if count == 0 {
|
|
out.List = []*mediacontentmod.MediaContent{}
|
|
out.Total = 0
|
|
return out, nil
|
|
}
|
|
out.Total = count
|
|
out.List, err = mediacontentmod.QueryAllList(q.Filter(), q.Options())
|
|
if err != nil {
|
|
log.Error(fmt.Sprintf("mediacontentmod queryAllList error:%+v:", err))
|
|
return nil, err
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
type WebCreateReq struct {
|
|
MediaID primitive.ObjectID `json:"mediaId" form:"mediaId" binding:"required"` // 媒体资源ID
|
|
MediaType string `json:"mediaType" form:"mediaType" binding:"required"` // 媒体类型
|
|
EpisodeNumber uint `json:"episodeNumber" form:"episodeNumber" ` // 第几集
|
|
ListenPermission int `json:"listenPermission" form:"listenPermission" ` // 收听权限 0:会员 1:金币购买 2:免费
|
|
Price int64 `json:"price" form:"price" ` // 购买价格
|
|
Cover string `json:"cover" form:"cover"` //封面
|
|
Name string `json:"name" form:"name" ` // 章节名
|
|
Text string `json:"text" form:"text" ` // 内容(小说专用)
|
|
Md5 string `json:"md5" form:"md5" ` // 媒体md5
|
|
SourceID string `json:"sourceID" form:"sourceID"` // 视频唯一id,对应文件服
|
|
VideoUrl string `json:"videoUrl" form:"videoUrl" ` // 视频地址
|
|
AudioUrl string `json:"audioUrl" form:"audioUrl"` // 有声小说地址
|
|
UrlSet []string `json:"urlSet" form:"urlSet"` // 地址集(多个资源地址用 例如漫画)
|
|
Filename string `json:"filename" form:"filename"` // 文件名称
|
|
IsActive bool `json:"isActive" form:"isActive" ` // 是否激活
|
|
PreviewEnabled bool `json:"previewEnabled" form:"previewEnabled"` // 是否允许试看
|
|
PreviewStart int `json:"previewStart" form:"previewStart"` // 试看起始时间(秒)
|
|
PreviewSeconds int `json:"previewSeconds" form:"previewSeconds"` // 试看时长(秒)
|
|
}
|
|
|
|
const defaultDramaPreviewSeconds = 60
|
|
|
|
func normalizeDramaPreview(enabled bool, start, seconds int, playTime uint) (bool, int, int, error) {
|
|
if !enabled {
|
|
return false, 0, 0, nil
|
|
}
|
|
if start < 0 {
|
|
return false, 0, 0, errors.New("试看起始时间不能小于0")
|
|
}
|
|
if seconds < 0 {
|
|
return false, 0, 0, errors.New("试看时长不能小于0")
|
|
}
|
|
if seconds == 0 {
|
|
seconds = defaultDramaPreviewSeconds
|
|
}
|
|
if playTime > 0 && uint64(start)+uint64(seconds) > uint64(playTime) {
|
|
return false, 0, 0, errors.New("试看起始时间与时长之和不能超过视频总时长")
|
|
}
|
|
return true, start, seconds, nil
|
|
}
|
|
|
|
func validateDramaEpisodeState(episodeNumber int, listenPermission int, price int64, name, videoURL, h265URL string, active bool, status int) error {
|
|
if episodeNumber < 1 {
|
|
return errors.New("剧集编号必须大于0")
|
|
}
|
|
if strings.TrimSpace(name) == "" {
|
|
return errors.New("剧集名称不能为空")
|
|
}
|
|
if listenPermission != 1 && listenPermission != 2 {
|
|
return errors.New("短剧剧集listenPermission只支持1或2")
|
|
}
|
|
if listenPermission == 2 && price != 0 {
|
|
return errors.New("免费剧集价格必须为0")
|
|
}
|
|
if listenPermission == 1 && price <= 0 {
|
|
return errors.New("金币剧集价格必须大于0")
|
|
}
|
|
if active {
|
|
if status != 1 {
|
|
return errors.New("视频仍在转码,暂不能上架")
|
|
}
|
|
if strings.TrimSpace(videoURL) == "" && strings.TrimSpace(h265URL) == "" {
|
|
return errors.New("剧集没有播放资源,暂不能上架")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ensureEpisodeNumberUnique(mediaID primitive.ObjectID, episodeNumber int, excludeID primitive.ObjectID) error {
|
|
filter := bson.M{
|
|
"mediaId": mediaID,
|
|
"episodeNumber": episodeNumber,
|
|
"isDelete": false,
|
|
}
|
|
if !excludeID.IsZero() {
|
|
filter["_id"] = bson.M{"$ne": excludeID}
|
|
}
|
|
count, err := mediacontentmod.QueryAllCount(filter)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if count > 0 {
|
|
return errors.New("剧集编号已存在")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func fileStatusToEpisodeStatus(status string) int {
|
|
switch status {
|
|
case vidmod.ConvertCompleted, vidmod.Completed:
|
|
return 1
|
|
case vidmod.FileUploadError, vidmod.MergeError, vidmod.ConvertError:
|
|
return 2
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func mediaMetadataFromFs(id string) (vidmod.AwsPullResp, int, error) {
|
|
resp, err := updownloadser.PullFileInfo(id)
|
|
if err != nil {
|
|
return vidmod.AwsPullResp{}, 0, err
|
|
}
|
|
status := fileStatusToEpisodeStatus(resp.Data.Status)
|
|
return resp.Data, status, nil
|
|
}
|
|
|
|
// Create 发布数据
|
|
func (p *WebCreateReq) Create(manager string) (err error, vid primitive.ObjectID) {
|
|
now := time.Now()
|
|
media, err := mediamod.QueryMediaByID(bson.M{"_id": p.MediaID})
|
|
if err != nil {
|
|
return err, primitive.ObjectID{}
|
|
}
|
|
if media == nil || media.ID.IsZero() {
|
|
return errors.New("media info is nll"), primitive.ObjectID{}
|
|
}
|
|
if media.IsDelete || p.MediaType != media.MediaType {
|
|
return errors.New("剧集mediaType与父媒体不一致"), primitive.ObjectID{}
|
|
}
|
|
p.Name = strings.TrimSpace(p.Name)
|
|
|
|
if p.MediaType == mediamod.MediaTypeVideo || p.MediaType == mediamod.MediaTypeDrama {
|
|
vid, err = primitive.ObjectIDFromHex(p.SourceID)
|
|
if err != nil || vid.IsZero() {
|
|
return errors.New("sourceID不是有效的资源ID"), primitive.ObjectID{}
|
|
}
|
|
} else {
|
|
vid = primitive.NewObjectID()
|
|
}
|
|
if err = ensureEpisodeNumberUnique(media.ID, int(p.EpisodeNumber), primitive.NilObjectID); err != nil {
|
|
return err, primitive.ObjectID{}
|
|
}
|
|
if media.MediaType == mediamod.MediaTypeDrama {
|
|
if err = validateDramaEpisodeState(int(p.EpisodeNumber), p.ListenPermission, p.Price, p.Name, p.VideoUrl, "", false, 0); err != nil {
|
|
return err, primitive.ObjectID{}
|
|
}
|
|
if strings.TrimSpace(p.VideoUrl) == "" {
|
|
return errors.New("短剧剧集必须提供videoUrl"), primitive.ObjectID{}
|
|
}
|
|
}
|
|
previewEnabled, previewStart, previewSeconds := false, 0, 0
|
|
if media.MediaType == mediamod.MediaTypeDrama {
|
|
previewEnabled, previewStart, previewSeconds, err = normalizeDramaPreview(p.PreviewEnabled, p.PreviewStart, p.PreviewSeconds, 0)
|
|
if err != nil {
|
|
return err, primitive.ObjectID{}
|
|
}
|
|
}
|
|
mc := mediacontentmod.MediaContent{
|
|
ID: vid,
|
|
MediaID: p.MediaID,
|
|
MediaType: media.MediaType,
|
|
Cover: p.Cover,
|
|
EpisodeNumber: int(p.EpisodeNumber),
|
|
ListenPermission: p.ListenPermission,
|
|
Price: p.Price,
|
|
Name: p.Name,
|
|
IsActive: p.IsActive,
|
|
IsDelete: false,
|
|
UpdatedAct: manager,
|
|
UpdateTime: now,
|
|
CreatedAt: now,
|
|
PreviewEnabled: previewEnabled,
|
|
PreviewStart: previewStart,
|
|
PreviewSeconds: previewSeconds,
|
|
}
|
|
// 视频和短剧都使用视频资源。
|
|
if media.MediaType == mediamod.MediaTypeVideo || media.MediaType == mediamod.MediaTypeDrama {
|
|
mc.Md5 = p.Md5
|
|
mc.VideoUrl = strings.TrimSpace(p.VideoUrl)
|
|
mc.Status = 0
|
|
if p.IsActive {
|
|
metadata, status, metadataErr := mediaMetadataFromFs(vid.Hex())
|
|
if metadataErr != nil {
|
|
return errors.New("文件服务连接失败,暂不能上架剧集"), primitive.ObjectID{}
|
|
}
|
|
mc.Status = status
|
|
mc.PlayTime = metadata.PlayTime
|
|
mc.Weight = metadata.Width
|
|
mc.Height = metadata.Height
|
|
mc.MediaSize = int64(metadata.Size)
|
|
if metadata.CheckSum != "" {
|
|
mc.Md5 = metadata.CheckSum
|
|
}
|
|
mc.Ratio = metadata.Ratio
|
|
if mc.Ratio == 0 && mc.Height > 0 {
|
|
mc.Ratio = float64(mc.Weight) / float64(mc.Height)
|
|
}
|
|
if media.MediaType == mediamod.MediaTypeDrama {
|
|
mc.PreviewEnabled, mc.PreviewStart, mc.PreviewSeconds, err = normalizeDramaPreview(mc.PreviewEnabled, mc.PreviewStart, mc.PreviewSeconds, mc.PlayTime)
|
|
if err != nil {
|
|
return err, primitive.ObjectID{}
|
|
}
|
|
if err = validateDramaEpisodeState(mc.EpisodeNumber, mc.ListenPermission, mc.Price, mc.Name, mc.VideoUrl, mc.H265Url, true, mc.Status); err != nil {
|
|
return err, primitive.ObjectID{}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// 图集
|
|
if media.MediaType == mediamod.MediaTypeImage {
|
|
mc.UrlSet = p.UrlSet
|
|
}
|
|
// 小说
|
|
if media.MediaType == mediamod.MediaTypeText {
|
|
mc.AudioUrl = p.AudioUrl
|
|
}
|
|
|
|
// 创建数据
|
|
if _, err := mediacontentmod.Insert(nil, mc); err != nil {
|
|
return err, primitive.ObjectID{}
|
|
}
|
|
|
|
err = OnMediaContentChange(media.ID, media.MediaType, true, mc.IsActive)
|
|
if err != nil {
|
|
return err, primitive.ObjectID{}
|
|
}
|
|
return nil, vid
|
|
}
|
|
|
|
type WebUpdateReq struct {
|
|
ID primitive.ObjectID `json:"id" binding:"required"`
|
|
EpisodeNumber *int `json:"episodeNumber"` // 第几集
|
|
ListenPermission *int `json:"listenPermission"` // 收听权限 0:会员 1:金币购买 2:免费
|
|
Price *int64 `json:"price"` // 购买价格
|
|
Name *string `json:"name"` // 章节名
|
|
Cover *string `json:"cover"` // 封面
|
|
Text *string `json:"text"` // 内容(小说专用)
|
|
Md5 *string `json:"md5"` // 媒体md5
|
|
VideoUrl *string `json:"videoUrl"` // 视频地址
|
|
H265Url *string `json:"h265Url"` // H.265视频地址
|
|
AudioUrl *string `json:"audioUrl"` // 有声小说地址
|
|
UrlSet *[]string `json:"urlSet"` // 地址集(多个资源地址用 例如漫画)
|
|
IsActive *bool `json:"isActive"` // 是否激活
|
|
PreviewEnabled *bool `json:"previewEnabled"` // 是否允许试看
|
|
PreviewStart *int `json:"previewStart"` // 试看起始时间(秒)
|
|
PreviewSeconds *int `json:"previewSeconds"` // 试看时长(秒)
|
|
}
|
|
|
|
// Update 更新数据
|
|
func (p *WebUpdateReq) Update(manager string) error {
|
|
mediaContent, err := mediacontentmod.GetInfo(p.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if mediaContent.ID.IsZero() {
|
|
return errors.New("media content is null")
|
|
}
|
|
media, err := mediamod.GetInfo(mediaContent.MediaID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
next := mediaContent
|
|
if p.EpisodeNumber != nil {
|
|
next.EpisodeNumber = *p.EpisodeNumber
|
|
}
|
|
if p.ListenPermission != nil {
|
|
next.ListenPermission = *p.ListenPermission
|
|
}
|
|
if p.Price != nil {
|
|
next.Price = *p.Price
|
|
}
|
|
if p.Name != nil {
|
|
next.Name = strings.TrimSpace(*p.Name)
|
|
}
|
|
if p.VideoUrl != nil {
|
|
next.VideoUrl = strings.TrimSpace(*p.VideoUrl)
|
|
}
|
|
if p.H265Url != nil {
|
|
next.H265Url = strings.TrimSpace(*p.H265Url)
|
|
}
|
|
if p.IsActive != nil {
|
|
next.IsActive = *p.IsActive
|
|
}
|
|
if p.PreviewEnabled != nil {
|
|
next.PreviewEnabled = *p.PreviewEnabled
|
|
}
|
|
if p.PreviewStart != nil {
|
|
next.PreviewStart = *p.PreviewStart
|
|
}
|
|
if p.PreviewSeconds != nil {
|
|
next.PreviewSeconds = *p.PreviewSeconds
|
|
}
|
|
var metadata *vidmod.AwsPullResp
|
|
if next.IsActive && (media.MediaType == mediamod.MediaTypeVideo || media.MediaType == mediamod.MediaTypeDrama) && next.Status != 1 {
|
|
loaded, status, metadataErr := mediaMetadataFromFs(mediaContent.ID.Hex())
|
|
if metadataErr != nil {
|
|
return errors.New("文件服务连接失败,暂不能上架剧集")
|
|
}
|
|
metadata = &loaded
|
|
next.Status = status
|
|
next.PlayTime = loaded.PlayTime
|
|
next.Weight = loaded.Width
|
|
next.Height = loaded.Height
|
|
next.MediaSize = int64(loaded.Size)
|
|
next.Ratio = loaded.Ratio
|
|
if next.Ratio == 0 && next.Height > 0 {
|
|
next.Ratio = float64(next.Weight) / float64(next.Height)
|
|
}
|
|
}
|
|
if media.MediaType == mediamod.MediaTypeDrama {
|
|
next.PreviewEnabled, next.PreviewStart, next.PreviewSeconds, err = normalizeDramaPreview(next.PreviewEnabled, next.PreviewStart, next.PreviewSeconds, next.PlayTime)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = validateDramaEpisodeState(next.EpisodeNumber, next.ListenPermission, next.Price, next.Name, next.VideoUrl, next.H265Url, next.IsActive, next.Status); err != nil {
|
|
return err
|
|
}
|
|
if err = ensureEpisodeNumberUnique(media.ID, next.EpisodeNumber, mediaContent.ID); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
data := make(map[string]interface{})
|
|
if p.EpisodeNumber != nil {
|
|
data["episodeNumber"] = *p.EpisodeNumber
|
|
}
|
|
if p.ListenPermission != nil {
|
|
data["listenPermission"] = *p.ListenPermission
|
|
}
|
|
if p.Price != nil {
|
|
data["price"] = *p.Price
|
|
}
|
|
if p.Name != nil {
|
|
data["name"] = next.Name
|
|
}
|
|
if p.Cover != nil {
|
|
data["cover"] = *p.Cover
|
|
}
|
|
if p.Text != nil {
|
|
data["text"] = *p.Text
|
|
}
|
|
if p.Md5 != nil {
|
|
data["md5"] = *p.Md5
|
|
}
|
|
if p.VideoUrl != nil {
|
|
data["videoUrl"] = next.VideoUrl
|
|
}
|
|
if p.H265Url != nil {
|
|
data["h265Url"] = next.H265Url
|
|
}
|
|
if p.AudioUrl != nil {
|
|
data["audioUrl"] = *p.AudioUrl
|
|
}
|
|
if p.UrlSet != nil {
|
|
data["urlSet"] = *p.UrlSet
|
|
}
|
|
|
|
if p.IsActive != nil {
|
|
data["isActive"] = *p.IsActive
|
|
}
|
|
if media.MediaType == mediamod.MediaTypeDrama && (p.PreviewEnabled != nil || p.PreviewStart != nil || p.PreviewSeconds != nil) {
|
|
data["previewEnabled"] = next.PreviewEnabled
|
|
data["previewStart"] = next.PreviewStart
|
|
data["previewSeconds"] = next.PreviewSeconds
|
|
}
|
|
if metadata != nil {
|
|
data["playTime"] = metadata.PlayTime
|
|
data["weight"] = metadata.Width
|
|
data["height"] = metadata.Height
|
|
data["mediaSize"] = metadata.Size
|
|
data["status"] = next.Status
|
|
data["ratio"] = next.Ratio
|
|
if metadata.CheckSum != "" {
|
|
data["md5"] = metadata.CheckSum
|
|
}
|
|
}
|
|
|
|
data["updatedAct"] = manager
|
|
data["updateTime"] = time.Now()
|
|
if _, err = mediacontentmod.UpdateByID(nil, p.ID, data); err != nil {
|
|
return err
|
|
}
|
|
published := p.IsActive != nil && *p.IsActive && !mediaContent.IsActive
|
|
err = OnMediaContentChange(media.ID, media.MediaType, false, published)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type WebBatchUpdateReq struct {
|
|
IDS []string `json:"ids" binding:"required"`
|
|
IsActive *bool `json:"isActive"` // 是否激活
|
|
Price *int64 `json:"price"` // 价格
|
|
Permission *int `json:"permission"` // 收听权限 0:会员 1:金币购买 2:免费
|
|
FreeEpisode *int `json:"freeEpisode"` // 前几集免费
|
|
}
|
|
|
|
func (p *WebBatchUpdateReq) Filter() []primitive.ObjectID {
|
|
ids, _ := p.ValidatedIDs()
|
|
return ids
|
|
}
|
|
|
|
func (p *WebBatchUpdateReq) ValidatedIDs() ([]primitive.ObjectID, error) {
|
|
if len(p.IDS) == 0 || len(p.IDS) > 100 {
|
|
return nil, errors.New("ids数量必须在1到100之间")
|
|
}
|
|
if p.IsActive == nil && p.Price == nil && p.Permission == nil && p.FreeEpisode == nil {
|
|
return nil, errors.New("至少需要一个批量更新字段")
|
|
}
|
|
if p.FreeEpisode != nil && *p.FreeEpisode < 0 {
|
|
return nil, errors.New("freeEpisode不能小于0")
|
|
}
|
|
ids := make([]primitive.ObjectID, 0, len(p.IDS))
|
|
seen := make(map[primitive.ObjectID]struct{}, len(p.IDS))
|
|
for _, raw := range p.IDS {
|
|
id, err := primitive.ObjectIDFromHex(raw)
|
|
if err != nil || id.IsZero() {
|
|
return nil, errors.New("ids包含无效的剧集ID")
|
|
}
|
|
if _, ok := seen[id]; ok {
|
|
continue
|
|
}
|
|
seen[id] = struct{}{}
|
|
ids = append(ids, id)
|
|
}
|
|
return ids, nil
|
|
}
|
|
|
|
func (p *WebBatchUpdateReq) applyContentFields(content mediacontentmod.MediaContent) mediacontentmod.MediaContent {
|
|
if p.IsActive != nil {
|
|
content.IsActive = *p.IsActive
|
|
}
|
|
if p.Price != nil {
|
|
content.Price = *p.Price
|
|
}
|
|
if p.Permission != nil {
|
|
content.ListenPermission = *p.Permission
|
|
}
|
|
return content
|
|
}
|
|
|
|
// Update 更新数据
|
|
func (p *WebBatchUpdateReq) Update(manager string) error {
|
|
ids, err := p.ValidatedIDs()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
contents, err := mediacontentmod.QueryAllList(bson.M{
|
|
"_id": bson.M{"$in": ids},
|
|
"isDelete": false,
|
|
}, options.Find().SetLimit(int64(len(ids))))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(contents) != len(ids) {
|
|
return errors.New("部分剧集不存在或已删除")
|
|
}
|
|
mediaID := contents[0].MediaID
|
|
mediaType := contents[0].MediaType
|
|
published := false
|
|
for _, content := range contents {
|
|
if content.MediaID != mediaID {
|
|
return errors.New("批量操作的剧集必须属于同一部短剧")
|
|
}
|
|
next := p.applyContentFields(*content)
|
|
if p.IsActive != nil && *p.IsActive && !content.IsActive {
|
|
published = true
|
|
}
|
|
validateState := p.Price != nil || p.Permission != nil || (p.IsActive != nil && *p.IsActive)
|
|
if validateState && mediaType == mediamod.MediaTypeDrama {
|
|
if err = validateDramaEpisodeState(next.EpisodeNumber, next.ListenPermission, next.Price, next.Name, next.VideoUrl, next.H265Url, next.IsActive, next.Status); err != nil {
|
|
return fmt.Errorf("第%d集:%w", content.EpisodeNumber, err)
|
|
}
|
|
}
|
|
}
|
|
data := make(map[string]interface{})
|
|
if p.IsActive != nil {
|
|
data["isActive"] = *p.IsActive
|
|
}
|
|
if p.Price != nil {
|
|
data["price"] = *p.Price
|
|
}
|
|
if p.Permission != nil {
|
|
data["listenPermission"] = *p.Permission
|
|
}
|
|
data["updatedAct"] = manager
|
|
data["updateTime"] = time.Now()
|
|
|
|
contentChanged := p.IsActive != nil || p.Price != nil || p.Permission != nil
|
|
if contentChanged {
|
|
if _, err := mediacontentmod.UpdateByIDS(nil, ids, data); err != nil {
|
|
return err
|
|
}
|
|
if err = OnMediaContentChange(mediaID, mediaType, false, published); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if p.FreeEpisode != nil {
|
|
if _, err = mediamod.UpdateByID(nil, mediaID, map[string]interface{}{
|
|
"freeEpisode": *p.FreeEpisode,
|
|
"updatedAct": manager,
|
|
"updateTime": time.Now(),
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type WebDeleteReq struct {
|
|
ID primitive.ObjectID `json:"id" binding:"required"`
|
|
}
|
|
|
|
// Delete 删除数据
|
|
func (p *WebDeleteReq) Delete() error {
|
|
mediaContent, err := mediacontentmod.GetInfo(p.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := mediacontentmod.DeleteByID(nil, p.ID); err != nil {
|
|
return err
|
|
}
|
|
|
|
err = OnMediaContentChange(mediaContent.MediaID, mediaContent.MediaType, false, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func OnMediaContentChange(mediaId primitive.ObjectID, mediaType string, contentAdded, published bool) (err error) {
|
|
// 获取当前最小的章节,重新update
|
|
defaultContent := mediamod.DefaultContent{}
|
|
if mediaType == mediamod.MediaTypeImage {
|
|
contentList, _, _, err := mediacontentmod.GetList(bson.M{"mediaId": mediaId, "isActive": true, "isDelete": false}, 0, 1, bson.D{{Key: "episodeNumber", Value: 1}, {Key: "createdAt", Value: 1}})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(contentList) > 0 {
|
|
defaultContent.Id = contentList[0].ID
|
|
defaultContent.Name = contentList[0].Name
|
|
defaultContent.UrlSet = contentList[0].UrlSet
|
|
defaultContent.Cover = contentList[0].Cover
|
|
}
|
|
}
|
|
// 子集总数
|
|
count, err := mediacontentmod.QueryAllCount(bson.M{"mediaId": mediaId, "isActive": true, "isDelete": false})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// 获取子集免费章节总数
|
|
freeCount, err := mediacontentmod.QueryAllCount(bson.M{"mediaId": mediaId, "listenPermission": 2, "isActive": true, "isDelete": false})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
contentsPrice, err := mediacontentmod.QueryContentsPrice(mediaId)
|
|
if err != nil {
|
|
return
|
|
}
|
|
updateData := bson.M{
|
|
"currentEpisode": count,
|
|
"contentsPrice": contentsPrice,
|
|
"freeEpisode": freeCount,
|
|
"updateTime": time.Now(),
|
|
"defaultContent": defaultContent, // 默认展示的子集内容,冗余一个字段,减少一次查询
|
|
}
|
|
now := time.Now()
|
|
if contentAdded {
|
|
updateData["contentUpdateTime"] = now
|
|
}
|
|
if published {
|
|
updateData["latestPublishedAt"] = now
|
|
}
|
|
//修改当前父级的记录
|
|
_, err = mediamod.UpdateByID(nil, mediaId, updateData)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ViewMediaFromFs 查看视频转码状态
|
|
func ViewMediaFromFs(id string) (code stderr.Code, data interface{}) {
|
|
resp, err := updownloadser.PullFileInfo(id)
|
|
if err != nil {
|
|
return stderr.ErrConnectToFs, nil
|
|
}
|
|
status := fileStatusToEpisodeStatus(resp.Data.Status)
|
|
ratio := resp.Data.Ratio
|
|
if ratio == 0 && resp.Data.Height > 0 {
|
|
ratio = float64(resp.Data.Width) / float64(resp.Data.Height)
|
|
}
|
|
set := map[string]interface{}{
|
|
"status": status,
|
|
"playTime": resp.Data.PlayTime,
|
|
"weight": resp.Data.Width,
|
|
"height": resp.Data.Height,
|
|
"mediaSize": int64(resp.Data.Size),
|
|
"ratio": ratio,
|
|
"updateTime": time.Now(),
|
|
}
|
|
if resp.Data.CheckSum != "" {
|
|
set["md5"] = resp.Data.CheckSum
|
|
}
|
|
objId, _ := primitive.ObjectIDFromHex(id)
|
|
if _, err = mediacontentmod.UpdateByID(nil, objId, set); err != nil {
|
|
return stderr.ErrDbUpdateError, nil
|
|
}
|
|
back := make(map[string]interface{}, 10)
|
|
back["playTime"] = resp.Data.PlayTime
|
|
back["size"] = resp.Data.Size
|
|
back["width"] = resp.Data.Width
|
|
back["height"] = resp.Data.Height
|
|
back["status"] = resp.Data.Status
|
|
back["ratio"] = ratio
|
|
fileName := resp.Data.Filename
|
|
fieldNameFs := resp.Data.FieldNameFs
|
|
back["fileName"] = filepath.Join(filepath.Dir(fieldNameFs), fileName)
|
|
switch resp.Data.Status {
|
|
case vidmod.UploadLoadingToFs:
|
|
back["statusDesc"] = "正在上传中"
|
|
case vidmod.UploadCompleted:
|
|
back["statusDesc"] = "上传完成"
|
|
case vidmod.FileUploadError:
|
|
back["statusDesc"] = "上传错误"
|
|
case vidmod.Merging:
|
|
back["statusDesc"] = "正在合并中"
|
|
case vidmod.MergeCompleted:
|
|
back["statusDesc"] = "合并完成"
|
|
case vidmod.MergeError:
|
|
back["statusDesc"] = "合并失败"
|
|
case vidmod.Converting:
|
|
back["statusDesc"] = "正在转码中"
|
|
case vidmod.ConvertError:
|
|
back["statusDesc"] = "转码失败"
|
|
case vidmod.ConvertCompleted, vidmod.Completed:
|
|
back["statusDesc"] = "完成状态"
|
|
}
|
|
return stderr.Success, back
|
|
}
|