Initial commit
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
package videosync
|
||||
|
||||
import (
|
||||
"time"
|
||||
"videoweb/pub/models/videoinformationmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type MediaTaskSyncReq struct {
|
||||
AppId int64 `bson:"appId" json:"appId"` // APPID
|
||||
LastUpdateTime time.Time `bson:"lastUpdateTime" json:"lastUpdateTime"` // 最后更新时间
|
||||
PageSize int64 `bson:"pageSize" json:"pageSize"`
|
||||
}
|
||||
|
||||
type MediaTaskSyncResp struct {
|
||||
Code int `json:"code,omitempty"` //
|
||||
Data []MediaTaskInfo `json:"data,omitempty"` //
|
||||
Msg string `json:"msg,omitempty"` //
|
||||
}
|
||||
|
||||
// @table:media
|
||||
type MediaTaskInfo struct {
|
||||
Id primitive.ObjectID `bson:"_id,omitempty" json:"id"`
|
||||
MediaSourceId primitive.ObjectID `bson:"mediaSourceId,omitempty" json:"mediaSourceId"` // 原始Id
|
||||
VideoTime int32 `bson:"videoTime,omitempty" json:"videoTime"` // 影片长度
|
||||
Size int64 `bson:"size,omitempty" json:"size"` // 文件size,单位byte
|
||||
AppId int64 `bson:"appId,omitempty" json:"appId"` // 应用列表
|
||||
CoverImg string `bson:"coverImg,omitempty" json:"coverImg"` // 封面
|
||||
CoverImgNew string `bson:"coverImgNew,omitempty" json:"coverImgNew"` // 动态封面
|
||||
UnencryptedImage string `bson:"unencryptedImage,omitempty" json:"unencryptedImage"` // 不加密封面
|
||||
Title string `bson:"title,omitempty" json:"title"` // 标题
|
||||
Publish string `bson:"publish,omitempty" json:"publish"` // 视频发布者
|
||||
Remark string `bson:"remark,omitempty" json:"remark"` // 备注
|
||||
Keyworld []string `bson:"keyworld,omitempty" json:"keyworld"` // 关键字
|
||||
|
||||
Nodes []videoinformationmod.Node `bson:"nodes" json:"nodes"` // 节点
|
||||
FirstTag string `bson:"firstTag,omitempty" json:"firstTag"` // 一级标签
|
||||
SecondTags []string `bson:"secondTags,omitempty" json:"secondTags"` // 二级标签标签
|
||||
Status string `bson:"status,omitempty" json:"status"` // 当前状态
|
||||
Desc string `bson:"desc,omitempty" json:"desc"` // 详细描述
|
||||
TaskStartTime time.Time `bson:"taskStartTime,omitempty" json:"taskStartTime"` // 任务开始时间
|
||||
SourceNameFs string `bson:"sourceNameFs,omitempty" json:"sourceNameFs"` // 远程源文件
|
||||
IsDelLocalDir bool `bson:"isDelLocalDir,omitempty" json:"isDelLocalDir"` // 是否删除本机文件
|
||||
WatermarkImage string `json:"watermarkImage" bson:"watermarkImage"` // 图片水印文件
|
||||
WatermarkMedia string `json:"watermarkMedia" bson:"watermarkMedia"` // 视频水印文件
|
||||
DownloadRetry int `bson:"downloadRetry,omitempty" json:"downloadRetry"` // 下载重试次数
|
||||
WatermarkRetry int `bson:"watermarkRetry" json:"watermarkRetry"` // 水印重试次数
|
||||
CompressRetry int `bson:"compressRetry" json:"compressRetry"` // 压缩重试次数
|
||||
SegmentRetry int `bson:"segmentRetry" json:"segmentRetry"` // 切片重试次数
|
||||
LinodeRetry int `bson:"linodeRetry" json:"linodeRetry"` // 上传重试次数
|
||||
|
||||
// 切片相关信息
|
||||
Filename string `bson:"filename,omitempty" json:"filename"` // 文件名称
|
||||
FieldNameFs string `bson:"fieldNameFs,omitempty" json:"fieldNameFs"` // 文件名称
|
||||
TempFieldNameFs string `bson:"tempFieldNameFs,omitempty" json:"tempFieldNameFs"` // 临时存储
|
||||
Ext string `bson:"ext,omitempty" json:"ext"` // 视频后缀
|
||||
UnencryptedPreMp4 string `bson:"unencryptedPreMp4,omitempty" json:"unencryptedPreMp4"` // 不加密的预览MP4
|
||||
TaskID string `bson:"taskId,omitempty" json:"taskId"` // 项目组传来的唯一标示
|
||||
Width int64 `bson:"width,omitempty" json:"width"` // 分辨率宽
|
||||
Height int64 `bson:"height,omitempty" json:"height"` // 分辨率高
|
||||
Ratio float64 `bson:"ratio,omitempty" json:"ratio"` // 宽高比
|
||||
FrameRate string `bson:"frameRate,omitempty" json:"frameRate"` // 视频贞数
|
||||
SyBitRate string `bson:"syBitRate,omitempty" json:"syBitRate"` // 音频bit率
|
||||
VidBitRate string `bson:"vidBitRate,omitempty" json:"vidBitRate"` // 视频bit率
|
||||
CreatedAt time.Time `bson:"createdAt,omitempty" json:"createdAt"` // 创建时间
|
||||
UpdatedAt time.Time `bson:"updatedAt,omitempty" json:"updatedAt"` // 更新时间
|
||||
ProxyUpload *int32 `bson:"proxyUpload,omitempty" json:"proxyUpload"` // 代理上传
|
||||
Retry int `bson:"retry" json:"retry"` // 重试次数 默认为零 最大重试3次
|
||||
NotifySignal bool `bson:"notifySignal" json:"notifySignal"` //中止通知的信号 false 可以发送通知,true 中止发送通知
|
||||
|
||||
InfoTsName string `bson:"infoTsName" json:"infoTsName"` // 从该ts中获取视频分辨率等信息
|
||||
SyncFlag int64 `bson:"syncFlag" json:"syncFlag"` // 同步状态
|
||||
SrcKey string `bson:"srcKey,omitempty" json:"srcKey"` // 原始Key
|
||||
UploadUserName string `bson:"uploadUserName" json:"uploadUserName,omitempty"` // 上传用户名
|
||||
OptionAccount string `bson:"optionAccount,omitempty" json:"optionAccount"` // 最后操作人
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package videosync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
nethttp "net/http"
|
||||
"sync"
|
||||
"time"
|
||||
"videoweb/pub/common/log"
|
||||
"videoweb/pub/common/srvdef"
|
||||
"videoweb/pub/models/syncfilemod"
|
||||
"videoweb/pub/pubdef"
|
||||
"videoweb/pub/storage/proxy"
|
||||
"videoweb/web/service/srv_videoinfo"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
/*
|
||||
1.小说采集后上传媒资库
|
||||
2.业务后台记录最新更新章节ID(数据ID自增)
|
||||
3.查询对应小说ID是否存在,如果不存在需要先同步媒资库小说信息,再更新小说
|
||||
注意:业务后台和媒资库 存储的小说ID和章节ID需一致, 资源以媒资库为准, 不单独更新业务后台小说
|
||||
*/
|
||||
|
||||
var (
|
||||
isRunning = false
|
||||
lock sync.Mutex
|
||||
)
|
||||
|
||||
func getIsRunning() bool {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
return isRunning
|
||||
}
|
||||
func setRunning(b bool) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
isRunning = b
|
||||
}
|
||||
func VideoSync() {
|
||||
isRunning = getIsRunning()
|
||||
if isRunning {
|
||||
return
|
||||
}
|
||||
setRunning(true)
|
||||
ctx := context.Background()
|
||||
log.Info("VideoSync begin")
|
||||
pageSize := int64(200)
|
||||
recordFilter := bson.M{
|
||||
syncfilemod.FN.SyncRecord.Job: syncfilemod.VideoJob,
|
||||
}
|
||||
recordInfo, serr := syncfilemod.FindOneSyncRecord(ctx, recordFilter)
|
||||
if serr != nil {
|
||||
log.Error("VideoSync", log.Any("recordFilter", recordFilter), log.E(serr))
|
||||
setRunning(false)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
setRunning(false)
|
||||
// 更新同步记录
|
||||
set := bson.M{
|
||||
"$set": bson.M{
|
||||
syncfilemod.FN.SyncRecord.RecordTime: recordInfo.RecordTime,
|
||||
syncfilemod.FN.SyncRecord.SuccessAt: time.Now(),
|
||||
},
|
||||
}
|
||||
_ = syncfilemod.UpdateOneSyncRecord(ctx, recordFilter, set)
|
||||
}()
|
||||
// 循环同步
|
||||
for {
|
||||
// 查询接口
|
||||
req := proxy.MediaTaskSyncReq{
|
||||
AppId: pubdef.AppId,
|
||||
LastUpdateTime: recordInfo.RecordTime,
|
||||
PageSize: pageSize,
|
||||
}
|
||||
result, err := proxy.SyncMedia(&req, "")
|
||||
if err != nil || result.Code != nethttp.StatusOK {
|
||||
log.Error("VideoSync SyncMedia", log.Any("req", req), log.E(err))
|
||||
return
|
||||
}
|
||||
|
||||
// 处理
|
||||
for _, media := range result.Data {
|
||||
// 先推进水位,确保单条失败后下次不会卡在同一位置
|
||||
if media.UpdatedAt.After(recordInfo.RecordTime) {
|
||||
recordInfo.RecordTime = media.UpdatedAt
|
||||
}
|
||||
se := srv_videoinfo.SyncOneVideo(ctx, media)
|
||||
if se != nil {
|
||||
log.Error("VideoSync SyncOneVideo", log.Any("media", media), log.E(se))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if len(result.Data) < int(pageSize) {
|
||||
log.Info("VideoSync success", log.Any("RecordTime", recordInfo.RecordTime))
|
||||
break
|
||||
}
|
||||
}
|
||||
log.Info("VideoSync success", log.Any("RecordTime", recordInfo.RecordTime))
|
||||
}
|
||||
|
||||
// SyncUnencryptedFields 全量补齐历史资源的 UnencryptedImage / UnencryptedPreMp4 字段。
|
||||
// 从头分页拉取媒资库,仅在源端有值时写回,本地不存在或字段已有值不覆盖。
|
||||
// 独立于增量同步的 SyncRecord,用于一次性修复;可安全重复执行。
|
||||
func SyncUnencryptedFields() {
|
||||
ctx := context.Background()
|
||||
log.Info("SyncUnencryptedFields begin")
|
||||
pageSize := int64(200)
|
||||
lastUpdateTime := time.Time{}
|
||||
for {
|
||||
req := proxy.MediaTaskSyncReq{
|
||||
AppId: pubdef.AppId,
|
||||
LastUpdateTime: lastUpdateTime,
|
||||
PageSize: pageSize,
|
||||
}
|
||||
result, err := proxy.SyncMedia(&req, "")
|
||||
if err != nil || result.Code != nethttp.StatusOK {
|
||||
log.Error("SyncUnencryptedFields SyncMedia", log.Any("req", req), log.E(err))
|
||||
return
|
||||
}
|
||||
for _, media := range result.Data {
|
||||
// 先推进时间戳,保证 continue 也不阻塞分页
|
||||
if media.UpdatedAt.After(lastUpdateTime) {
|
||||
lastUpdateTime = media.UpdatedAt
|
||||
}
|
||||
se := srv_videoinfo.SyncUnencryptedOne(ctx, media)
|
||||
if se != nil && !errors.Is(se.Code, srvdef.ErrNoData) {
|
||||
log.Warn("SyncUnencryptedFields SyncUnencryptedOne", log.Any("mediaId", media.Id.Hex()), log.E(se))
|
||||
}
|
||||
}
|
||||
if len(result.Data) < int(pageSize) {
|
||||
break
|
||||
}
|
||||
}
|
||||
log.Info("SyncUnencryptedFields success", log.Any("lastUpdateTime", lastUpdateTime))
|
||||
}
|
||||
Reference in New Issue
Block a user