From e32d783de4f0b1c73a73c8c042f20900624a7590 Mon Sep 17 00:00:00 2001 From: jianzhichu Date: Mon, 12 Jan 2026 22:13:59 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81nfo=E6=96=87=E4=BB=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=EF=BC=8C=E4=BF=AE=E5=A4=8D=E6=BC=94=E5=91=98=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=EF=BC=88=E8=A7=86=E9=A2=91=E4=BD=9C=E8=80=85=EF=BC=89?= =?UTF-8?q?=202=E3=80=81=E5=9B=BE=E6=96=87=E8=A7=86=E9=A2=91=EF=BC=8C?= =?UTF-8?q?=E5=9B=A0=E7=89=88=E6=9D=83=E5=8E=9F=E5=9B=A0=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E7=9A=84=E9=9F=B3=E9=A2=91=EF=BC=8C=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E9=85=8D=E7=BD=AE=E6=96=B0=E5=A2=9E=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E9=9F=B3=E9=A2=91=E4=B8=8A=E4=BC=A0=E5=85=A5=E5=8F=A3=EF=BC=8C?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=88=90=E5=8A=9F=E5=90=8E=EF=BC=8C=E5=90=8E?= =?UTF-8?q?=E9=9D=A2=E5=9C=A8=E9=81=87=E5=88=B0=E6=97=A0=E6=B3=95=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD=E9=9F=B3=E9=A2=91=E6=97=B6=EF=BC=8C=E5=B0=86=E7=94=A8?= =?UTF-8?q?=E8=AF=A5=E9=9F=B3=E9=A2=91=E8=BF=9B=E8=A1=8C=E5=9B=BE=E6=96=87?= =?UTF-8?q?=E8=A7=86=E9=A2=91=E5=90=88=E6=88=90=E7=9A=84=E9=9F=B3=E9=A2=91?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=203=E3=80=81=E5=A2=9E=E5=8A=A0=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E8=A7=86=E9=A2=91=E5=90=88=E6=88=90=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E3=80=82=204=E3=80=81=E5=AE=B9=E5=99=A8=E9=87=8D=E5=90=AF?= =?UTF-8?q?=E5=90=8E=EF=BC=8C=E6=89=8B=E6=9C=BA=E7=AB=AF=E4=BC=9A=E8=BF=9E?= =?UTF-8?q?=E7=BB=AD=E8=B7=B3=E8=BD=AC=E7=99=BB=E5=BD=95=E9=A1=B5=E5=BE=88?= =?UTF-8?q?=E5=A4=9A=E6=AC=A1=E7=9A=84bug=E4=BF=AE=E5=A4=8D=205=E3=80=81?= =?UTF-8?q?=E5=85=B6=E4=BB=96=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Controllers/ConfigController.cs | 115 +++++ Controllers/LogsController.cs | 6 +- Controllers/VideoController.cs | 41 ++ Properties/PublishProfiles/docker.pubxml.user | 2 +- app/src/components/layout/FrontView.vue | 1 - app/src/pages/mobile/MobileDashboard.vue | 2 +- app/src/pages/mylogs/MyLogs.vue | 248 ++++++--- app/src/pages/set/AppSet.vue | 475 ++++++++++++++---- app/src/pages/workplace/RecordTable.vue | 98 +++- app/src/router/guards.ts | 130 ++++- app/src/store/coreapi.ts | 40 +- app/src/store/http.ts | 59 ++- app/src/utils/axiosHttp.ts | 55 +- job/DouyinBasicSyncJob.cs | 99 ++-- model/AppConfig.cs | 10 +- service/DouyinCommonService.cs | 6 +- service/DouyinMergeVideoService.cs | 165 +----- service/DouyinVideoService.cs | 8 +- utils/DouyinFileNameHelper.cs | 15 + utils/FFmpegHelper.cs | 108 +++- utils/NfoFileGenerator.cs | 86 +++- 21 files changed, 1286 insertions(+), 483 deletions(-) diff --git a/Controllers/ConfigController.cs b/Controllers/ConfigController.cs index 98908cb..e04a002 100644 --- a/Controllers/ConfigController.cs +++ b/Controllers/ConfigController.cs @@ -28,6 +28,18 @@ namespace dy.net.Controllers private readonly DouyinFollowService douyinFollowService; private readonly DouyinCookieService douyinCookieService; + // 定义允许上传的音频扩展名(小写) + private readonly string[] _allowedAudioExtensions = { ".mp3", ".wav" }; + + // 定义允许的音频 MIME 类型(增强验证) + private readonly string[] _allowedAudioMimeTypes = { + "audio/mpeg", "audio/wav" + }; + + // 最大文件大小:20MB(可根据需求调整) + private const long _maxFileSize = 20 * 1024 * 1024; + + public ConfigController(DouyinCookieService dyCookieService, DouyinCommonService commonService, DouyinQuartzJobService quartzJobService, DouyinFollowService douyinFollowService, DouyinCookieService douyinCookieService) { this.dyCookieService = dyCookieService; @@ -374,5 +386,108 @@ namespace dy.net.Controllers return ApiResult.Fail("请求失败"); } } + + + /// + /// 上传音频文件接口 + /// + /// 要上传的音频文件 + /// 上传结果 + [HttpPost("UploadAudio")] + public IActionResult UploadAudio(IFormFile file) + { + // 1. 验证文件是否为空 + if (file == null || file.Length == 0) + { + return BadRequest(new { success = false, message = "请选择要上传的音频文件" }); + } + + try + { + // 2. 验证文件大小 + if (file.Length > _maxFileSize) + { + return BadRequest(new { success = false, message = $"文件大小超过限制(最大允许 {_maxFileSize / 1024 / 1024}MB)" }); + } + + // 3. 获取文件扩展名并验证 + var fileExtension = Path.GetExtension(file.FileName).ToLower(); + if (!_allowedAudioExtensions.Contains(fileExtension)) + { + return BadRequest(new + { + success = false, + message = $"不支持的音频格式,仅允许:{string.Join(", ", _allowedAudioExtensions)}" + }); + } + + // 4. 验证 MIME 类型(可选但推荐,防止扩展名伪造) + var contentType = file.ContentType.ToLower(); + if (!_allowedAudioMimeTypes.Contains(contentType)) + { + return BadRequest(new + { + success = false, + message = "文件类型验证失败,请上传合法的音频文件" + }); + } + + //先删除 + var uploadMp3 = Directory.GetFiles(Path.Combine(AppContext.BaseDirectory, "mp3")) + .Where(filePath => Path.GetFileNameWithoutExtension(filePath) != "silent_10") + .FirstOrDefault(); + if (!string.IsNullOrWhiteSpace(uploadMp3) && System.IO.File.Exists(uploadMp3)) + { + System.IO.File.Delete(uploadMp3); + } + + + // 5. 生成唯一文件名(避免重复) + var uniqueFileName = $"dysync_default{fileExtension}"; + + // 6. 定义文件保存路径(建议配置在 appsettings.json 中,这里简化处理) + var uploadPath = Path.Combine(AppContext.BaseDirectory, "mp3"); + + // 确保目录存在 + if (!Directory.Exists(uploadPath)) + { + Directory.CreateDirectory(uploadPath); + } + + // 7. 保存文件 + var filePath = Path.Combine(uploadPath, uniqueFileName); + + if (System.IO.File.Exists(filePath)) + { + System.IO.File.Delete(filePath); + } + using (var stream = new FileStream(filePath, FileMode.Create)) + { + file.CopyTo(stream); + } + + // 8. 返回成功结果(可根据需求返回文件路径/URL 等) + return ApiResult.Success(new { fileName = uniqueFileName, filePath }); + } + catch (Exception ex) + { + // 捕获异常并返回错误信息 + return ApiResult.Fail(ex.Message); + } + } + [AllowAnonymous] + [HttpGet("defaudio")] + public async Task GetDefaultAudioUrl() + { + var uploadMp3 = Directory.GetFiles(Path.Combine(AppContext.BaseDirectory, "mp3")) + .Where(filePath => Path.GetFileNameWithoutExtension(filePath) != "silent_10") + .FirstOrDefault(); + if (!string.IsNullOrWhiteSpace(uploadMp3) && System.IO.File.Exists(uploadMp3)) + { + return File(System.IO.File.ReadAllBytes(uploadMp3), "application/octet-stream", Path.GetFileName(uploadMp3)); + } + return ApiResult.Fail("未上传音频"); + } + } } diff --git a/Controllers/LogsController.cs b/Controllers/LogsController.cs index e614d46..c9e56be 100644 --- a/Controllers/LogsController.cs +++ b/Controllers/LogsController.cs @@ -20,13 +20,13 @@ namespace dy.net.Controllers this.logInfoService = logInfoService; } - [HttpGet] - public async Task GetLog(string type, string date) + [HttpGet("/api/logs/GetLog/{type}/{date}")] + public async Task GetLog([FromRoute]string type, [FromRoute] string date) { var filePath = Path.Combine(webHostEnvironment.IsDevelopment() ? Directory.GetCurrentDirectory() : AppDomain.CurrentDomain.BaseDirectory, "logs", $"log-{type}-{date}.txt"); if (!System.IO.File.Exists(filePath)) { - var msg = $"Log file log-{type}-{date}.txt not found."; + var msg = $"{date},没有发现{type}的日志"; //Serilog.Log.Error(msg); return Ok(msg); } diff --git a/Controllers/VideoController.cs b/Controllers/VideoController.cs index 8d5760f..8b7166d 100644 --- a/Controllers/VideoController.cs +++ b/Controllers/VideoController.cs @@ -4,6 +4,7 @@ using dy.net.service; using dy.net.utils; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using System; namespace dy.net.Controllers { @@ -270,5 +271,45 @@ namespace dy.net.Controllers var data = await douyinVideoService.DeleteInvalidVideo(); return Ok(data); } + + /// + /// 所有视频重新生成nfo文件 + /// + /// + [HttpGet("renfo")] + public async Task ReCreateNfo() + { + var videos= await douyinVideoService.GetAllAsync(); + if (videos == null || videos.Count == 0) + { + return ApiResult.Success("暂无视频数据需要生成NFO文件"); + } + _ = Task.Run(async () => + { + try + { + var totalCount = videos.Count; + foreach (var video in videos) + { + try + { + NfoFileGenerator.GenerateVideoNfoFile(video); + Serilog.Log.Debug($"刮削视频(Path:{video.VideoSavePath})生成NFO成功!"); + await Task.Delay(50); + } + catch (Exception singleEx) + { + Serilog.Log.Error($"刮削视频(Path:{video.VideoSavePath})生成NFO失败:{singleEx.Message}"); + } + } + } + catch (Exception ex) + { + Serilog.Log.Error($"NFO文件生成任务执行异常:{ex.Message}\n{ex.StackTrace}"); + } + }); + + return ApiResult.Success(); + } } } diff --git a/Properties/PublishProfiles/docker.pubxml.user b/Properties/PublishProfiles/docker.pubxml.user index 564ba8e..b7e593f 100644 --- a/Properties/PublishProfiles/docker.pubxml.user +++ b/Properties/PublishProfiles/docker.pubxml.user @@ -3,7 +3,7 @@ <_PublishTargetUrl>E:\code\dysync\bin\Release\net6.0\publish\ - True|2026-01-11T13:25:07.5052845Z||;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||;True|2025-12-26T22:01:16.1660100+08:00||;True|2025-12-25T00:50:26.1116465+08:00||;True|2025-12-25T00:48:27.3087708+08:00||;True|2025-12-25T00:47:38.4835720+08:00||; + True|2026-01-12T14:04:14.5886955Z||;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||;True|2025-12-26T22:01:16.1660100+08:00||;True|2025-12-25T00:50:26.1116465+08:00||;True|2025-12-25T00:48:27.3087708+08:00||;True|2025-12-25T00:47:38.4835720+08:00||; \ No newline at end of file diff --git a/app/src/components/layout/FrontView.vue b/app/src/components/layout/FrontView.vue index dd373f8..6ef95e6 100644 --- a/app/src/components/layout/FrontView.vue +++ b/app/src/components/layout/FrontView.vue @@ -31,7 +31,6 @@ const isMobileBrowser = computed(() => { const enforceMobileRoute = () => { const targetMobilePath = '/mobile'; const currentPath = route.path.toLowerCase().trim(); - // 核心规则:手机浏览器 → 强制跳转到/mobile(无论当前路由是什么) if (isMobileBrowser.value) { if (currentPath !== targetMobilePath) { diff --git a/app/src/pages/mobile/MobileDashboard.vue b/app/src/pages/mobile/MobileDashboard.vue index 303fc66..9d38145 100644 --- a/app/src/pages/mobile/MobileDashboard.vue +++ b/app/src/pages/mobile/MobileDashboard.vue @@ -368,7 +368,7 @@ const loadLogDetailContent = (log: LogItem) => { try { const type = log.type.toLowerCase(); const date = log.date; - const requestParams = `type=${type}&date=${date}`; + const requestParams = `${type}/${date}`; useApiStore() .apiGetLogs(requestParams) diff --git a/app/src/pages/mylogs/MyLogs.vue b/app/src/pages/mylogs/MyLogs.vue index 8ef7b53..70120bd 100644 --- a/app/src/pages/mylogs/MyLogs.vue +++ b/app/src/pages/mylogs/MyLogs.vue @@ -1,92 +1,196 @@ + - + +// ========== 可选:日志类型按钮颜色也同步调整(可选) ========== +.type-btn { + :deep(&.ant-btn-primary) { + background: #722ed1 !important; // 日志类型选中用紫色(可改) + border-color: #722ed1 !important; + color: #fff !important; + + &:hover { + background: #8046e0 !important; + border-color: #8046e0 !important; + } + } +} + \ No newline at end of file diff --git a/app/src/pages/set/AppSet.vue b/app/src/pages/set/AppSet.vue index 214bed9..8b8876f 100644 --- a/app/src/pages/set/AppSet.vue +++ b/app/src/pages/set/AppSet.vue @@ -1,12 +1,11 @@ \ No newline at end of file diff --git a/app/src/pages/workplace/RecordTable.vue b/app/src/pages/workplace/RecordTable.vue index 5098e7e..6bac008 100644 --- a/app/src/pages/workplace/RecordTable.vue +++ b/app/src/pages/workplace/RecordTable.vue @@ -71,10 +71,13 @@