package srv_videoinfo import ( "context" "math/rand" "strings" "time" "videoweb/pub/common/log" "videoweb/pub/common/playletcache" "videoweb/pub/common/slugx" "videoweb/pub/common/srvdef" "videoweb/pub/common/utils" "videoweb/pub/models/playletchaptermod" "videoweb/pub/models/videocreatermod" "videoweb/pub/models/videoinformationmod" "videoweb/pub/mongos" "videoweb/pub/storage/proxy" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) func videoSyncKey(media proxy.MediaResp) string { if !media.MediaSourceId.IsZero() { return media.MediaSourceId.Hex() } return media.Id.Hex() } func buildVideoSyncFilter(media proxy.MediaResp) bson.M { keys := []string{media.Id.Hex()} if !media.MediaSourceId.IsZero() { msid := media.MediaSourceId.Hex() if msid != keys[0] { keys = append(keys, msid) } } or := make([]bson.M, 0, len(keys)*2) for _, key := range keys { or = append(or, bson.M{videoinformationmod.FN.VideoInformation.Video.FileId: key}, bson.M{videoinformationmod.FN.VideoInformation.SyncId: key}, ) } return bson.M{"$or": or} } 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 } // 同步时不再自动写 video_taginfo,标签由后台统一标签绑定维护 syncKey := videoSyncKey(media) filter := buildVideoSyncFilter(media) 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 serr } 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, MediaTypes: []int64{1}, Status: true, Sort: 0, UpdatedAt: time.Now(), CreatedAt: time.Now(), } createrExists := func(s string) bool { doc, _ := videocreatermod.FindOneVideoCreaterInfo(ctx, slugx.Filter( videocreatermod.FN.VideoCreaterInfo.Slug, videocreatermod.FN.VideoCreaterInfo.Id, s, 0)) return doc != nil } if serr = slugx.Ensure(&creater.Slug, utils.SlugSourceFromKeywordsOrTitle(nil, creater.Name), createrExists); serr != nil { return serr } serr = videocreatermod.InsertOneVideoCreaterInfo(ctx, creater) if serr != nil { log.Error("VideoSync.InsertOneVideoCreaterInfo", log.Any("media", media), log.E(serr)) return serr } } else { isFind := false for _, v := range creater.MediaTypes { if v == 1 { isFind = true } } if !isFind { mediaTypes := append(creater.MediaTypes, 1) _ = videocreatermod.UpdateOneVideoCreaterInfo(ctx, bson.M{videocreatermod.FN.VideoCreaterInfo.Id: creater.Id}, bson.M{ "$set": bson.M{videocreatermod.FN.VideoCreaterInfo.MediaTypes: mediaTypes}, }) } } videoInfo.CreatorId = creater.Id videoInfo.CreatorName = creater.Name } videoInfo.IndexNow = 0 applyDefaultFakeFlow(&videoInfo.Fake, rand.New(rand.NewSource(time.Now().UnixNano()))) exists := func(s string) bool { doc, _ := videoinformationmod.FindOneVideoInformation(ctx, slugx.Filter( videoinformationmod.FN.VideoInformation.Slug, videoinformationmod.FN.VideoInformation.Id, s, 0)) return doc != nil } if serr = slugx.Ensure(&videoInfo.Slug, utils.SlugSourceFromKeywordsOrTitle(videoInfo.Keyworld, videoInfo.Title), exists); serr != nil { return serr } 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 { mediaNodeMap := make(map[string]videoinformationmod.Node, len(media.Nodes)) for _, node := range media.Nodes { if node.NodeId != "" { mediaNodeMap[node.NodeId] = node } } // 更新节点里面图片水印 以本地视频列表的节点为主 for _, node := range videoInfo.Nodes { if mediaNode, ok := mediaNodeMap[node.NodeId]; ok { node.Images = mediaNode.Images } 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.IssuedBrand: media.IssuedBrand, videoinformationmod.FN.VideoInformation.IssuedAt: media.IssuedAt, } for k, v := range defaultFakeFlowSetDoc(rand.New(rand.NewSource(time.Now().UnixNano()))) { setDoc[k] = v } setDoc[videoinformationmod.FN.VideoInformation.SyncId] = syncKey if len(media.FieldNameFs) > 0 { video := videoinformationmod.VideoInfo{ TaskId: media.TaskID, FileId: syncKey, Url: media.FieldNameFs, Cover: media.CoverImg, Height: media.Height, Width: media.Width, VideoTime: media.VideoTime, } 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 } if preMp4 := strings.TrimSpace(media.PreMp4FileNameFs); preMp4 != "" { setDoc[videoinformationmod.FN.VideoInformation.Video.PreMp4FileNameFs] = preMp4 } if unencryptedPreMp4 := strings.TrimSpace(media.UnencryptedPreMp4); unencryptedPreMp4 != "" { setDoc[videoinformationmod.FN.VideoInformation.Video.UnencryptedPreMp4] = unencryptedPreMp4 } if videoInfo.Slug == "" { exists := func(s string) bool { doc, _ := videoinformationmod.FindOneVideoInformation(ctx, slugx.Filter( videoinformationmod.FN.VideoInformation.Slug, videoinformationmod.FN.VideoInformation.Id, s, videoInfo.Id)) return doc != nil } slug := "" if serr = slugx.Ensure(&slug, utils.SlugSourceFromKeywordsOrTitle(media.Keyworld, videoInfo.Title), exists); serr != nil { return serr } setDoc[videoinformationmod.FN.VideoInformation.Slug] = slug } serr = videoinformationmod.UpdateOneVideoInformation(ctx, bson.M{videoinformationmod.FN.VideoInformation.Id: videoInfo.Id}, bson.M{"$set": setDoc}) if serr != nil { return serr } } playletcache.ClearHomeCache() _ = syncNodeVideo(ctx, media) return nil } // syncNodeVideo 同步节点里的视频 func syncNodeVideo(ctx context.Context, media proxy.MediaResp) *srvdef.SrvErr { mediaID := videoSyncKey(media) nodeIDs := []string{mediaID} if !media.MediaSourceId.IsZero() { msid := media.MediaSourceId.Hex() if msid != mediaID { nodeIDs = append(nodeIDs, msid) } } if media.Id.Hex() != mediaID && media.Id.Hex() != "" { nodeIDs = append(nodeIDs, media.Id.Hex()) } filter := bson.M{"nodes.nodeId": bson.M{"$in": nodeIDs}} 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 { writeModels := make([]mongo.WriteModel, 0, len(videoInfo)) now := time.Now() // 更新视频路径 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 == mediaID || containsString(nodeIDs, v.FileId) { node.Videos[j].Url = media.FieldNameFs isFind = true break } } } if isFind { video.Nodes[i] = node break } } if isFind { writeModels = append(writeModels, mongo.NewUpdateOneModel(). SetFilter(bson.M{videoinformationmod.FN.VideoInformation.Id: video.Id}). SetUpdate(bson.M{"$set": bson.M{ videoinformationmod.FN.VideoInformation.UpdatedAt: now, videoinformationmod.FN.VideoInformation.Nodes: video.Nodes, }})) } } } if len(writeModels) > 0 { _, serr = videoinformationmod.BulkWriteVideoInformation(ctx, writeModels, options.BulkWrite().SetOrdered(false)) if serr != nil { log.Warn("syncNodeVideo BulkWriteVideoInformation failed", log.Any("filter", filter), log.E(serr)) return serr } } } return nil } func containsString(list []string, target string) bool { for _, item := range list { if item == target { return true } } return false }