From e270397334c01308eadc670944fd7f0e485a6288 Mon Sep 17 00:00:00 2001 From: jianzhichu Date: Thu, 22 Jan 2026 08:33:59 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=90=88=E9=9B=86=E3=80=81?= =?UTF-8?q?=E7=9F=AD=E5=89=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...CollectController.cs => CateController.cs} | 4 +- Controllers/ConfigController.cs | 8 +- Controllers/VideoController.cs | 256 ++++- Program.cs | 3 +- Properties/PublishProfiles/docker.pubxml.user | 4 +- README.md | 35 +- app/src/pages/cok/CookieTable.vue | 185 ++-- app/src/pages/desk/index.vue | 10 +- app/src/pages/set/AppSet.vue | 10 +- app/src/pages/workplace/RecordTable.vue | 125 ++- app/src/pages/workplace/statics copy.vue | 940 ++++++++++++++++++ app/src/pages/workplace/statics.vue | 853 +++++++++------- app/src/store/coreapi.ts | 28 + docs/findcookie_1_2_3.png | Bin 0 -> 421393 bytes docs/findcookie_4.png | Bin 0 -> 245588 bytes docs/findcookie_5.png | Bin 0 -> 174020 bytes docs/findcookie_6_7.png | Bin 0 -> 288308 bytes docs/findcookie_8.png | Bin 0 -> 174117 bytes dy.net.csproj | 6 + dy.net.csproj.user | 4 +- extension/ServiceExtension.cs | 1 + job/DependencyJobListener.cs | 180 ++-- job/DouYinCollectSyncJob.cs | 34 +- job/DouYinFavoritSyncJob.cs | 33 +- job/DouyinBasicSyncJob.cs | 489 +++++---- job/DouyinCollectCustomSyncJob.cs | 22 +- ...edoSyncJob.cs => DouyinFollowedSyncJob.cs} | 75 +- job/DouyinFollowsAndCollnectsSyncJob.cs | 347 +++---- job/DouyinMixSyncJob.cs | 24 +- job/DouyinSeriesSyncJob.cs | 19 + model/dto/DouyinVideoPageRequestDto.cs | 4 + model/dto/VideoChartItemDto.cs | 40 + model/dto/VideoStaticsDto.cs | 5 + model/entity/AppConfig.cs | 4 +- model/entity/DouyinCollectCate.cs | 10 +- model/entity/DouyinCookie.cs | 36 +- model/entity/DouyinVideo.cs | 6 + model/response/DouyinMixListResponse.cs | 10 +- model/response/DouyinSeriesListResponse.cs | 34 +- model/response/DouyinVideoInfoResponse.cs | 106 +- repository/DouyinCollectCateRepository.cs | 25 +- repository/DouyinVideoRepository.cs | 15 +- service/DouyinCollectCateService.cs | 24 +- service/DouyinCommonService.cs | 3 +- service/DouyinCookieService.cs | 6 +- service/DouyinHttpClientService.cs | 23 +- service/DouyinQuartzJobService.cs | 174 ++-- service/DouyinQuartzJobServiceXXX.cs | 371 +++++++ service/DouyinVideoService.cs | 205 +++- utils/DouyinRequestParamManager.cs | 89 +- utils/NfoFileGenerator.cs | 28 +- 51 files changed, 3502 insertions(+), 1411 deletions(-) rename Controllers/{CollectController.cs => CateController.cs} (91%) create mode 100644 app/src/pages/workplace/statics copy.vue create mode 100644 docs/findcookie_1_2_3.png create mode 100644 docs/findcookie_4.png create mode 100644 docs/findcookie_5.png create mode 100644 docs/findcookie_6_7.png create mode 100644 docs/findcookie_8.png rename job/{DouyinFollowedViedoSyncJob.cs => DouyinFollowedSyncJob.cs} (67%) create mode 100644 model/dto/VideoChartItemDto.cs create mode 100644 service/DouyinQuartzJobServiceXXX.cs diff --git a/Controllers/CollectController.cs b/Controllers/CateController.cs similarity index 91% rename from Controllers/CollectController.cs rename to Controllers/CateController.cs index 280f1a1..1a55367 100644 --- a/Controllers/CollectController.cs +++ b/Controllers/CateController.cs @@ -11,12 +11,12 @@ namespace dy.net.Controllers [Route("api/[controller]")] [ApiController] [Authorize] - public class CollectController : ControllerBase + public class CateController : ControllerBase { private readonly DouyinCollectCateService _douyinCollectCateService; private readonly DouyinQuartzJobService _douyinQuartzJobService; - public CollectController(DouyinCollectCateService douyinCollectCateService, DouyinQuartzJobService douyinQuartzJobService) + public CateController(DouyinCollectCateService douyinCollectCateService, DouyinQuartzJobService douyinQuartzJobService) { this._douyinCollectCateService = douyinCollectCateService; _douyinQuartzJobService = douyinQuartzJobService; diff --git a/Controllers/ConfigController.cs b/Controllers/ConfigController.cs index 9311aba..8f0f8d8 100644 --- a/Controllers/ConfigController.cs +++ b/Controllers/ConfigController.cs @@ -197,10 +197,10 @@ namespace dy.net.Controllers return ApiResult.Fail($"请在飞牛应用设置里面将{dyUserCookies.UpSavePath}添加读写权限"); } - if (!string.IsNullOrWhiteSpace(dyUserCookies.ImgSavePath) && !DouyinFileUtils.HasDirectoryReadWritePermission(dyUserCookies.ImgSavePath)) - { - return ApiResult.Fail($"请在飞牛应用设置里面将{dyUserCookies.ImgSavePath}添加读写权限"); - } + //if (!string.IsNullOrWhiteSpace(dyUserCookies.ImgSavePath) && !DouyinFileUtils.HasDirectoryReadWritePermission(dyUserCookies.ImgSavePath)) + //{ + // return ApiResult.Fail($"请在飞牛应用设置里面将{dyUserCookies.ImgSavePath}添加读写权限"); + //} var checkCk = await httpClientService.CheckCookie(dyUserCookies); diff --git a/Controllers/VideoController.cs b/Controllers/VideoController.cs index 91bf07c..1299211 100644 --- a/Controllers/VideoController.cs +++ b/Controllers/VideoController.cs @@ -47,8 +47,7 @@ namespace dy.net.Controllers [HttpGet("statics")] public async Task GetStaticsAsync() { - var data = await douyinVideoService.GetStatics(); - return ApiResult.Success(data); + return ApiResult.Success(await douyinVideoService.GetStatics()); } /// @@ -68,7 +67,7 @@ namespace dy.net.Controllers { return ApiResult.Fail($"视频不存在:{vid}"); } - return PlayViedo(viedo); + return await PlayVideoAsync(viedo); } catch (Exception ex) { @@ -76,11 +75,85 @@ namespace dy.net.Controllers } } - private IActionResult PlayViedo(DouyinVideo viedo) - { + //private IActionResult PlayViedo(DouyinVideo viedo) + //{ - // 1. 拼接完整物理路径(配置路径 + 文件名) - string videoFullPath = viedo.VideoSavePath; + // // 1. 拼接完整物理路径(配置路径 + 文件名) + // string videoFullPath = viedo.VideoSavePath; + + // // 2. 验证文件是否存在 + // if (!System.IO.File.Exists(videoFullPath)) + // { + // return ApiResult.Fail($"视频文件不存在:{videoFullPath}"); + // } + + // // 3. 获取文件信息(大小、类型) + // var fileInfo = new FileInfo(videoFullPath); + // long fileSize = fileInfo.Length; + // string contentType = GetContentType(videoFullPath); // 自动识别视频 MIME 类型 + + // // 4. 处理分片请求(前端视频标签自动发起,支持断点续传) + // if (Request.Headers.ContainsKey("Range") && long.TryParse(Request.Headers.Range.ToString().Split('=')[1].Split('-')[0], out long start)) + // { + // // 分片起始位置(前端请求的起始字节) + // long end = Math.Min(start + 1024 * 1024 * 2, fileSize - 1); // 每片 2MB(可调整) + // long chunkSize = end - start + 1; + + // // 5. 设置分片响应头 + // Response.StatusCode = StatusCodes.Status206PartialContent; + // Response.Headers.Add("Content-Range", $"bytes {start}-{end}/{fileSize}"); + // Response.Headers.Add("Accept-Ranges", "bytes"); + // Response.Headers.Add("Content-Length", chunkSize.ToString()); + + // // 6. 读取分片并返回流 + // var stream = new FileStream( + // videoFullPath, + // FileMode.Open, + // FileAccess.Read, + // FileShare.Read, + // bufferSize: 4096, + // useAsync: true); + + // stream.Seek(start, SeekOrigin.Begin); + // return new FileStreamResult(stream, contentType); + // } + // else + // { + // // 完整文件请求(兼容旧浏览器) + // return PhysicalFile(videoFullPath, contentType, enableRangeProcessing: true); + // } + //} + + /// + /// 抖音视频播放接口(优化版) + /// 解决内存泄漏、资源释放不及时、鲁棒性不足等问题 + /// + /// 视频实体(修正拼写错误:viedo -> video) + /// 视频流响应 + public async Task PlayVideoAsync(DouyinVideo video) + { + // 空值校验 + if (video == null) + { + return ApiResult.Fail("视频信息不能为空"); + } + + // 1. 获取完整物理路径并校验 + string videoFullPath = video.VideoSavePath; + if (string.IsNullOrWhiteSpace(videoFullPath)) + { + return ApiResult.Fail("视频保存路径不能为空"); + } + + // 安全校验:防止路径遍历攻击 + try + { + videoFullPath = Path.GetFullPath(videoFullPath); + } + catch + { + return ApiResult.Fail("视频路径格式非法"); + } // 2. 验证文件是否存在 if (!System.IO.File.Exists(videoFullPath)) @@ -88,36 +161,117 @@ namespace dy.net.Controllers return ApiResult.Fail($"视频文件不存在:{videoFullPath}"); } - // 3. 获取文件信息(大小、类型) - var fileInfo = new FileInfo(videoFullPath); - long fileSize = fileInfo.Length; - string contentType = GetContentType(videoFullPath); // 自动识别视频 MIME 类型 - - // 4. 处理分片请求(前端视频标签自动发起,支持断点续传) - if (Request.Headers.ContainsKey("Range") && long.TryParse(Request.Headers.Range.ToString().Split('=')[1].Split('-')[0], out long start)) + try { - // 分片起始位置(前端请求的起始字节) - long end = Math.Min(start + 1024 * 1024 * 2, fileSize - 1); // 每片 2MB(可调整) - long chunkSize = end - start + 1; + // 3. 获取文件信息(使用using确保FileInfo资源释放) + var fileInfo = new FileInfo(videoFullPath); + long fileSize = fileInfo.Length; - // 5. 设置分片响应头 - Response.StatusCode = StatusCodes.Status206PartialContent; - Response.Headers.Add("Content-Range", $"bytes {start}-{end}/{fileSize}"); - Response.Headers.Add("Accept-Ranges", "bytes"); - Response.Headers.Add("Content-Length", chunkSize.ToString()); + // 校验空文件 + if (fileSize == 0) + { + return ApiResult.Fail($"视频文件为空:{videoFullPath}"); + } - // 6. 读取分片并返回流 - var stream = new FileStream(videoFullPath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true); - stream.Seek(start, SeekOrigin.Begin); - return new FileStreamResult(stream, contentType); + string contentType = GetContentType(videoFullPath); + + // 4. 处理分片请求(Range) + if (Request.Headers.ContainsKey("Range")) + { + return await HandleRangeRequestAsync(videoFullPath, fileSize, contentType); + } + else + { + // 完整文件请求(启用分片处理,兼容前端断点续传) + return PhysicalFile(videoFullPath, contentType, enableRangeProcessing: true); + } } - else + catch (UnauthorizedAccessException) { - // 完整文件请求(兼容旧浏览器) - return PhysicalFile(videoFullPath, contentType, enableRangeProcessing: true); + return ApiResult.Fail($"没有权限访问视频文件:{videoFullPath}"); + } + catch (IOException ex) + { + // 记录日志(建议添加日志框架,如Serilog/NLog) + // _logger.LogError(ex, "读取视频文件失败:{Path}", videoFullPath); + return ApiResult.Fail($"读取视频文件失败:{ex.Message}"); + } + catch (Exception ex) + { + // _logger.LogError(ex, "视频播放接口异常:{Path}", videoFullPath); + return ApiResult.Fail($"服务器内部错误:{ex.Message}"); } } + /// + /// 处理分片请求(Range),异步安全处理流 + /// + /// 文件路径 + /// 文件总大小 + /// 内容类型 + /// 分片响应 + private async Task HandleRangeRequestAsync(string filePath, long fileSize, string contentType) + { + string rangeHeader = Request.Headers["Range"].ToString(); + + // 安全解析Range头 + if (!rangeHeader.StartsWith("bytes=")) + { + // 416 - 请求的范围无法满足 + Response.StatusCode = StatusCodes.Status416RequestedRangeNotSatisfiable; + Response.Headers.Add("Content-Range", $"bytes */{fileSize}"); + return new EmptyResult(); + } + + // 解析起始位置 + string[] rangeParts = rangeHeader.Split('=')[1].Split('-'); + if (!long.TryParse(rangeParts[0], out long start) || start < 0 || start >= fileSize) + { + Response.StatusCode = StatusCodes.Status416RequestedRangeNotSatisfiable; + Response.Headers.Add("Content-Range", $"bytes */{fileSize}"); + return new EmptyResult(); + } + + // 计算分片结束位置(每片2MB,可配置) + long end = Math.Min(start + 1024 * 1024 * 2, fileSize - 1); + long chunkSize = end - start + 1; + + // 设置206分片响应头 + Response.StatusCode = StatusCodes.Status206PartialContent; + Response.Headers.Add("Content-Range", $"bytes {start}-{end}/{fileSize}"); + Response.Headers.Add("Accept-Ranges", "bytes"); + Response.Headers.Add("Content-Length", chunkSize.ToString()); + Response.ContentType = contentType; + + // 核心改进:使用异步流 + using确保释放(通过管道直接写入响应流) + await using var fileStream = new FileStream( + filePath, + FileMode.Open, + FileAccess.Read, + FileShare.Read, + bufferSize: 8192, // 增大缓冲区提升性能,减少IO次数 + FileOptions.Asynchronous | FileOptions.SequentialScan); // 顺序扫描优化 + + // 定位到分片起始位置 + fileStream.Seek(start, SeekOrigin.Begin); + + // 直接写入响应流,避免FileStreamResult的延迟释放问题 + var buffer = new byte[8192]; + long remainingBytes = chunkSize; + + while (remainingBytes > 0) + { + int bytesRead = await fileStream.ReadAsync(buffer, 0, (int)Math.Min(remainingBytes, buffer.Length)); + if (bytesRead == 0) break; + + await Response.Body.WriteAsync(buffer.AsMemory(0, bytesRead)); + remainingBytes -= bytesRead; + } + + await Response.Body.FlushAsync(); + + return new EmptyResult(); + } /// /// 播放视频 @@ -144,7 +298,7 @@ namespace dy.net.Controllers return ApiResult.Fail($"视频地址无效"); } - return PlayViedo(viedo); + return await PlayVideoAsync(viedo); } catch (Exception ex) { @@ -155,16 +309,12 @@ namespace dy.net.Controllers /// /// 辅助方法:根据文件名获取 MIME 类型(确保前端正确识别视频格式) /// - private string GetContentType(string filename) + private static string GetContentType(string filename) { string extension = Path.GetExtension(filename).ToLowerInvariant(); return extension switch { ".mp4" => "video/mp4", - ".webm" => "video/webm", - ".ogg" => "video/ogg", - ".mov" => "video/quicktime", - ".avi" => "video/x-msvideo", _ => "application/octet-stream" // 默认二进制流 }; } @@ -277,7 +427,7 @@ namespace dy.net.Controllers //private async Task<(bool flowControl, IActionResult value)> BatchDeleteVideos(List videos) //{ - + // return (flowControl: true, value: null); //} @@ -287,7 +437,7 @@ namespace dy.net.Controllers /// /// [HttpGet("top{top}")] - public async Task GetLastSyncTop([FromRoute]int top = 5) + public async Task GetLastSyncTop([FromRoute] int top = 5) { return ApiResult.Success(await douyinVideoService.GetLastSyncTop(top)); } @@ -310,7 +460,7 @@ namespace dy.net.Controllers [HttpGet("renfo")] public async Task ReCreateNfo() { - var videos= await douyinVideoService.GetAllAsync(); + var videos = await douyinVideoService.GetAllAsync(); if (videos == null || videos.Count == 0) { return ApiResult.Success("暂无视频数据需要生成NFO文件"); @@ -330,7 +480,7 @@ namespace dy.net.Controllers } catch (Exception singleEx) { - Serilog.Log.Error($"刮削视频(Path:{video.VideoSavePath})生成NFO失败:{singleEx.Message}"); + Serilog.Log.Error($"刮削视频(Path:{video.VideoSavePath})生成NFO失败:{singleEx.Message}"); } } } @@ -342,5 +492,33 @@ namespace dy.net.Controllers return ApiResult.Success(); } + + + + /// + /// 获取7天视频同步趋势数据(曲线图) + /// + /// 7天图表数据列表 + [HttpGet("chart")] + public async Task Chart() + { + try + { + var chartData = await douyinVideoService.GetChartData(); + return ApiResult.Success(chartData); + } + catch (Exception ex) + { + return ApiResult.Fail(ex.Message); + } + } + + [HttpGet("/Move")] + public async Task Move() + { + await douyinVideoService.HandOldFolderVideos(); + + return ApiResult.Success(DateTime.Now); + } } } diff --git a/Program.cs b/Program.cs index bfab528..baf763a 100644 --- a/Program.cs +++ b/Program.cs @@ -220,12 +220,11 @@ namespace dy.net commonService.UpdateAllCookieSyncedToZero(); // 初始化配置 var config = commonService.InitConfig(); - if (!isDevelopment) + //if (!isDevelopment) { // 启动定时任务 var quartzJobService = services.GetRequiredService(); quartzJobService.InitOrReStartAllJobs(config?.Cron <= 0 ? "30" : config.Cron.ToString()); - DouyinHttpHelper.GetTenImage(Appsettings.Get("tagName"));//查询镜像版本 } // 初始化Cookie var deploy= Appsettings.Get("deploy"); diff --git a/Properties/PublishProfiles/docker.pubxml.user b/Properties/PublishProfiles/docker.pubxml.user index 8813048..7f6ff6d 100644 --- a/Properties/PublishProfiles/docker.pubxml.user +++ b/Properties/PublishProfiles/docker.pubxml.user @@ -2,8 +2,8 @@ - <_PublishTargetUrl>E:\gitea\dysync\bin\Release\net6.0\publish\ - True|2026-01-19T02:19:44.8997385Z||;True|2026-01-19T10:11:10.2305307+08:00||;True|2026-01-17T00:52:08.8180346+08:00||;False|2026-01-17T00:52:03.1829000+08:00||;True|2026-01-17T00:45:04.0498090+08:00||;True|2026-01-16T21:00:57.7239379+08:00||;True|2026-01-16T21:00:03.3912989+08:00||;True|2026-01-16T20:56:08.5505592+08:00||;True|2026-01-16T20:46:32.3067302+08:00||;True|2026-01-15T22:18:57.9835738+08:00||;True|2026-01-15T22:07:25.4753938+08:00||;True|2026-01-15T22:07:14.8243754+08:00||;True|2026-01-15T21:40:13.2613927+08:00||;True|2026-01-15T20:57:56.6291682+08:00||;True|2026-01-15T20:57:47.1363536+08:00||;True|2026-01-15T20:08:44.4710883+08:00||;True|2026-01-15T20:04:51.8284314+08:00||;False|2026-01-15T20:04:45.3258155+08:00||;True|2026-01-15T19:59:11.7907672+08:00||;True|2026-01-15T01:10:48.9066941+08:00||;True|2026-01-15T01:03:23.6216975+08:00||;True|2026-01-15T01:03:16.3364591+08:00||;False|2026-01-15T01:03:10.5654436+08:00||;True|2026-01-15T00:49:21.6559622+08:00||;True|2026-01-15T00:10:42.7288439+08:00||;True|2026-01-15T00:06:28.4241682+08:00||;True|2026-01-13T22:34:15.2372411+08:00||;True|2026-01-13T22:26:46.4522947+08:00||;True|2026-01-13T22:15:56.9735179+08:00||;True|2026-01-13T22:15:51.8250677+08:00||;False|2026-01-13T22:15:46.6608690+08:00||;True|2026-01-13T22:03:06.4683237+08:00||;False|2026-01-13T22:03:01.2036027+08:00||;True|2026-01-13T21:36:05.8776209+08:00||;True|2026-01-13T21:33:02.0766289+08:00||;False|2026-01-13T21:32:57.1976141+08:00||;True|2026-01-13T19:14:35.2519439+08:00||;True|2026-01-13T15:07:50.0836745+08:00||;False|2026-01-13T15:06:35.5929685+08:00||;True|2026-01-13T10:21:41.6012776+08:00||;True|2026-01-13T09:57:25.4788847+08:00||;True|2026-01-13T09:53:14.4900360+08:00||;True|2026-01-13T09:23:53.6465295+08:00||;True|2026-01-12T22:04:14.5886955+08:00||;False|2026-01-12T22:04:08.2008101+08:00||;True|2026-01-12T21:53:46.9947176+08:00||;True|2026-01-12T21:11:15.8358024+08:00||;True|2026-01-12T21:09:51.8663228+08:00||;True|2026-01-11T21:25:07.5052845+08:00||;False|2026-01-11T21:24:27.5744557+08:00||;True|2026-01-11T19:59:15.4734611+08:00||;False|2026-01-11T19:59:04.9543339+08:00||;True|2026-01-11T18:51:30.6649269+08:00||;True|2026-01-08T21:07:57.5094466+08:00||;True|2026-01-08T14:51:18.2266231+08:00||;True|2026-01-08T14:31:55.1137120+08:00||;True|2026-01-08T00:16:24.4451490+08:00||;True|2026-01-08T00:14:20.7430793+08:00||;True|2026-01-08T00:07:46.7228194+08:00||;True|2026-01-07T23:52:13.3290082+08:00||;True|2026-01-07T23:49:28.4838650+08:00||;True|2026-01-07T23:47:45.1936189+08:00||;True|2026-01-07T23:35:09.7818611+08:00||;True|2026-01-07T23:20:43.7462863+08:00||;True|2026-01-07T23:04:39.5140429+08:00||;False|2026-01-07T23:04:36.5367611+08:00||;True|2026-01-07T22:53:14.6013449+08:00||;True|2026-01-07T22:49:59.9549006+08:00||;True|2026-01-07T22:36:40.0254856+08:00||;False|2026-01-07T22:36:28.8275903+08:00||;True|2026-01-07T21:59:51.4355171+08:00||;True|2026-01-03T21:38:51.4307034+08:00||;True|2026-01-02T19:09:16.9668906+08:00||;False|2026-01-02T19:09:11.7496369+08:00||;True|2026-01-02T15:42:08.2215697+08:00||;True|2026-01-02T09:46:56.1654861+08:00||;True|2026-01-02T09:35:28.0211225+08:00||;True|2026-01-01T23:07:01.9022045+08:00||;False|2026-01-01T23:06:56.0537216+08:00||;True|2026-01-01T22:16:10.2974067+08:00||;True|2026-01-01T22:16:06.2123787+08:00||;False|2026-01-01T22:15:33.3626979+08:00||;True|2026-01-01T22:01:30.7161900+08:00||;True|2026-01-01T21:10:19.3664263+08:00||;True|2026-01-01T21:09:38.6071080+08:00||;True|2025-12-30T11:45:54.5543034+08:00||;True|2025-12-30T09:19:08.3178124+08:00||;True|2025-12-29T18:57:29.7032246+08:00||;True|2025-12-27T14:52:24.4780776+08:00||;False|2025-12-27T14:52:19.5635794+08:00||;True|2025-12-27T14:48:01.6252748+08:00||;False|2025-12-27T14:47:55.7976192+08:00||;True|2025-12-27T14:38:23.9723838+08:00||;True|2025-12-27T13:00:29.8583858+08:00||;True|2025-12-26T22:18:42.4015637+08:00||;True|2025-12-26T22:10:58.5274572+08:00||;True|2025-12-26T22:06:26.3129600+08:00||;True|2025-12-26T22:03:55.6718618+08:00||;False|2025-12-26T22:03:48.3809954+08:00||;True|2025-12-26T22:02:33.1840390+08:00||; + <_PublishTargetUrl>E:\code\dysync\bin\Release\net6.0\publish\ + True|2026-01-21T17:37:53.6972395Z||;True|2026-01-22T01:34:14.2142463+08:00||;True|2026-01-22T01:32:50.9228911+08:00||;True|2026-01-22T01:29:43.8871986+08:00||;True|2026-01-22T01:29:40.7838709+08:00||;True|2026-01-22T01:27:34.4763970+08:00||;True|2026-01-22T01:26:55.0117367+08:00||;True|2026-01-22T01:23:19.4789587+08:00||;True|2026-01-19T10:19:44.8997385+08:00||;True|2026-01-19T10:11:10.2305307+08:00||;True|2026-01-17T00:52:08.8180346+08:00||;False|2026-01-17T00:52:03.1829000+08:00||;True|2026-01-17T00:45:04.0498090+08:00||;True|2026-01-16T21:00:57.7239379+08:00||;True|2026-01-16T21:00:03.3912989+08:00||;True|2026-01-16T20:56:08.5505592+08:00||;True|2026-01-16T20:46:32.3067302+08:00||;True|2026-01-15T22:18:57.9835738+08:00||;True|2026-01-15T22:07:25.4753938+08:00||;True|2026-01-15T22:07:14.8243754+08:00||;True|2026-01-15T21:40:13.2613927+08:00||;True|2026-01-15T20:57:56.6291682+08:00||;True|2026-01-15T20:57:47.1363536+08:00||;True|2026-01-15T20:08:44.4710883+08:00||;True|2026-01-15T20:04:51.8284314+08:00||;False|2026-01-15T20:04:45.3258155+08:00||;True|2026-01-15T19:59:11.7907672+08:00||;True|2026-01-15T01:10:48.9066941+08:00||;True|2026-01-15T01:03:23.6216975+08:00||;True|2026-01-15T01:03:16.3364591+08:00||;False|2026-01-15T01:03:10.5654436+08:00||;True|2026-01-15T00:49:21.6559622+08:00||;True|2026-01-15T00:10:42.7288439+08:00||;True|2026-01-15T00:06:28.4241682+08:00||;True|2026-01-13T22:34:15.2372411+08:00||;True|2026-01-13T22:26:46.4522947+08:00||;True|2026-01-13T22:15:56.9735179+08:00||;True|2026-01-13T22:15:51.8250677+08:00||;False|2026-01-13T22:15:46.6608690+08:00||;True|2026-01-13T22:03:06.4683237+08:00||;False|2026-01-13T22:03:01.2036027+08:00||;True|2026-01-13T21:36:05.8776209+08:00||;True|2026-01-13T21:33:02.0766289+08:00||;False|2026-01-13T21:32:57.1976141+08:00||;True|2026-01-13T19:14:35.2519439+08:00||;True|2026-01-13T15:07:50.0836745+08:00||;False|2026-01-13T15:06:35.5929685+08:00||;True|2026-01-13T10:21:41.6012776+08:00||;True|2026-01-13T09:57:25.4788847+08:00||;True|2026-01-13T09:53:14.4900360+08:00||;True|2026-01-13T09:23:53.6465295+08:00||;True|2026-01-12T22:04:14.5886955+08:00||;False|2026-01-12T22:04:08.2008101+08:00||;True|2026-01-12T21:53:46.9947176+08:00||;True|2026-01-12T21:11:15.8358024+08:00||;True|2026-01-12T21:09:51.8663228+08:00||;True|2026-01-11T21:25:07.5052845+08:00||;False|2026-01-11T21:24:27.5744557+08:00||;True|2026-01-11T19:59:15.4734611+08:00||;False|2026-01-11T19:59:04.9543339+08:00||;True|2026-01-11T18:51:30.6649269+08:00||;True|2026-01-08T21:07:57.5094466+08:00||;True|2026-01-08T14:51:18.2266231+08:00||;True|2026-01-08T14:31:55.1137120+08:00||;True|2026-01-08T00:16:24.4451490+08:00||;True|2026-01-08T00:14:20.7430793+08:00||;True|2026-01-08T00:07:46.7228194+08:00||;True|2026-01-07T23:52:13.3290082+08:00||;True|2026-01-07T23:49:28.4838650+08:00||;True|2026-01-07T23:47:45.1936189+08:00||;True|2026-01-07T23:35:09.7818611+08:00||;True|2026-01-07T23:20:43.7462863+08:00||;True|2026-01-07T23:04:39.5140429+08:00||;False|2026-01-07T23:04:36.5367611+08:00||;True|2026-01-07T22:53:14.6013449+08:00||;True|2026-01-07T22:49:59.9549006+08:00||;True|2026-01-07T22:36:40.0254856+08:00||;False|2026-01-07T22:36:28.8275903+08:00||;True|2026-01-07T21:59:51.4355171+08:00||;True|2026-01-03T21:38:51.4307034+08:00||;True|2026-01-02T19:09:16.9668906+08:00||;False|2026-01-02T19:09:11.7496369+08:00||;True|2026-01-02T15:42:08.2215697+08:00||;True|2026-01-02T09:46:56.1654861+08:00||;True|2026-01-02T09:35:28.0211225+08:00||;True|2026-01-01T23:07:01.9022045+08:00||;False|2026-01-01T23:06:56.0537216+08:00||;True|2026-01-01T22:16:10.2974067+08:00||;True|2026-01-01T22:16:06.2123787+08:00||;False|2026-01-01T22:15:33.3626979+08:00||;True|2026-01-01T22:01:30.7161900+08:00||;True|2026-01-01T21:10:19.3664263+08:00||;True|2026-01-01T21:09:38.6071080+08:00||;True|2025-12-30T11:45:54.5543034+08:00||;True|2025-12-30T09:19:08.3178124+08:00||;True|2025-12-29T18:57:29.7032246+08:00||;True|2025-12-27T14:52:24.4780776+08:00||;False|2025-12-27T14:52:19.5635794+08:00||;True|2025-12-27T14:48:01.6252748+08:00||;False|2025-12-27T14:47:55.7976192+08:00||; \ No newline at end of file diff --git a/README.md b/README.md index 0732fd2..b262994 100644 --- a/README.md +++ b/README.md @@ -36,25 +36,22 @@ Cookie 及 `sec_user_id` 是同步功能的核心,需严格按步骤获取, -### 1.1 提取抖音 Cookie +### 1.1 提取`Cookie`以及 `sec_uer_id` 1. 打开 **抖音网页版** (https://www.douyin.com/) 并登录目标账号; -2. 进入「我的收藏」页面,确保页面加载完成; -3. 按 `F12` 打开浏览器「开发者工具」,切换到「Network (网络)」标签; -4. 刷新页面,在搜索框中输入 `v1/web/aweme/listcollection` 筛选请求; -5. 点击任意一条筛选结果,在右侧「Headers (标头)」中找到 `Cookie` 字段,**完整复制整段内容**(不可删减字符)。 +2. 进入个人主页,按下F12进入开发者模式、并切换到 `网络`(也可能叫`network`). +3. 在筛选框中输入`/follow`. +4. 点击自己头像边上的 `关注` 按钮、会弹出你的关注列表,然后在右侧网络请求里面会出现多个请求,随便选一个. +4. 在请求的标签里面切换到`负载`(也可能叫`payload`) +5. 找到sec_user_id,复制值即可 +6. 在请求的标签里面切换到`标头`(也可能叫`Headers`) +7. 往下拉,直到出现`Cookie` ,复制完整的值,注意前后不要带换行符,很多人会多复制个换行符出来. -![获取Cookie步骤](docs/getcookies.png) +![cookie](docs/findcookie_1_2_3.png) +![cookie](docs/findcookie_4.png) +![cookie](docs/findcookie_5.png) +![cookie](docs/findcookie_6_7.png) -### 1.2 提取你的`sec_user_id` -- **个人 sec_user_id**(同步自己喜欢用): - 1.进入自己的抖音主页: -- 方式:按 `F12` 点击`Network` 或`网络` 筛选器里面填`web/user/following/list` 然后 查看请求的 `payload` 或`负载` 即可找到`sec_user_id`; - 2.进入抖音主页后随便点一个自己的作品,然后你的名字,进入目标博主主页; - 方式:直接复制地址栏中 `user/` 到 `?from_tab_name` 中间部分内容即是博主的 `sec_user_id`; - - ![获取Cookie步骤](docs/getmysecuid.png) - -### 1.3 提取 博主的`sec_user_id`以及博主的uid +### 1.2 提取 博主的`sec_user_id`以及博主的uid - **对于想下载博主视频,但是又不想关注博主,需要用到** - 1.进入博主主页,按`F12` 点击`Network` 或`网络` 筛选器里面填`/web/aweme/post` 然后切到`预览`或`preview` 展开 json数据结果 找到`aweme_list` 然后随便点开其中一个子项 即可找到`author_user_id` 这便是博主的uid 。后续在关注列表中,需要手动添加非关注博主同步视频时将会要用到。 ![获取Cookie步骤](docs/getuperuid.png) @@ -96,7 +93,7 @@ Cookie 及 `sec_user_id` 是同步功能的核心,需严格按步骤获取, | 镜像标签 | 架构 | | ----------------- | -------------- | -| `beta_1.9.9` | x86_64 (amd64) | +| `beta_2.0.0` | x86_64 (amd64) | | `arm_1.9.8` | ARM64 | @@ -114,7 +111,7 @@ docker run -d --restart=always \ -v /opt/dysync/uper:/app/uper \ -p 10101:10101 \ --name dysync2026 \ - ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_1.9.9 + ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_2.0.0 ``` @@ -129,7 +126,7 @@ version: '3.8' services: dysync: - image: ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_1.9.9 + image: ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_2.0.0 container_name: dysync2026 # 容器名称 restart: unless-stopped # 始终重启容器,除非容器被手动停止或Docker服务停止 ports: diff --git a/app/src/pages/cok/CookieTable.vue b/app/src/pages/cok/CookieTable.vue index 9919934..b8a4177 100644 --- a/app/src/pages/cok/CookieTable.vue +++ b/app/src/pages/cok/CookieTable.vue @@ -29,7 +29,7 @@ columns.value = [ { title: '收藏路径', dataIndex: 'savePath' }, { title: '喜欢路径', dataIndex: 'favSavePath' }, { title: '博主路径', dataIndex: 'upSavePath' }, - { title: '图文视频', dataIndex: 'imgSavePath' }, + // { title: '图文视频', dataIndex: 'imgSavePath' }, // { title: 'Cookie', dataIndex: 'cookies' }, { title: '状态', dataIndex: 'status', width: 180 }, { title: '操作', dataIndex: 'edit', width: 200 }, // 加宽操作列宽度 @@ -54,11 +54,13 @@ type DataItem = { upSecUserIdsJson?: UpSecUserIdItem[]; upSecUserIds?: string; upSavePath?: string; - imgSavePath?: string; + // imgSavePath?: string; useSinglePath?: boolean; // 新增:是否全部用一个地址 useCollectFolder?: boolean; downMix?: boolean; downSeries?: boolean; + // mixPath?: string; + // seriesPath?: string; }; const loading = ref(false); @@ -117,11 +119,13 @@ const newCookie = (cookie?: DataItem) => { cookie.id = '0'; cookie.upSecUserIdsJson = undefined; cookie.upSavePath = undefined; - cookie.imgSavePath = undefined; + // cookie.imgSavePath = undefined; cookie.useSinglePath = false; // 新增:默认不使用单一路径 cookie.useCollectFolder = false; //是否按收藏夹来下载。 cookie.downMix = false; //是否下载收藏夹的合集 cookie.downSeries = false; //是否下载短剧 + // cookie.mixPath = undefined; //合集存储路径 + // cookie.seriesPath = undefined; //短剧存储路径 return cookie; }; @@ -141,7 +145,7 @@ watch( if (useSinglePath && newSavePath) { form.favSavePath = newSavePath; form.upSavePath = newSavePath; - form.imgSavePath = newSavePath; + // form.imgSavePath = newSavePath; } }, { immediate: true } @@ -220,6 +224,7 @@ const deleted = (id: string) => { }; function edit(record: DataItem) { + cookieId.value = record.id; editRecord.value = record; console.log(record); copyObject(form, record); @@ -316,6 +321,10 @@ onMounted(() => { const showDrawer = ref(false); // 抽屉类型(区分收藏夹/合集/短剧) type DrawerType = 'collect' | 'mix' | 'series'; + +const cookieId = ref(''); +const cateType = ref(5); + const drawerType = ref('collect'); // 抽屉滚动容器引用(用于监听触底分页) const drawerScrollRef = ref(null); @@ -332,28 +341,32 @@ const drawerPagination = reactive({ // ========== 定义抽屉数据项接口 ========== interface DrawerItem { - Id: string; // 不显示 - Name: string; // 名称 - SaveFolder: string; // 保存文件夹 - Sync: boolean; // 是否同步 - CoverUrl: string; // 封面 - CookieId: string; // 不显示 - XId: string; // 不显示 + id: string; // 不显示 + name: string; // 名称 + saveFolder: string; // 保存文件夹 + sync: boolean; // 是否同步 + coverUrl: string; // 封面 + cookieId: string; // 不显示 + xId: string; // 不显示 + total: number; } // ========== 3个打开抽屉的方法 ========== const openCollectFolderSetModal = () => { drawerType.value = 'collect'; + cateType.value = 5; openCommonDrawer(); }; const openMixDownSetModal = () => { drawerType.value = 'mix'; + cateType.value = 6; openCommonDrawer(); }; const openSeriesDownSetModal = () => { drawerType.value = 'series'; + cateType.value = 7; openCommonDrawer(); }; @@ -401,36 +414,32 @@ const handleDrawerScroll = () => { } }; -// ========== 加载抽屉数据(模拟接口请求,可替换为真实接口) ========== +// ========== 加载抽屉数据( const loadDrawerData = () => { if (drawerPagination.loading || !drawerPagination.hasMore) return; drawerPagination.loading = true; - - // 模拟接口请求(实际项目中替换为真实API调用) - setTimeout(() => { - // 模拟数据(可根据drawerType返回不同类型数据) - const mockData: DrawerItem[] = Array.from({ length: drawerPagination.pageSize }, (_, index) => ({ - Id: `${drawerPagination.current}-${index}`, - Name: `${getDrawerTypeName()} ${(drawerPagination.current - 1) * drawerPagination.pageSize + index + 1}`, - SaveFolder: `./${drawerType.value}/${(drawerPagination.current - 1) * drawerPagination.pageSize + index + 1}`, - Sync: Math.random() > 0.5, - CoverUrl: `https://picsum.photos/120/180?random=${ - // 改为竖向图片尺寸 120x180 - (drawerPagination.current - 1) * drawerPagination.pageSize + index + 1 - }`, - CookieId: form.id || '0', - XId: `X-${drawerPagination.current}-${index}`, - })); - - // 拼接数据列表 - drawerDataList.value = [...drawerDataList.value, ...mockData]; - // 更新分页状态 - drawerPagination.total = 100; // 模拟总条数 - drawerPagination.loading = false; - // 判断是否还有更多数据 - drawerPagination.hasMore = drawerDataList.value.length < drawerPagination.total; - }, 800); + useApiStore() + .CatePageList({ + cookieId: cookieId.value, + cateType: cateType.value, + }) + .then((res) => { + if (res.code === 0) { + drawerDataList.value = [...drawerDataList.value, ...res.data.data]; + drawerPagination.loading = false; + // 更新分页状态 + drawerPagination.total = res.data.total; + drawerPagination.loading = false; + // 判断是否还有更多数据 + drawerPagination.hasMore = drawerDataList.value.length < drawerPagination.total; + } else { + message.error(res.message); + } + }) + .finally(() => { + drawerPagination.loading = false; + }); }; // ========== 获取抽屉类型名称(用于页面展示) ========== @@ -443,15 +452,15 @@ const getDrawerTypeName = () => { case 'series': return '短剧'; default: - return '数据'; + return ''; } }; // ========== 切换同步状态(可根据需求对接真实接口) ========== const toggleDrawerItemSync = (item: DrawerItem, index: number) => { if (drawerDataList.value[index]) { - drawerDataList.value[index].Sync = !item.Sync; - console.log(`切换${getDrawerTypeName()}【${item.Name}】的同步状态为:${!item.Sync}`); + drawerDataList.value[index].sync = !item.sync; + console.log(`切换${getDrawerTypeName()}【${item.name}】的同步状态为:${!item.sync}`); } }; // ========== 关闭抽屉清理资源 ========== @@ -470,12 +479,20 @@ const saveDrawerData = () => { message.info('暂无需要保存的配置数据'); return; } - // 模拟保存逻辑(实际项目中可替换为真实接口,提交 drawerDataList.value 数据) drawerPagination.loading = true; - setTimeout(() => { - drawerPagination.loading = false; - message.success(`${getDrawerTypeName()}配置保存成功`); - }, 800); + useApiStore() + .BatchSaveCate(drawerDataList.value) + .then((res) => { + if (res.code === 0) { + showDrawer.value = false; + message.success('保存成功'); + } else { + message.error(res.message); + } + }) + .finally(() => { + drawerPagination.loading = false; + }); }; @@ -504,14 +521,14 @@ const saveDrawerData = () => {
- +
-
+
{{form.useSinglePath ?'是':'否'}}
@@ -519,11 +536,28 @@ const saveDrawerData = () => {
+ + + +
+ + +
+
+ + + +
+ + +
+
-
+
{{ form.useCollectFolder ? '是' : '否' }} + @@ -535,9 +569,11 @@ const saveDrawerData = () => {
-
+
{{ form.downMix ? '是' : '否' }} + +
@@ -548,9 +584,11 @@ const saveDrawerData = () => {
-
+
{{ form.downSeries ? '是' : '否' }} + +
@@ -559,29 +597,13 @@ const saveDrawerData = () => {
- - -
- - -
-
- - - -
- - -
-
- - + @@ -614,35 +636,39 @@ const saveDrawerData = () => { - +
- +
- {{ item.Name || '未命名' }} + {{ item.name || '未命名' }}
- - + + +
+
+ + {{ item.total || '0' }}
- - + +
- 已加载全部{{ getDrawerTypeName() }}数据 +
@@ -756,6 +782,13 @@ const saveDrawerData = () => { .cookie-content::-webkit-scrollbar-corner { background: transparent; } +.form-item-div { + width: 300px; +} +.form-item-div-input { + width: 150px; + margin-left: 10px; +} /* Firefox 透明滚动条适配 */ .cookie-content { scrollbar-width: thin; diff --git a/app/src/pages/desk/index.vue b/app/src/pages/desk/index.vue index 5fe7600..4e41d5b 100644 --- a/app/src/pages/desk/index.vue +++ b/app/src/pages/desk/index.vue @@ -22,7 +22,7 @@ type ConfigItem = { secUserId: string; status: number; upSavePath: string; - imgSavePath: string; + // imgSavePath: string; useSinglePath: boolean; // 非可选 }; @@ -37,7 +37,7 @@ const newConfig = (config?: ConfigItem): ConfigItem => { secUserId: '', status: 0, upSavePath: '', - imgSavePath: '', + // imgSavePath: '', useSinglePath: false, }; }; @@ -61,7 +61,7 @@ watch( if (useSinglePath && newSavePath) { form.value.favSavePath = newSavePath; form.value.upSavePath = newSavePath; - form.value.imgSavePath = newSavePath; + // form.value.imgSavePath = newSavePath; } }, { immediate: true } @@ -216,9 +216,9 @@ const manualCheckForm = (): { pass: boolean; msg: string } => {
- + diff --git a/app/src/pages/set/AppSet.vue b/app/src/pages/set/AppSet.vue index a7a4d8f..899a6f8 100644 --- a/app/src/pages/set/AppSet.vue +++ b/app/src/pages/set/AppSet.vue @@ -88,7 +88,7 @@ 开启后,将图片文件和音频文件合成为视频文件
- +
@@ -304,7 +304,7 @@ interface FormState { LogKeepDay: number; DownImage: boolean; DownMp3: boolean; - ImageViedoSaveAlone: boolean; + //ImageViedoSaveAlone: boolean; FollowedTitleTemplate: string[]; FollowedTitleSeparator: string; FullFollowedTitleTemplate: string; @@ -327,7 +327,7 @@ const formState: UnwrapRef = reactive({ DownImageVideo: false, DownMp3: false, DownImage: false, - ImageViedoSaveAlone: true, + // ImageViedoSaveAlone: true, FollowedTitleTemplate: [], FollowedTitleSeparator: '', FullFollowedTitleTemplate: '', @@ -403,7 +403,7 @@ const getConfig = () => { FollowedTitleTemplate: parsedTemplateArr, FollowedTitleSeparator: res.data.followedTitleSeparator || '', FullFollowedTitleTemplate: fullTemplate, - ImageViedoSaveAlone: res.data.imageViedoSaveAlone, + // ImageViedoSaveAlone: res.data.imageViedoSaveAlone, AutoDistinct: res.data.autoDistinct, PriorityLevel: res.data.priorityLevel, DownDynamicVideo: res.data.downDynamicVideo, diff --git a/app/src/pages/workplace/RecordTable.vue b/app/src/pages/workplace/RecordTable.vue index f064df0..8cd24b0 100644 --- a/app/src/pages/workplace/RecordTable.vue +++ b/app/src/pages/workplace/RecordTable.vue @@ -144,7 +144,7 @@ - +