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

679 lines
22 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package srv_mediaresource
import (
"context"
"math/rand"
"strings"
"time"
"videoweb/pub/common/log"
"videoweb/pub/common/slugx"
"videoweb/pub/common/srvdef"
"videoweb/pub/common/utils"
"videoweb/pub/models/mwacc"
"videoweb/pub/models/playletchaptermod"
"videoweb/pub/models/playletmod"
"videoweb/web/api/proto"
"videoweb/web/middleware/auth"
"videoweb/web/webg"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
const (
defaultResourceSyncType = 1
defaultResourcePageNum = 1
defaultResourcePageSize = 20
maxResourcePageSize = 100
maxResourceImportIds = 100
playletResourceImportMsg = "同步任务已提交,后台正在处理"
playletHeatMinValue = 1000
playletHeatMaxValue = 5000
playletResourceMediaTypeAnime = 1 // 动漫
playletResourceMediaTypeComic = 4 // 漫画短剧
playletResourceMediaTypeShort = 5 // 短剧
playletResourceMediaTypeNormal = 6 // 普通短剧
)
// ListPlayletResource 分页查询短剧媒资列表
func ListPlayletResource(ctx context.Context, req *proto.PlayletResourceListReq) (*proto.PlayletResourceListResp, *srvdef.SrvErr) {
if req == nil {
return nil, srvdef.New(srvdef.ErrParamError, "", "")
}
syncType := defaultResourceSyncType
if req.SyncType != nil {
syncType = *req.SyncType
}
pageNumber := req.PageNumber
if pageNumber <= 0 {
pageNumber = defaultResourcePageNum
}
pageSize := req.PageSize
if pageSize <= 0 {
pageSize = defaultResourcePageSize
}
if pageSize > maxResourcePageSize {
pageSize = maxResourcePageSize
}
mediaType, err1 := resolvePlayletResourceMediaType(req.MediaType)
if err1 != nil {
return nil, err1
}
ms := newPlayletMediaCenterFromConfig()
data, err := ms.mediaList(&playletMediaListReq{
MediaType: mediaType,
Title: req.Title,
Tag: req.Tag,
SyncType: syncType,
Page: uint(pageNumber),
Size: uint(pageSize),
})
if err != nil {
return nil, srvdef.New(srvdef.Failure, err.Error(), "")
}
if err := fillPlayletResourceAddStatus(ctx, data.Data); err != nil {
return nil, err
}
return &proto.PlayletResourceListResp{
List: data.Data,
Total: data.Pagination.Count,
}, nil
}
// ListPlayletResourceChapters 查询短剧媒资剧集信息
func ListPlayletResourceChapters(ctx context.Context, req *proto.PlayletResourceChapterListReq) (*proto.PlayletResourceChapterListResp, *srvdef.SrvErr) {
if req == nil || req.Id == 0 {
return nil, srvdef.New(srvdef.ErrParamError, "媒资ID不能为空", "")
}
ms := newPlayletMediaCenterFromConfig()
result, err := ms.mediaSync(&playletMediaSyncReq{
Id: req.Id,
SyncTime: 0,
})
if err != nil {
return nil, srvdef.New(srvdef.Failure, err.Error(), "")
}
if result.IsDelete || result.Media == nil {
return nil, srvdef.New(srvdef.ErrNoData, "媒资不存在或已删除", "")
}
list := make([]*proto.PlayletResourceChapterItem, 0, len(result.MediaContentList))
for _, item := range result.MediaContentList {
videoUrl := strings.TrimSpace(item.M3u8Url)
if videoUrl == "" {
videoUrl = strings.TrimSpace(item.Urls)
}
list = append(list, &proto.PlayletResourceChapterItem{
Id: item.ID,
EpisodeNumber: item.EpisodeNumber,
Title: strings.TrimSpace(item.Title),
Cover: item.Cover,
VideoUrl: videoUrl,
Height: item.Height,
Width: item.Width,
MediaSize: item.MediaSize,
PlayTime: item.PlayTime,
SaleCoin: item.SaleCoin,
HashId: item.HashId,
CreateTime: item.CreateTime,
UpdateTime: item.UpdateTime,
})
}
return &proto.PlayletResourceChapterListResp{
MediaId: req.Id,
Title: result.Media.Title,
List: list,
Total: len(list),
}, nil
}
// resolvePlayletResourceMediaType 校验后台显式传入的媒资类型
func resolvePlayletResourceMediaType(mediaType *int) (int, *srvdef.SrvErr) {
if mediaType == nil {
return 0, srvdef.New(srvdef.ErrParamError, "媒体类型不能为空", "媒体类型不能为空")
}
if !validPlayletResourceMediaType(*mediaType) {
return 0, srvdef.New(srvdef.ErrParamError, "媒体类型无效,仅支持1-动漫、2-漫画、4-漫画短剧、5-短剧、6-普通短剧", "媒体类型无效,仅支持1-动漫、2-漫画、4-漫画短剧、5-短剧、6-普通短剧")
}
return *mediaType, nil
}
// validPlayletResourceMediaType 媒资列表当前允许的媒体类型
func validPlayletResourceMediaType(mediaType int) bool {
switch mediaType {
case playletResourceMediaTypeAnime, playletResourceMediaTypeComic, playletResourceMediaTypeShort, playletResourceMediaTypeNormal:
return true
default:
return false
}
}
// validPlayletImportMediaType 允许入库的媒资类型
func validPlayletImportMediaType(mediaType int) bool {
switch mediaType {
case playletResourceMediaTypeAnime, playletResourceMediaTypeComic, playletResourceMediaTypeShort, playletResourceMediaTypeNormal:
return true
default:
return false
}
}
// contentTypeFromMediaType 媒资中心 media_type 映射为本项目 contentType
func contentTypeFromMediaType(mediaType int) playletmod.ContentType {
switch mediaType {
case playletResourceMediaTypeAnime:
return playletmod.ContentTypeAnime
case playletResourceMediaTypeComic:
return playletmod.ContentTypeComic
case playletResourceMediaTypeNormal:
return playletmod.ContentTypeAlbum
default:
return playletmod.ContentTypeTheater
}
}
// fillPlayletResourceAddStatus 回填媒资列表是否已加入后台短剧库
func fillPlayletResourceAddStatus(ctx context.Context, list []*proto.PlayletResourceListItem) *srvdef.SrvErr {
mediaIds := make([]uint, 0, len(list))
seen := make(map[uint]struct{}, len(list))
for _, item := range list {
if item == nil || item.ID == 0 {
continue
}
if _, ok := seen[item.ID]; ok {
continue
}
seen[item.ID] = struct{}{}
mediaIds = append(mediaIds, item.ID)
}
if len(mediaIds) == 0 {
return nil
}
playletList, err := playletmod.FindManyPlaylet(ctx, bson.M{
playletmod.FN.Playlet.MediaCenterId: bson.M{"$in": mediaIds},
})
if err != nil {
log.Error("查询短剧媒资加入状态失败", log.Any("mediaIds", mediaIds), log.Any("err", err))
return err
}
addedMap := make(map[uint]bool, len(playletList))
for _, item := range playletList {
if item == nil || item.MediaCenterId == 0 {
continue
}
addedMap[item.MediaCenterId] = true
}
for _, item := range list {
if item != nil {
item.IsAdd = addedMap[item.ID]
}
}
return nil
}
// ImportPlayletResource 提交短剧媒资入库任务
func ImportPlayletResource(ctx context.Context, req *proto.PlayletResourceUseReq) (string, *srvdef.SrvErr) {
ids, err := validatePlayletResourceImportReq(req)
if err != nil {
return "", err
}
operatorId, operatorName := resolveImportOperator(ctx)
if err := pushPlayletResourceImportJob(ctx, ids, operatorId, operatorName); err != nil {
return "", err
}
return playletResourceImportMsg, nil
}
// importPlayletResourceBatch 批量处理短剧媒资入库任务
func importPlayletResourceBatch(ctx context.Context, ids []uint, operatorId int64, operatorName string) *proto.PlayletResourceUseResp {
resp := &proto.PlayletResourceUseResp{
Success: make([]*proto.PlayletResourceUseSuccess, 0, len(ids)),
Failed: make([]*proto.PlayletResourceUseFailed, 0),
}
importedMap, err := findImportedPlayletResourceMap(ctx, ids)
if err != nil {
for _, id := range ids {
resp.Failed = append(resp.Failed, &proto.PlayletResourceUseFailed{
MediaId: id,
Reason: "查询导入状态失败",
})
}
return resp
}
ms := newPlayletMediaCenterFromConfig()
for _, id := range ids {
if importedMap[id] {
resp.Failed = append(resp.Failed, &proto.PlayletResourceUseFailed{
MediaId: id,
Reason: "该媒资已导入",
})
continue
}
success, reason := importSinglePlayletResource(ctx, ms, id, operatorId, operatorName)
if reason != "" {
resp.Failed = append(resp.Failed, &proto.PlayletResourceUseFailed{
MediaId: id,
Reason: reason,
})
continue
}
resp.Success = append(resp.Success, success)
}
return resp
}
// findImportedPlayletResourceMap 批量查询已经导入的媒资ID
func findImportedPlayletResourceMap(ctx context.Context, ids []uint) (map[uint]bool, *srvdef.SrvErr) {
result := make(map[uint]bool, len(ids))
if len(ids) == 0 {
return result, nil
}
playletList, err := playletmod.FindManyPlaylet(ctx, bson.M{
playletmod.FN.Playlet.MediaCenterId: bson.M{"$in": ids},
})
if err != nil {
log.Error("批量查询短剧媒资导入状态失败", log.Any("mediaIds", ids), log.Any("err", err))
return nil, err
}
for _, item := range playletList {
if item == nil || item.MediaCenterId == 0 {
continue
}
result[item.MediaCenterId] = true
}
return result, nil
}
func newPlayletMediaCenterFromConfig() *playletMediaCenterService {
return newPlayletMediaCenterService(playletMediaCenterConfig{
Url: webg.Conf.MediaResourceDBCfg.BaseUrl,
ProId: webg.Conf.MediaResourceDBCfg.Merchat,
Secret: webg.Conf.MediaResourceDBCfg.Secret,
})
}
// validatePlayletResourceImportReq 校验短剧媒资入库请求
func validatePlayletResourceImportReq(req *proto.PlayletResourceUseReq) ([]uint, *srvdef.SrvErr) {
if req == nil {
return nil, srvdef.New(srvdef.ErrParamError, "参数不能为空", "")
}
ids := dedupePlayletResourceIds(req.Ids)
if len(ids) == 0 {
return nil, srvdef.New(srvdef.ErrParamError, "媒资ID不能为空", "")
}
if len(ids) > maxResourceImportIds {
return nil, srvdef.New(srvdef.ErrParamError, "单次最多导入100条媒资", "")
}
return ids, nil
}
// dedupePlayletResourceIds 去重并过滤无效媒资ID
func dedupePlayletResourceIds(ids []uint) []uint {
result := make([]uint, 0, len(ids))
seen := make(map[uint]struct{}, len(ids))
for _, id := range ids {
if id == 0 {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
result = append(result, id)
}
return result
}
// importSinglePlayletResource 导入单条短剧媒资
func importSinglePlayletResource(ctx context.Context, ms *playletMediaCenterService, mediaId uint, operatorId int64, operatorName string) (*proto.PlayletResourceUseSuccess, string) {
locked, err := lockPlayletResourceImportMedia(mediaId)
if err != nil {
log.Error("获取短剧媒资导入锁失败", log.Any("mediaId", mediaId), log.E(err))
return nil, "获取导入锁失败"
}
if !locked {
return nil, "该媒资正在导入"
}
defer unlockPlayletResourceImportMedia(mediaId)
if reason := checkPlayletResourceImported(ctx, mediaId); reason != "" {
return nil, reason
}
result, err := ms.mediaSync(&playletMediaSyncReq{
Id: mediaId,
SyncTime: 0,
})
if err != nil {
log.Error("同步短剧媒资失败", log.Any("mediaId", mediaId), log.E(err))
return nil, "同步媒资失败"
}
if result.IsDelete || result.Media == nil {
return nil, "媒资不存在或已删除"
}
if !validPlayletImportMediaType(result.Media.MediaType) {
return nil, "该媒资类型不支持导入短剧"
}
if len(result.MediaContentList) == 0 {
return nil, "媒资章节为空"
}
playlet := buildPlayletFromMedia(result.Media, operatorId, operatorName)
playlet.MediaSyncTime = maxPlayletMediaContentUpdateTime(0, result.MediaContentList)
if playlet.Title == "" {
return nil, "媒资标题为空"
}
exists := func(s string) bool {
item, _ := playletmod.FindOnePlaylet(ctx, slugx.Filter(playletmod.FN.Playlet.Slug, playletmod.FN.Playlet.Id, s, 0))
return item != nil
}
if serr := slugx.Ensure(&playlet.Slug, utils.SlugSourceFromKeywordsOrTitle(nil, playlet.Title), exists); serr != nil {
if serr.Tips != "" {
return nil, serr.Tips
}
return nil, "slug生成失败"
}
// 原图继续写入 Cover;压缩成功才写 CompressedCover,失败保持空。
playlet.CompressedCover = compressPlayletCoverOrEmpty(playlet.Cover)
chapters := buildPlayletChaptersFromMedia(result.MediaContentList)
if len(chapters) == 0 {
return nil, "媒资章节为空"
}
if err1 := playletmod.InsertOnePlaylet(ctx, playlet); err1 != nil {
log.Error("短剧媒资主表入库失败", log.Any("mediaId", mediaId), log.Any("err", err1))
return nil, "主资源入库失败"
}
for _, chapter := range chapters {
chapter.PlayletId = playlet.Id
}
if err1 := playletchaptermod.InsertManyPlayletChapter(ctx, chapters); err1 != nil {
log.Error("短剧媒资章节批量入库失败", log.Any("mediaId", mediaId), log.Any("playletId", playlet.Id), log.Any("chapterCount", len(chapters)), log.Any("err", err1))
rollbackImportedPlayletResource(ctx, playlet.Id, collectPlayletChapterIds(chapters))
return nil, "章节入库失败"
}
return &proto.PlayletResourceUseSuccess{
MediaId: mediaId,
PlayletId: playlet.Id,
}, ""
}
// collectPlayletChapterIds 收集章节ID
func collectPlayletChapterIds(chapters playletchaptermod.PlayletChapterList) []int64 {
ids := make([]int64, 0, len(chapters))
for _, chapter := range chapters {
if chapter == nil || chapter.Id == 0 {
continue
}
ids = append(ids, chapter.Id)
}
return ids
}
// checkPlayletResourceImported 检查媒资是否已经导入到本项目短剧表
func checkPlayletResourceImported(ctx context.Context, mediaId uint) string {
_, err := playletmod.FindOnePlaylet(ctx, bson.M{playletmod.FN.Playlet.MediaCenterId: mediaId})
if err == nil {
return "该媒资已导入"
}
if err.Code != srvdef.ErrNoData {
log.Error("查询短剧媒资导入状态失败", log.Any("mediaId", mediaId), log.Any("err", err))
return "查询导入状态失败"
}
return ""
}
// buildPlayletFromMedia 将媒资中心主资源转换为本项目 playlet 主表模型
func buildPlayletFromMedia(media *proto.PlayletResourceListItem, operatorId int64, operatorName string) *playletmod.Playlet {
cover := strings.TrimSpace(media.VerticalCover)
if cover == "" {
cover = strings.TrimSpace(media.HorizontalCover)
}
return &playletmod.Playlet{
MediaCenterId: uint(media.ID),
MediaCreatedAt: mediaResourceTime(media.CreateTime),
MediaUpdatedAt: mediaResourceTime(media.UpdateTime),
Title: strings.TrimSpace(media.Title),
Desc: strings.TrimSpace(media.Summary),
Anchor: strings.TrimSpace(media.Anchor),
Cover: cover,
OldTags: oldTagNamesFromPlayletTagsText(media.TagsText),
MediaBusiness: playletMediaBusiness(media),
ContentType: contentTypeFromMediaType(media.MediaType),
TotalEpisode: int64(media.TotalEpisode),
UpdateStatus: normalizePlayletUpdateStatus(media.UpdateStatus),
HeatValue: randomPlayletHeatValue(),
Status: playletmod.StatusDown,
OperatorId: operatorId,
OperatorName: operatorName,
}
}
// playletMediaBusinessCGDJ 赤果短剧、CGDJ 标题前缀对应的媒资业务来源。
const playletMediaBusinessCGDJ = "CGDJ"
// playletMediaBusinessRule 把媒资标题前缀映射成要写入的业务来源。先匹配先生效。
type playletMediaBusinessRule struct {
Prefix string
Business string
}
// playletMediaBusinessRules 多个前缀可以对应同一个业务来源。
// 新增来源时在这里追加;同一来源的不同叫法共用同一个 Business。
var playletMediaBusinessRules = []playletMediaBusinessRule{
//{Prefix: "赤果短剧", Business: playletMediaBusinessCGDJ},
{Prefix: "CGDJ", Business: playletMediaBusinessCGDJ},
}
func playletMediaBusiness(media *proto.PlayletResourceListItem) string {
if media == nil || media.MediaType != playletResourceMediaTypeShort {
return ""
}
title := strings.ToUpper(strings.TrimSpace(media.Title))
for _, rule := range playletMediaBusinessRules {
prefix := strings.ToUpper(strings.TrimSpace(rule.Prefix))
business := strings.TrimSpace(rule.Business)
if prefix == "" || business == "" {
continue
}
if strings.HasPrefix(title, prefix) {
return business
}
}
return ""
}
// playletMediaBusinessSearchTitles 返回回填时用来搜索媒资库的标题前缀。
func playletMediaBusinessSearchTitles() []string {
seen := make(map[string]struct{}, len(playletMediaBusinessRules))
titles := make([]string, 0, len(playletMediaBusinessRules))
for _, rule := range playletMediaBusinessRules {
prefix := strings.TrimSpace(rule.Prefix)
if prefix == "" {
continue
}
key := strings.ToUpper(prefix)
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
titles = append(titles, prefix)
}
return titles
}
func oldTagNamesFromPlayletTagsText(tagsText string) []string {
fields := strings.FieldsFunc(strings.TrimSpace(tagsText), func(r rune) bool {
switch r {
case ',', ',', '、', ';', ';', '/', '|':
return true
default:
return false
}
})
names := make([]string, 0, len(fields))
seen := make(map[string]struct{}, len(fields))
for _, field := range fields {
name := strings.TrimSpace(field)
if name == "" {
continue
}
if _, ok := seen[name]; ok {
continue
}
seen[name] = struct{}{}
names = append(names, name)
}
return names
}
func randomPlayletHeatValue() int64 {
return int64(rand.Intn(playletHeatMaxValue-playletHeatMinValue+1) + playletHeatMinValue)
}
// mediaResourceTime 转换媒资中心时间戳
func mediaResourceTime(ts int64) time.Time {
if ts <= 0 {
return time.Time{}
}
if ts > 1e12 {
return time.UnixMilli(ts)
}
return time.Unix(ts, 0)
}
// buildPlayletChaptersFromMedia 将媒资中心章节资源转换为本项目 playlet_chapter 模型
func buildPlayletChaptersFromMedia(mediaContentList []playletMediaContent) playletchaptermod.PlayletChapterList {
chapters := make(playletchaptermod.PlayletChapterList, 0, len(mediaContentList))
seenMediaContentIds := make(map[uint]struct{}, len(mediaContentList))
seenEpisodes := make(map[int]struct{}, len(mediaContentList))
seenHashIds := make(map[string]struct{}, len(mediaContentList))
seenVideoUrls := make(map[string]struct{}, len(mediaContentList))
for _, item := range mediaContentList {
videoUrl := strings.TrimSpace(item.M3u8Url)
if videoUrl == "" {
videoUrl = strings.TrimSpace(item.Urls)
}
if videoUrl == "" {
continue
}
if isDuplicatePlayletMediaChapter(item, videoUrl, seenMediaContentIds, seenEpisodes, seenHashIds, seenVideoUrls) {
continue
}
episodeNumber := int64(item.EpisodeNumber)
if episodeNumber <= 0 {
episodeNumber = int64(len(chapters) + 1)
}
height := int64(item.Height)
width := int64(item.Width)
ratio := item.Ratio
if ratio <= 0 && height > 0 {
ratio = float64(width) / float64(height)
}
chapters = append(chapters, &playletchaptermod.PlayletChapter{
MediaContentId: uint(item.ID),
CurrentEpisode: episodeNumber,
Title: strings.TrimSpace(item.Title),
Hash: item.HashId,
Height: height,
Width: width,
Ratio: ratio,
VideoUrl: videoUrl,
MediaSize: int64(item.MediaSize),
MediaTime: int64(item.PlayTime),
PayType: playletchaptermod.PayTypeFree,
SingleCoins: int64(item.SaleCoin),
WatermarkStatus: playletchaptermod.WatermarkStatusPending,
})
}
return chapters
}
// isDuplicatePlayletMediaChapter 判断媒资中心章节是否重复
func isDuplicatePlayletMediaChapter(item playletMediaContent, videoUrl string, seenMediaContentIds map[uint]struct{}, seenEpisodes map[int]struct{}, seenHashIds map[string]struct{}, seenVideoUrls map[string]struct{}) bool {
if item.ID > 0 {
if _, ok := seenMediaContentIds[item.ID]; ok {
log.Info("跳过重复短剧媒资章节", log.Any("mediaContentId", item.ID), log.Any("episodeNumber", item.EpisodeNumber))
return true
}
seenMediaContentIds[item.ID] = struct{}{}
return false
}
if item.EpisodeNumber > 0 {
if _, ok := seenEpisodes[item.EpisodeNumber]; ok {
log.Info("跳过重复短剧媒资章节", log.Any("episodeNumber", item.EpisodeNumber))
return true
}
seenEpisodes[item.EpisodeNumber] = struct{}{}
return false
}
hashId := strings.TrimSpace(item.HashId)
if hashId != "" {
if _, ok := seenHashIds[hashId]; ok {
log.Info("跳过重复短剧媒资章节", log.Any("hashId", hashId))
return true
}
seenHashIds[hashId] = struct{}{}
return false
}
if _, ok := seenVideoUrls[videoUrl]; ok {
log.Info("跳过重复短剧媒资章节", log.Any("videoUrl", videoUrl))
return true
}
seenVideoUrls[videoUrl] = struct{}{}
return false
}
// rollbackImportedPlayletResource 回滚单条媒资入库产生的数据
func rollbackImportedPlayletResource(ctx context.Context, playletId int64, chapterIds []int64) {
for _, chapterId := range chapterIds {
if _, err := playletchaptermod.DelOnePlayletChapter(ctx, bson.M{playletchaptermod.FN.PlayletChapter.Id: chapterId}); err != nil {
log.Error("回滚短剧媒资章节失败", log.Any("playletId", playletId), log.Any("chapterId", chapterId), log.Any("err", err))
}
}
if _, err := playletmod.DelOnePlaylet(ctx, bson.M{playletmod.FN.Playlet.Id: playletId}); err != nil {
log.Error("回滚短剧媒资主表失败", log.Any("playletId", playletId), log.Any("err", err))
}
}
// resolveImportOperator 从后台登录态解析当前操作人
func resolveImportOperator(ctx context.Context) (int64, string) {
c, ok := ctx.(*gin.Context)
if !ok || c == nil {
return 0, ""
}
accId := auth.GetAccount(c)
if accId <= 0 {
return 0, ""
}
acc, err := mwacc.FindOneAccByFilter(c, bson.M{
mwacc.FN.MwAcc.Id: accId,
}, options.FindOne().SetProjection(bson.D{
{Key: mwacc.FN.MwAcc.Id, Value: 1},
{Key: mwacc.FN.MwAcc.UserName, Value: 1},
}))
if err != nil || acc == nil {
return accId, ""
}
return acc.Id, acc.UserName
}
// maxPlayletMediaContentUpdateTime 记录本次同步返回章节中的最大更新时间
func maxPlayletMediaContentUpdateTime(syncTime int64, mediaContentList []playletMediaContent) int64 {
result := syncTime
for _, item := range mediaContentList {
if item.UpdateTime > result {
result = item.UpdateTime
}
}
return result
}
// normalizePlayletUpdateStatus 三方 update_status=0 在本项目按 2-已完结保存
func normalizePlayletUpdateStatus(updateStatus int) int {
if updateStatus == 0 {
return 2
}
return updateStatus
}