package srv_videoinfo import ( "context" "hkdj-web-server/pub/common/log" "hkdj-web-server/pub/common/srvdef" "hkdj-web-server/pub/models/playletchaptermod" "hkdj-web-server/pub/models/videocreatermod" "hkdj-web-server/pub/models/videoinformationmod" "hkdj-web-server/pub/mongos" "hkdj-web-server/pub/storage/proxy" "hkdj-web-server/web/service/srv_videoslug" "strings" "time" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" ) func IsPlayletMedia(ctx context.Context, media proxy.MediaResp) bool { return playletchaptermod.IsLinkedMedia(ctx, media.IsPlaylet, media.PlayletKeys()...) } func SyncOneVideo(ctx context.Context, media proxy.MediaResp) *srvdef.SrvErr { if IsPlayletMedia(ctx, media) { log.Info("VideoSync skip playlet media", log.Any("mediaId", media.Id.Hex()), log.Any("mediaSourceId", media.MediaSourceId.Hex())) return nil } filter := bson.M{ "$or": []bson.M{ {videoinformationmod.FN.VideoInformation.Video.FileId: media.Id.Hex()}, {videoinformationmod.FN.VideoInformation.SyncId: media.Id.Hex()}, }, } videoInfo, serr := videoinformationmod.FindOneVideoInformation(ctx, filter) if serr != nil { // 新加入的视频 if serr.Code == srvdef.ErrNoData { videoInfo = media.ToVideoInfo() if videoInfo == nil { log.Error("VideoSync FindOneVideoInformation", log.Any("MediaId", media.Id)) return srvdef.New(srvdef.ErrNoData, "videoInfo is nil") } videoInfo.IndexNow = 0 if len(media.Publish) > 0 { creater, serr := videocreatermod.FindOneVideoCreaterInfo(ctx, bson.M{ videocreatermod.FN.VideoCreaterInfo.Name: media.Publish, }) if serr != nil && serr.Code != srvdef.ErrNoData { log.Error("VideoSync.FindOneVideoCreaterInfo", log.Any("media", media), log.E(serr)) return serr } if creater == nil { createrId := mongos.Lid.VideoCreator.Get() creater = &videocreatermod.VideoCreaterInfo{ Id: createrId, Name: media.Publish, Status: true, Sort: 0, UpdatedAt: time.Now(), CreatedAt: time.Now(), } if slugErr := srv_videoslug.EnsureCreater(ctx, creater, 0); slugErr != nil { return slugErr } serr = videocreatermod.InsertOneVideoCreaterInfo(ctx, creater) if serr != nil { log.Error("VideoSync.InsertOneVideoCreaterInfo", log.Any("media", media), log.E(serr)) return serr } } videoInfo.CreatorId = creater.Id videoInfo.CreatorName = creater.Name } if slugErr := srv_videoslug.EnsureVideo(ctx, videoInfo, 0); slugErr != nil { return slugErr } serr = videoinformationmod.InsertOneVideoInformation(ctx, videoInfo) if serr != nil { log.Error("VideoSync InsertOneVideoInformation", log.Any("videoInfo", videoInfo), log.E(serr)) return serr } } else { log.Error("VideoSync FindOneVideoInformation", log.Any("filter", filter), log.E(serr)) return serr } } else { // 更新资源 nodes := make([]videoinformationmod.Node, 0) if videoInfo != nil { // 更新节点里面图片水印 以本地视频列表的节点为主 for _, node := range videoInfo.Nodes { isFind := false if len(node.NodeId) > 0 { for _, v := range media.Nodes { if node.NodeId == v.NodeId { isFind = true node.Images = v.Images nodes = append(nodes, node) break } } } if !isFind { nodes = append(nodes, node) } } } else { nodes = media.Nodes } setDoc := bson.M{ videoinformationmod.FN.VideoInformation.Nodes: nodes, videoinformationmod.FN.VideoInformation.Keyworld: media.Keyworld, videoinformationmod.FN.VideoInformation.FirstTag: media.FirstTag, videoinformationmod.FN.VideoInformation.SecondTags: media.SecondTags, videoinformationmod.FN.VideoInformation.CoverImg: media.CoverImg, videoinformationmod.FN.VideoInformation.UnencryptedImage: media.UnencryptedImage, } if len(media.FieldNameFs) > 0 { video := videoinformationmod.VideoInfo{ TaskId: media.TaskID, FileId: media.Id.Hex(), Url: media.FieldNameFs, Cover: media.CoverImg, Height: media.Height, Width: media.Width, VideoTime: media.VideoTime, UnencryptedPreMp4: media.UnencryptedPreMp4, PreMp4FileNameFs: media.PreMp4FileNameFs, } if !strings.HasSuffix(video.Url, ".m3u8") { video.Url += ".m3u8" } setDoc[videoinformationmod.FN.VideoInformation.Video.TaskId] = video.TaskId setDoc[videoinformationmod.FN.VideoInformation.Video.FileId] = video.FileId setDoc[videoinformationmod.FN.VideoInformation.Video.Url] = video.Url setDoc[videoinformationmod.FN.VideoInformation.Video.Cover] = video.Cover setDoc[videoinformationmod.FN.VideoInformation.Video.Height] = video.Height setDoc[videoinformationmod.FN.VideoInformation.Video.Width] = video.Width setDoc[videoinformationmod.FN.VideoInformation.Video.VideoTime] = video.VideoTime setDoc[videoinformationmod.FN.VideoInformation.Video.UnencryptedPreMp4] = video.UnencryptedPreMp4 } if strings.TrimSpace(videoInfo.Slug) == "" { if slugErr := srv_videoslug.EnsureVideo(ctx, videoInfo, videoInfo.Id); slugErr == nil && strings.TrimSpace(videoInfo.Slug) != "" { setDoc[videoinformationmod.FN.VideoInformation.Slug] = videoInfo.Slug } } serr = videoinformationmod.UpdateOneVideoInformation(ctx, bson.M{videoinformationmod.FN.VideoInformation.Id: videoInfo.Id}, bson.M{"$set": setDoc}) if serr != nil { return serr } } _ = syncNodeVideo(ctx, media) return nil } // SyncUnencryptedOne 仅补齐单条资源的 UnencryptedImage / UnencryptedPreMp4 字段。 // 源数据对应字段为空时跳过该字段(避免覆盖本地已有值);本地无对应资源时直接返回。 // 幂等:可重复执行,用于历史数据补齐。 func SyncUnencryptedOne(ctx context.Context, media proxy.MediaResp) *srvdef.SrvErr { if media.UnencryptedImage == "" && media.UnencryptedPreMp4 == "" { return nil } filter := bson.M{videoinformationmod.FN.VideoInformation.Video.FileId: media.Id.Hex()} videoInfo, serr := videoinformationmod.FindOneVideoInformation(ctx, filter) if serr != nil { return serr } setDoc := bson.M{} if media.UnencryptedImage != "" { setDoc[videoinformationmod.FN.VideoInformation.UnencryptedImage] = media.UnencryptedImage } // 仅当源端有主视频文件时,才补 Video.UnencryptedPreMp4(避免向空的 Video 子结构写字段) if media.UnencryptedPreMp4 != "" && len(media.FieldNameFs) > 0 { setDoc[videoinformationmod.FN.VideoInformation.Video.UnencryptedPreMp4] = media.UnencryptedPreMp4 } if len(setDoc) == 0 { return nil } return videoinformationmod.UpdateOneVideoInformation(ctx, bson.M{videoinformationmod.FN.VideoInformation.Id: videoInfo.Id}, bson.M{"$set": setDoc}) } // syncNodeVideo 同步节点里的视频 func syncNodeVideo(ctx context.Context, media proxy.MediaResp) *srvdef.SrvErr { filter := bson.M{"nodes.nodeId": media.Id.Hex()} opts := &options.FindOptions{} opts.SetLimit(100) videoInfo, serr := videoinformationmod.FindManyVideoInformations(ctx, filter, opts) if serr != nil { log.Warn("syncNodeVideo", log.Any("filter", filter), log.E(serr)) return serr } if !strings.HasSuffix(media.FieldNameFs, ".m3u8") { media.FieldNameFs += ".m3u8" } if len(videoInfo) > 0 { // 更新视频路径 for _, video := range videoInfo { if video.Nodes != nil && len(video.Nodes) > 0 { isFind := false for i, node := range video.Nodes { if node.Videos != nil && len(node.Videos) > 0 { for j, v := range node.Videos { if v.FileId == media.Id.Hex() { node.Videos[j].Url = media.FieldNameFs isFind = true break } } } if isFind { video.Nodes[i] = node break } } if isFind { serr = videoinformationmod.UpdateOneVideoInformation(ctx, bson.M{videoinformationmod.FN.VideoInformation.Id: video.Id}, bson.M{"$set": bson.M{ videoinformationmod.FN.VideoInformation.UpdatedAt: time.Now(), videoinformationmod.FN.VideoInformation.Nodes: video.Nodes, }}) if serr != nil { log.Warn("syncNodeVideo UpdateOneVideoInformation failed", log.Any("filter", filter), log.E(serr)) continue } } } } } return nil }