结构调整

This commit is contained in:
jianzhichu
2026-01-19 09:57:40 +08:00
parent d9cb721f64
commit f5449f3ace
77 changed files with 4060 additions and 1516 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
using dy.net.dto;
using dy.net.model.dto;
using dy.net.service;
using Quartz;
using Serilog;
+30 -11
View File
@@ -1,5 +1,6 @@
using dy.net.dto;
using dy.net.model;
using dy.net.model.dto;
using dy.net.model.entity;
using dy.net.model.response;
using dy.net.service;
using dy.net.utils;
using System;
@@ -36,21 +37,26 @@ namespace dy.net.job
}
protected override bool IsCookieValid(DouyinCookie cookie)
{
return !string.IsNullOrWhiteSpace(cookie.Cookies)&& !string.IsNullOrWhiteSpace(cookie.SavePath);
}
protected override async Task<DouyinVideoInfo> FetchVideoData(DouyinCookie cookie, string cursor,string uperUid)
protected override async Task<DouyinVideoInfoResponse> FetchVideoData(DouyinCookie cookie, string cursor, DouyinFollowed followed,DouyinCollectItem collectItem=null)
{
if (cookie.UseCollectFolder && collectItem != null && !string.IsNullOrWhiteSpace(collectItem.CollectsId))
{
return await douyinHttpClientService.SyncCollectVideosByCollectId(cursor, count, cookie.Cookies, collectItem);
}
return await douyinHttpClientService.SyncCollectVideos(cursor, count, cookie.Cookies);
}
protected override bool ShouldContinueSync(DouyinCookie cookie, DouyinVideoInfo data, DouyinFollowed followed = null)
protected override bool ShouldContinueSync(DouyinCookie cookie, DouyinVideoInfoResponse data, DouyinFollowed followed = null)
{
return data != null && data.HasMore == 1 && cookie.CollHasSyncd == 0;
}
protected override string GetNextCursor(DouyinVideoInfo data)
protected override string GetNextCursor(DouyinVideoInfoResponse data)
{
return data?.Cursor ?? "0";
}
@@ -80,13 +86,26 @@ namespace dy.net.job
return new VideoEntityDifferences();
}
protected override string CreateSaveFolder(DouyinCookie cookie, Aweme item, AppConfig config, DouyinFollowed followed)
protected override string CreateSaveFolder(DouyinCookie cookie, Aweme item, AppConfig config, DouyinFollowed followed, DouyinCollectItem collectItem)
{
var (tag1, _, _) = GetVideoTags(item);
var safeTag1 = string.IsNullOrWhiteSpace(tag1) ? "other" : DouyinFileNameHelper.SanitizeLinuxFileName(tag1,"",true);
var folder = Path.Combine(cookie.SavePath, safeTag1, $"{DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId,true)}");
if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
return folder;
if(collectItem!=null&&cookie.UseCollectFolder &&!string.IsNullOrWhiteSpace(collectItem.CollectsId))
{
var safeCollectName = DouyinFileNameHelper.SanitizeLinuxFileName(collectItem.CollectsName, "", true);
var folderCollect = Path.Combine(cookie.SavePath, safeCollectName);
var safeVideoDesc = DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId, true);
var folder = Path.Combine(folderCollect, safeVideoDesc);
if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
return folder;
}
else
{
var (tag1, _, _) = GetVideoTags(item);
var safeTag1 = string.IsNullOrWhiteSpace(tag1) ? "other" : DouyinFileNameHelper.SanitizeLinuxFileName(tag1, "", true);
var folder = Path.Combine(cookie.SavePath, safeTag1, $"{DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId, true)}");
if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
return folder;
}
}
}
}
+7 -9
View File
@@ -1,10 +1,8 @@
using dy.net.dto;
using dy.net.model;
using dy.net.model.dto;
using dy.net.model.entity;
using dy.net.model.response;
using dy.net.service;
using dy.net.utils;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace dy.net.job
{
@@ -32,17 +30,17 @@ namespace dy.net.job
!string.IsNullOrWhiteSpace(cookie.SecUserId) && cookie.SecUserId.Length >= 10;
}
protected override async Task<DouyinVideoInfo> FetchVideoData(DouyinCookie cookie, string cursor,string uperUid)
protected override async Task<DouyinVideoInfoResponse> FetchVideoData(DouyinCookie cookie, string cursor,DouyinFollowed followed, DouyinCollectItem collectId)
{
return await douyinHttpClientService.SyncFavoriteVideos(count, cursor, cookie.SecUserId, cookie.Cookies);
}
protected override bool ShouldContinueSync(DouyinCookie cookie, DouyinVideoInfo data, DouyinFollowed followed=null)
protected override bool ShouldContinueSync(DouyinCookie cookie, DouyinVideoInfoResponse data, DouyinFollowed followed=null)
{
return data != null && data.HasMore == 1 && cookie.FavHasSyncd == 0;
}
protected override string GetNextCursor(DouyinVideoInfo data)
protected override string GetNextCursor(DouyinVideoInfoResponse data)
{
return data?.MaxCursor ?? "0";
}
@@ -67,7 +65,7 @@ namespace dy.net.job
}
}
protected override string CreateSaveFolder(DouyinCookie cookie, Aweme item, AppConfig config, DouyinFollowed followed)
protected override string CreateSaveFolder(DouyinCookie cookie, Aweme item, AppConfig config, DouyinFollowed followed, DouyinCollectItem collectItem)
{
var (tag1, _, _) = GetVideoTags(item);
var safeTag1 = string.IsNullOrWhiteSpace(tag1) ? "other" : DouyinFileNameHelper.SanitizeLinuxFileName(tag1,"",true);
+85 -52
View File
@@ -1,17 +1,12 @@
using ClockSnowFlake;
using dy.net.dto;
using dy.net.model;
using dy.net.model.dto;
using dy.net.model.entity;
using dy.net.model.response;
using dy.net.service;
using dy.net.utils;
using Newtonsoft.Json;
using Quartz;
using Quartz.Util;
using Serilog;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace dy.net.job
{
@@ -133,26 +128,22 @@ namespace dy.net.job
/// <returns>一个表示异步操作的任务</returns>
public async Task Execute(IJobExecutionContext context)
{
// 1. 获取应用配置
//if (VideoType != VideoTypeEnum.dy_follows)
// return;
// 获取应用配置
var config = douyinCommonService.GetConfig();
if (config == null)
{
Log.Debug($"{VideoType}-未获取到系统配置,任务终止!!!");
return;
}
//if(VideoType!= VideoTypeEnum.dy_follows)
//{
// return;
//}
// 2. 从配置中获取每页请求数量--固定18
//if (config.BatchCount > 0)
// count = config.BatchCount.ToString();
// 3. 在处理Cookie之前执行的预处理操作
// 在处理Cookie之前执行的预处理
await BeforeProcessCookies();
// 4. 获取所有有效的Cookie
// 获取所有有效的Cookie
var cookies = await GetValidCookies();
if (cookies == null || !cookies.Any())
{
@@ -161,7 +152,7 @@ namespace dy.net.job
}
Log.Debug($"{VideoType}-共发现{cookies.Count}个有效的cookie,同步即将开始...");
// 6. 遍历每个有效的Cookie,执行同步操作
// 遍历每个有效的Cookie,执行同步
foreach (var cookie in cookies)
{
await ProcessSyncUserCookie(cookie, config);
@@ -202,8 +193,9 @@ namespace dy.net.job
/// <param name="cookie">用户Cookie</param>
/// <param name="cursor">分页游标,用于获取下一页数据</param>
/// <param name="uperUid">关注的人</param>
/// <param name="collectId">收藏夹ID</param>
/// <returns>视频信息对象,包含视频列表和分页信息</returns>
protected abstract Task<DouyinVideoInfo> FetchVideoData(DouyinCookie cookie, string cursor, string uperUid = "");
protected abstract Task<DouyinVideoInfoResponse> FetchVideoData(DouyinCookie cookie, string cursor, DouyinFollowed uperUid , DouyinCollectItem collectId);
/// <summary>
/// 判断是否应该继续同步下一页数据
@@ -213,7 +205,7 @@ namespace dy.net.job
/// <param name="data">当前获取到的视频数据</param>
/// <param name="followed">关注博主</param>
/// <returns>如果应该继续同步,则为true;否则为false</returns>
protected abstract bool ShouldContinueSync(DouyinCookie cookie, DouyinVideoInfo data, DouyinFollowed followed);
protected abstract bool ShouldContinueSync(DouyinCookie cookie, DouyinVideoInfoResponse data, DouyinFollowed followed);
/// <summary>
/// 获取下一页数据的游标
@@ -221,7 +213,7 @@ namespace dy.net.job
/// </summary>
/// <param name="data">当前获取到的视频数据</param>
/// <returns>下一页数据的游标</returns>
protected abstract string GetNextCursor(DouyinVideoInfo data);
protected abstract string GetNextCursor(DouyinVideoInfoResponse data);
/// <summary>
/// 创建视频保存文件夹
@@ -230,9 +222,10 @@ namespace dy.net.job
/// <param name="cookie">用户Cookie</param>
/// <param name="item">视频信息</param>
/// <param name="followed">关注用户</param>
/// <param name="collectItem">收藏夹Item</param>
/// <param name="config">应用配置</param>
/// <returns>创建的视频保存文件夹路径</returns>
protected abstract string CreateSaveFolder(DouyinCookie cookie, Aweme item, AppConfig config, DouyinFollowed followed);
protected abstract string CreateSaveFolder(DouyinCookie cookie, Aweme item, AppConfig config, DouyinFollowed followed,DouyinCollectItem collectItem);
/// <summary>
/// 获取视频文件名,默认就用id作文件名
@@ -279,7 +272,6 @@ namespace dy.net.job
/// <summary>
/// 获取NFO文件中的图片(如海报)文件名
/// 子类可以重写此方法来自定义NFO图片的命名规则
/// </summary>
/// <param name="cookie">当前操作的Cookie</param>
/// <param name="item">视频信息</param>
@@ -343,16 +335,57 @@ namespace dy.net.job
}
else
{
int syncCount = 0; // 本次同步成功的视频数量
string cursor = "0"; // 初始游标
bool hasMore = true; // 是否还有更多数据
(syncCount, cursor, hasMore) = await GetViedos(cookie, config, syncCount, cursor, hasMore);
// 仅在视频类型为抖音收藏时执行对应逻辑
if (VideoType == VideoTypeEnum.dy_collects)
{
// 统一声明变量,避免分散定义,提升可读性
int syncCount = 0; // 本次同步成功的视频数量
string cursor = "0"; // 初始游标
bool hasMore = true; // 是否还有更多数据
// 处理同步完成后的操作
await HandleSyncCompletion(cookie, syncCount);
// 处理「使用收藏文件夹」和「不使用收藏文件夹」的统一逻辑
if (cookie.UseCollectFolder)
{
// 获取收藏文件夹列表
var folders = await douyinHttpClientService.SyncCollectFolderList(cookie.Cookies,"");
Log.Debug("获取收藏文件夹列表完成,共计{Count}个包含视频的收藏夹", folders?.CollectsList?.Count(x => x.TotalNumber > 0) ?? 0);
// 文件夹为空时,执行默认同步逻辑(与不使用文件夹逻辑一致)
if (folders?.CollectsList == null || !folders.CollectsList.Any(x => x.TotalNumber > 0))
{
(syncCount, cursor, hasMore) = await GetViedos(cookie, config, syncCount, cursor, hasMore);
await HandleSyncCompletion(cookie, syncCount);
}
// 文件夹不为空时,遍历每个文件夹同步视频
else
{
foreach (var item in folders.CollectsList.Where(x=>x.TotalNumber>0))
{
// 重置游标,每个文件夹独立从头同步
string folderCursor = "0";
await GetViedos(cookie, config, syncCount, folderCursor, hasMore, null, item);
await HandleSyncCompletion(cookie, syncCount, null);
}
}
}
// 不使用收藏文件夹,直接执行默认同步逻辑
else
{
(syncCount, cursor, hasMore) = await GetViedos(cookie, config, syncCount, cursor, hasMore);
await HandleSyncCompletion(cookie, syncCount);
}
}
// 非抖音收藏类型,执行统一的默认同步逻辑
else
{
// 提取重复变量,与上方逻辑保持一致命名
int syncCount = 0;
string cursor = "0";
bool hasMore = true;
(syncCount, cursor, hasMore) = await GetViedos(cookie, config, syncCount, cursor, hasMore);
await HandleSyncCompletion(cookie, syncCount);
}
}
}
catch (Exception ex)
{
@@ -360,13 +393,13 @@ namespace dy.net.job
}
}
private async Task<(int syncCount, string cursor, bool hasMore)> GetViedos(DouyinCookie cookie, AppConfig config, int syncCount, string cursor, bool hasMore, DouyinFollowed followed = null)
private async Task<(int syncCount, string cursor, bool hasMore)> GetViedos(DouyinCookie cookie, AppConfig config, int syncCount, string cursor, bool hasMore, DouyinFollowed followed = null,DouyinCollectItem collectItem=null)
{
// 循环获取视频数据
while (hasMore)
{
// 获取视频数据
var data = await FetchVideoData(cookie, cursor, followed == null ? "" : followed?.SecUid);
var data = await FetchVideoData(cookie, cursor, followed,collectItem);
if (data == null)
{
Log.Debug($"{VideoType}-Cookie[{cookie.UserName}]同步数据失败!!!");
@@ -383,7 +416,7 @@ namespace dy.net.job
break;
// 处理视频列表
var videos = await ProcessVideoList(cookie, data, config, followed);
var videos = await ProcessVideoList(cookie, data, config, followed, collectItem);
// 保存视频信息到数据库
syncCount += await SaveVideos(videos);
@@ -435,23 +468,23 @@ namespace dy.net.job
return false;
}
/// <summary>
/// 处理视频列表
/// 遍历视频列表,分别处理每个视频和图片集
/// </summary>
/// <param name="cookie">用户Cookie</param>
/// <param name="data">视频信息对象</param>
/// <param name="config">应用配置</param>
/// <param name="followed">关注的</param>
/// <param name="collectItem">收藏夹Item</param>
/// <returns>处理后的视频实体列表</returns>
protected async Task<List<DouyinVideo>> ProcessVideoList(DouyinCookie cookie, DouyinVideoInfo data, AppConfig config, DouyinFollowed followed = null)
protected async Task<List<DouyinVideo>> ProcessVideoList(DouyinCookie cookie, DouyinVideoInfoResponse data, AppConfig config, DouyinFollowed followed = null,DouyinCollectItem collectItem=null)
{
var videos = new List<DouyinVideo>();
foreach (var item in data.AwemeList)
{
if (item.AwemeId != "7440444948844956985")
{
continue;
}
//if (item.AwemeId != "7440444948844956985")
//{
// continue;
//}
//if (!item.Desc.Contains("小房车正式启程"))
//{
// continue;
@@ -488,7 +521,7 @@ namespace dy.net.job
}
}
// 处理单个视频
var video = await ProcessSingleVideo(cookie, item, data, config, followed);
var video = await ProcessSingleVideo(cookie, item, data, config, followed,collectItem);
if (video != null)
videos.Add(video);
else
@@ -578,7 +611,7 @@ namespace dy.net.job
// 统一处理图文视频逻辑
if (config.DownImageVideo || config.DownMp3 || config.DownImage)
{
var mergevideo = await ProcessImageSetAndMergeToVideo(cookie, item, data, config, followed);
var mergevideo = await ProcessImageSetAndMergeToVideo(cookie, item, data, config, followed, collectItem);
if (mergevideo != null)
videos.Add(mergevideo);
}
@@ -773,8 +806,9 @@ namespace dy.net.job
/// <param name="data">视频信息对象</param>
/// <param name="config">应用配置</param>
/// <param name="followed">关注</param>
/// <param name="collectItem">收藏夹Item</param>
/// <returns>处理后的视频实体,如果处理失败则为null</returns>
protected async Task<DouyinVideo> ProcessSingleVideo(DouyinCookie cookie, Aweme item, DouyinVideoInfo data, AppConfig config, DouyinFollowed followed = null)
protected async Task<DouyinVideo> ProcessSingleVideo(DouyinCookie cookie, Aweme item, DouyinVideoInfoResponse data, AppConfig config, DouyinFollowed followed = null,DouyinCollectItem collectItem=null)
{
// 检查视频数据是否有效
if (!IsAwemeValid(item)) return null;
@@ -790,7 +824,7 @@ namespace dy.net.job
// 获取视频标签
var (tag1, tag2, tag3) = GetVideoTags(item);
// 创建保存文件夹
var saveFolder = CreateSaveFolder(cookie, item, config, followed);
var saveFolder = CreateSaveFolder(cookie, item, config, followed,collectItem);
// 获取视频文件名
var fileName = GetVideoFileName(cookie, item, config);
// 拼接视频保存路径
@@ -836,14 +870,14 @@ namespace dy.net.job
protected async Task<DouyinVideo> ProcessDynamicVideo(List<DouyinDynamicVideoDto> dynamicUrls, DouyinCookie cookie, Aweme item, DouyinVideoInfo data, AppConfig config, DouyinFollowed followed = null)
protected async Task<DouyinVideo> ProcessDynamicVideo(List<DouyinDynamicVideoDto> dynamicUrls, DouyinCookie cookie, Aweme item, DouyinVideoInfoResponse data, AppConfig config, DouyinFollowed followed = null,DouyinCollectItem collectItem=null)
{
// 获取视频标签
var (tag1, tag2, tag3) = GetVideoTags(item);
// 创建保存文件夹
var saveFolder = CreateSaveFolder(cookie, item, config, followed);
var saveFolder = CreateSaveFolder(cookie, item, config, followed,collectItem);
// 获取视频文件名
//var fileName = DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId) + ".mp4";
@@ -969,7 +1003,6 @@ namespace dy.net.job
}
/// <summary>
/// 处理图片集并合成为视频
/// 负责下载图片、合成视频、处理封面和头像等
/// </summary>
/// <param name="cookie">用户Cookie</param>
@@ -978,7 +1011,7 @@ namespace dy.net.job
/// <param name="config">应用配置</param>
/// <param name="followed">应用配置</param>
/// <returns>合成后的视频实体,如果处理失败则为null</returns>
protected async Task<DouyinVideo> ProcessImageSetAndMergeToVideo(DouyinCookie cookie, Aweme item, DouyinVideoInfo data, AppConfig config, DouyinFollowed followed)
protected async Task<DouyinVideo> ProcessImageSetAndMergeToVideo(DouyinCookie cookie, Aweme item, DouyinVideoInfoResponse data, AppConfig config, DouyinFollowed followed,DouyinCollectItem collectItem)
{
try
{
@@ -1010,7 +1043,7 @@ namespace dy.net.job
}
else
{
fileNamefolder = CreateSaveFolder(cookie, item, config, followed);
fileNamefolder = CreateSaveFolder(cookie, item, config, followed,collectItem);
}
if (!Directory.Exists(fileNamefolder)) Directory.CreateDirectory(fileNamefolder);
@@ -1293,7 +1326,7 @@ namespace dy.net.job
/// <returns>创建的视频实体对象</returns>
private async Task<DouyinVideo> CreateVideoEntity(AppConfig config,
DouyinCookie cookie, Aweme item, VideoBitRate bitRate, string savePath, string saveFolder,
string tag1, string tag2, string tag3, string avatarSavePath, string avatarUrl, DouyinVideoInfo data, List<DouyinDynamicVideoDto> dynamicVideos = null)
string tag1, string tag2, string tag3, string avatarSavePath, string avatarUrl, DouyinVideoInfoResponse data, List<DouyinDynamicVideoDto> dynamicVideos = null)
{
var diffs = GetVideoEntityDifferences(cookie, item);
+3 -3
View File
@@ -1,5 +1,5 @@
using dy.net.dto;
using dy.net.model;
using dy.net.model.entity;
using dy.net.model.response;
using dy.net.service;
using Quartz;
using System.Collections.Generic;
@@ -70,7 +70,7 @@ namespace dy.net.job
{
hasmore = false;
}
ck.StatusMsg = err.StatusCode == 8 ? "无效" : "正 常";
ck.StatusMsg = err.StatusCode == 8 ? "无效" : "正常";
ck.StatusCode = err.StatusCode;
await _dyCookieService.UpdateAsync(ck);
});
+8 -7
View File
@@ -1,6 +1,7 @@
using ClockSnowFlake;
using dy.net.dto;
using dy.net.model;
using dy.net.model.dto;
using dy.net.model.entity;
using dy.net.model.response;
using dy.net.service;
using dy.net.utils;
using Newtonsoft.Json;
@@ -29,17 +30,17 @@ namespace dy.net.job
return !string.IsNullOrWhiteSpace(cookie.Cookies)&&!string.IsNullOrWhiteSpace(cookie.UpSavePath);
}
protected override async Task<DouyinVideoInfo> FetchVideoData(DouyinCookie cookie, string cursor, string uperUid = "")
protected override async Task<DouyinVideoInfoResponse> FetchVideoData(DouyinCookie cookie, string cursor, DouyinFollowed followed, DouyinCollectItem collectId)
{
return await douyinHttpClientService.SyncUpderPostVideos(count, cursor, uperUid, cookie.Cookies);
return await douyinHttpClientService.SyncUpderPostVideos(count, cursor, followed.SecUid, cookie.Cookies);
}
protected override bool ShouldContinueSync(DouyinCookie cookie, DouyinVideoInfo data, DouyinFollowed followed)
protected override bool ShouldContinueSync(DouyinCookie cookie, DouyinVideoInfoResponse data, DouyinFollowed followed)
{
return data != null && data.HasMore == 1 && cookie.UperSyncd == 0 && followed.FullSync;
}
protected override string GetNextCursor(DouyinVideoInfo data)
protected override string GetNextCursor(DouyinVideoInfoResponse data)
{
return data?.MaxCursor ?? "0";
}
@@ -52,7 +53,7 @@ namespace dy.net.job
/// <param name="followed"></param>
/// <param name="config"></param>
/// <returns></returns>
protected override string CreateSaveFolder(DouyinCookie cookie, Aweme item, AppConfig config, DouyinFollowed followed)
protected override string CreateSaveFolder(DouyinCookie cookie, Aweme item, AppConfig config, DouyinFollowed followed, DouyinCollectItem collectItem)
{
#region 使UP主名称作为文件夹名称使
// 1. 优先获取有效的作者名称(遵循原有优先级:followed.UperName > item.Author.Nickname > 默认值)