package mediaService import ( "91porn-server/common/log" "91porn-server/common/mediaCenter" "91porn-server/models/v/mediacontentmod" "91porn-server/models/v/mediamod" "fmt" "strings" "time" "go.mongodb.org/mongo-driver/bson/primitive" ) // SyncMediaData 同步acg动漫 func (m *mediaSync) SyncMediaData(media *mediamod.Media) (err error) { if media == nil { return } // 加锁,防止多个同步出错 // 判断当前是否有正在同步的任务,有的话就不执行 key := fmt.Sprintf("MediaSync:%v", media.MediaCenterId) has, err := m.rd.SetNX(key, 1, time.Minute*10) if err != nil { log.Error("SyncAll setNx fail", log.E(err)) return } if !has { log.Info("【ACG同步任务】正在进行", log.Any("task", key)) return } // 释放锁 defer m.rd.Del(key) log.Info("【ACG同步任务】开始", log.Any("mediaId", media.ID), log.Any("mediaCenterId", media.MediaCenterId)) // 同步数据 data, err := m.Ms.MediaSync(&mediaCenter.MediaSyncReq{ Id: media.MediaCenterId, SyncTime: media.SyncTime, }) if err != nil { log.Error("SyncMediaData call mediaSync Fail", log.Any("id", media.MediaCenterId), log.E(err)) return err } if data.IsDelete { // 数据已经删除,不需要同步 log.Info("【ACG同步任务】 数据在媒资库已经删除,不需要同步", log.Any("mediaId", media.ID), log.Any("mediaCenterId", media.MediaCenterId)) return } err = m.OnSyncMediaData(media.ID, data.Media) if err != nil { log.Error("SyncMediaData call OnSyncMediaData Fail", log.Any("id", media.MediaCenterId), log.E(err)) return err } newSyncTime, isAdd, err := m.OnSyncMediaContentData(media, data.MediaContentList) if err != nil { return } err = OnMediaContentChange(media.ID, media.MediaType, isAdd) if err != nil { log.Error("【ACG同步任务】 OnMediaContentChange Fail", log.Any("mediaId", media.ID), log.E(err)) return err } // 修改下一次需要同步的时间点位 m.OnSyncTimeChange(media.ID, newSyncTime) log.Info("【ACG同步任务】完成", log.Any("mediaId", media.ID), log.Any("mediaCenterId", media.MediaCenterId)) return nil } func (m *mediaSync) OnSyncMediaContentData(media *mediamod.Media, mediaContentDataList []mediaCenter.MediaContent) (int64, bool, error) { isAdd := false syncTime := media.SyncTime updatedIDs := make([]primitive.ObjectID, 0) if len(mediaContentDataList) == 0 { // 已经没有需要同步的数据了,直接返回 return syncTime, false, nil } log.Info("【ACG同步任务】获取到需要同步的子集", log.Any("mediaId", media.ID), log.Any("mediaCenterId", media.MediaCenterId), log.Any("子集数量", len(mediaContentDataList))) for _, mediaContentData := range mediaContentDataList { h265URL := normalizeH265URL(mediaContentData.H265Url) // 更改下一次同步的时间点 if mediaContentData.UpdateTime > syncTime { syncTime = mediaContentData.UpdateTime } log.Info("【ACG同步任务】同步子集", log.Any("mediaId", media.ID), log.Any("mediaCenterId", media.MediaCenterId), log.Any("子集ID", mediaContentData.ID)) // 判断是否已经存在该记录 mediaContent, err := mediacontentmod.GetInfoByHashId(media.ID, mediaContentData.HashId) if err != nil && err != mediacontentmod.MediaContentNotFound { log.Error("【ACG同步任务】同步子集失败", log.Any("mediaId", media.ID), log.Any("mediaCenterId", media.MediaCenterId), log.Any("子集ID", mediaContentData.ID), log.Any("err", err)) return 0, false, err } // TODO 后续同步,只做追加,不对之前的子集数据进行更新,避免数据出错 if err == mediacontentmod.MediaContentNotFound { isAdd = true // 不存在则入库 mediaContent = mediacontentmod.MediaContent{ MediaID: media.ID, MediaType: media.MediaType, EpisodeNumber: mediaContentData.EpisodeNumber, ListenPermission: 0, // 收听权限 0:会员 1:金币购买 2:免费 Name: mediaContentData.Title, // 章节名 Text: mediaContentData.Text, // 内容(小说专用) Author: media.Anchor, // 作者 Height: int(mediaContentData.Height), // 高 Weight: int(mediaContentData.Width), // 宽 MediaSize: int64(mediaContentData.MediaSize), // 资源大小 PlayTime: mediaContentData.PlayTime, // 影片长度 Status: 1, // 状态 0:转码中 1:转码成功 2:转码失败, 视频资源使用 UpdatedAct: "系统自动同步", // 操作用户账号 CreatedAt: time.Now(), // 文档创建时间 UpdateTime: time.Now(), // 文档更新时间 IsActive: true, // 同步过来的默认上架 HashId: mediaContentData.HashId, // 在媒资库中的唯一标识 VideoUrl: mediaContentData.M3u8Url, H265Url: h265URL, AudioUrl: mediaContentData.AudioUrl, } if media.MediaType == mediamod.MediaTypeDrama { mediaContent.Price = int64(mediaContentData.SaleCoin) mediaContent.ListenPermission = 1 if mediaContentData.SaleCoin == 0 { mediaContent.ListenPermission = 2 } } if strings.TrimSpace(mediaContentData.Urls) != "" { mediaContent.UrlSet = strings.Split(mediaContentData.Urls, ",") } else { mediaContent.UrlSet = []string{} } _, err = mediacontentmod.Insert(nil, mediaContent) if err != nil { log.Error("【ACG同步任务】同步子集失败", log.Any("mediaId", media.ID), log.Any("mediaCenterId", media.MediaCenterId), log.Any("子集ID", mediaContentData.ID), log.Any("err", err)) return 0, false, err } } else if shouldUpdateH265URL(mediaContent.H265Url, h265URL) { // 历史子集仍保持只追加策略,仅允许同步 H.265 地址并清理详情缓存。 _, err = mediacontentmod.UpdateByID(nil, mediaContent.ID, map[string]interface{}{ "h265Url": h265URL, "updateTime": time.Now(), }) if err != nil { log.Error("【ACG同步任务】更新H.265地址失败", log.Any("mediaId", media.ID), log.Any("mediaContentId", mediaContent.ID), log.Any("子集ID", mediaContentData.ID), log.Any("err", err)) return 0, false, err } updatedIDs = append(updatedIDs, mediaContent.ID) } } return syncTime, isAdd, nil } func normalizeH265URL(raw string) string { return strings.TrimSpace(raw) } func shouldUpdateH265URL(current, incoming string) bool { incoming = normalizeH265URL(incoming) return incoming != "" && normalizeH265URL(current) != incoming } func (m *mediaSync) OnSyncMediaData(mediaId primitive.ObjectID, mediaData *mediaCenter.Media) (err error) { //修改数据(不修改这个价格了,避免跟运营自己修改的价格冲突,只做部分内容的修改) updateData := make(map[string]interface{}) //updateData["title"] = data.Media.Title //updateData["horizontalCover"] = data.Media.HorizontalCover //updateData["verticalCover"] = data.Media.VerticalCover //updateData["summary"] = data.Media.Summary // TODO 后续更新只 对集数跟连载状态进行修改 updateData["totalEpisode"] = int(mediaData.TotalEpisode) updateData["updateStatus"] = mediaData.UpdateStatus if mediaData.UpdateStatus == 0 { // 直接是已完结 updateData["updateStatus"] = 2 } //updateData["anchor"] = data.Media.Anchor updateData["updateTime"] = time.Now() _, err = mediamod.UpdateByID(nil, mediaId, updateData) if err != nil { return } return }