Files
web_hghl_server/web/service/srv_videoinfo/sync.go
T
2026-10-26 16:28:14 +08:00

295 lines
10 KiB
Go

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
}