Initial commit: web_hghl_server
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,294 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user