210
This commit is contained in:
@@ -82,9 +82,9 @@ namespace dy.net.Controllers
|
||||
return ApiResult.Fail("请输入有效文件夹名称(字母数字中文简体)");
|
||||
}
|
||||
|
||||
if (dto.SavePath.Length > 15)
|
||||
if (dto.SavePath.Length > 20)
|
||||
{
|
||||
return ApiResult.Fail("请输入有效文件夹名称(最长15)");
|
||||
return ApiResult.Fail("请输入有效文件夹名称(最长20)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +218,8 @@ namespace dy.net
|
||||
// 重置博主作品同步状态为未同步
|
||||
//commonService.UpdateAllCookieSyncedToZero();
|
||||
|
||||
await commonService.UpdateFollowedSavePathAsync();
|
||||
|
||||
// 初始化Cookie
|
||||
var cookieService = services.GetRequiredService<DouyinCookieService>();
|
||||
var deploy = Appsettings.Get("deploy");
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
<SelfContained>false</SelfContained>
|
||||
<!--<DockerArch>x86</DockerArch>-->
|
||||
<!--<DockerArch>arm</DockerArch> -->
|
||||
<!--<DockerCoreVersion>2.0.9</DockerCoreVersion>-->
|
||||
<!--<DockerCoreVersion>2.1.0</DockerCoreVersion>-->
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -3,7 +3,7 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<_PublishTargetUrl>E:\code\dysync\bin\Release\net6.0\publish\</_PublishTargetUrl>
|
||||
<History>True|2026-02-01T13:34:30.5989260Z||;True|2026-02-01T21:30:20.7556066+08:00||;True|2026-01-30T21:56:47.1840381+08:00||;True|2026-01-28T22:01:00.8412391+08:00||;False|2026-01-28T21:49:29.1242233+08:00||;True|2026-01-28T21:48:26.1707204+08:00||;True|2026-01-28T21:48:05.8291762+08:00||;False|2026-01-28T21:48:01.1596391+08:00||;True|2026-01-28T21:37:59.4831763+08:00||;False|2026-01-28T21:37:53.5855464+08:00||;True|2026-01-28T21:27:51.1872828+08:00||;True|2026-01-28T13:01:14.1677037+08:00||;True|2026-01-28T12:36:45.7819776+08:00||;True|2026-01-28T12:09:15.6540204+08:00||;True|2026-01-28T12:08:40.0490617+08:00||;True|2026-01-28T11:58:55.4078870+08:00||;True|2026-01-27T22:37:03.3304942+08:00||;True|2026-01-27T22:05:40.4316281+08:00||;True|2026-01-26T22:30:43.5083924+08:00||;False|2026-01-26T22:30:33.7355213+08:00||;True|2026-01-26T22:26:26.0218548+08:00||;True|2026-01-26T22:21:32.3171217+08:00||;True|2026-01-26T22:20:40.3916780+08:00||;True|2026-01-26T22:16:19.1017273+08:00||;True|2026-01-26T22:13:34.2028883+08:00||;True|2026-01-26T21:46:23.7117812+08:00||;True|2026-01-26T21:41:19.6963033+08:00||;True|2026-01-25T21:27:13.9893777+08:00||;True|2026-01-24T21:45:22.8493624+08:00||;True|2026-01-24T21:31:35.4635265+08:00||;True|2026-01-24T21:27:31.3098506+08:00||;True|2026-01-24T15:47:01.6934638+08:00||;True|2026-01-24T15:32:35.6316009+08:00||;True|2026-01-24T15:16:28.7216639+08:00||;True|2026-01-23T22:14:26.8066324+08:00||;True|2026-01-23T13:36:48.4086406+08:00||;True|2026-01-23T13:30:27.2924204+08:00||;True|2026-01-23T13:02:53.3219368+08:00||;True|2026-01-23T13:01:54.3466638+08:00||;True|2026-01-23T13:01:28.8560809+08:00||;True|2026-01-22T23:18:32.0400436+08:00||;True|2026-01-22T22:29:29.5746209+08:00||;True|2026-01-22T21:32:21.7284081+08:00||;True|2026-01-22T21:20:26.5985955+08:00||;False|2026-01-22T21:20:21.1372539+08:00||;True|2026-01-22T21:11:18.3771739+08:00||;True|2026-01-22T20:58:55.9862778+08:00||;True|2026-01-22T20:51:48.6699461+08:00||;False|2026-01-22T20:51:42.2079210+08:00||;True|2026-01-22T20:50:34.7280114+08:00||;True|2026-01-22T20:50:11.1588492+08:00||;True|2026-01-22T16:11:14.0626037+08:00||;True|2026-01-22T15:53:16.6377347+08:00||;True|2026-01-22T15:45:34.0935091+08:00||;True|2026-01-22T12:44:28.1577014+08:00||;True|2026-01-22T12:40:45.7449335+08:00||;True|2026-01-22T12:39:07.0683214+08:00||;True|2026-01-22T09:55:49.7086190+08:00||;True|2026-01-22T01:37:53.6972395+08:00||;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||;</History>
|
||||
<History>True|2026-02-02T15:24:09.9134119Z||;True|2026-02-02T23:21:52.3235946+08:00||;False|2026-02-02T23:21:47.1386702+08:00||;True|2026-02-02T23:20:46.6277206+08:00||;False|2026-02-02T23:20:40.3158947+08:00||;True|2026-02-02T22:55:39.4268475+08:00||;True|2026-02-01T21:34:30.5989260+08:00||;True|2026-02-01T21:30:20.7556066+08:00||;True|2026-01-30T21:56:47.1840381+08:00||;True|2026-01-28T22:01:00.8412391+08:00||;False|2026-01-28T21:49:29.1242233+08:00||;True|2026-01-28T21:48:26.1707204+08:00||;True|2026-01-28T21:48:05.8291762+08:00||;False|2026-01-28T21:48:01.1596391+08:00||;True|2026-01-28T21:37:59.4831763+08:00||;False|2026-01-28T21:37:53.5855464+08:00||;True|2026-01-28T21:27:51.1872828+08:00||;True|2026-01-28T13:01:14.1677037+08:00||;True|2026-01-28T12:36:45.7819776+08:00||;True|2026-01-28T12:09:15.6540204+08:00||;True|2026-01-28T12:08:40.0490617+08:00||;True|2026-01-28T11:58:55.4078870+08:00||;True|2026-01-27T22:37:03.3304942+08:00||;True|2026-01-27T22:05:40.4316281+08:00||;True|2026-01-26T22:30:43.5083924+08:00||;False|2026-01-26T22:30:33.7355213+08:00||;True|2026-01-26T22:26:26.0218548+08:00||;True|2026-01-26T22:21:32.3171217+08:00||;True|2026-01-26T22:20:40.3916780+08:00||;True|2026-01-26T22:16:19.1017273+08:00||;True|2026-01-26T22:13:34.2028883+08:00||;True|2026-01-26T21:46:23.7117812+08:00||;True|2026-01-26T21:41:19.6963033+08:00||;True|2026-01-25T21:27:13.9893777+08:00||;True|2026-01-24T21:45:22.8493624+08:00||;True|2026-01-24T21:31:35.4635265+08:00||;True|2026-01-24T21:27:31.3098506+08:00||;True|2026-01-24T15:47:01.6934638+08:00||;True|2026-01-24T15:32:35.6316009+08:00||;True|2026-01-24T15:16:28.7216639+08:00||;True|2026-01-23T22:14:26.8066324+08:00||;True|2026-01-23T13:36:48.4086406+08:00||;True|2026-01-23T13:30:27.2924204+08:00||;True|2026-01-23T13:02:53.3219368+08:00||;True|2026-01-23T13:01:54.3466638+08:00||;True|2026-01-23T13:01:28.8560809+08:00||;True|2026-01-22T23:18:32.0400436+08:00||;True|2026-01-22T22:29:29.5746209+08:00||;True|2026-01-22T21:32:21.7284081+08:00||;True|2026-01-22T21:20:26.5985955+08:00||;False|2026-01-22T21:20:21.1372539+08:00||;True|2026-01-22T21:11:18.3771739+08:00||;True|2026-01-22T20:58:55.9862778+08:00||;True|2026-01-22T20:51:48.6699461+08:00||;False|2026-01-22T20:51:42.2079210+08:00||;True|2026-01-22T20:50:34.7280114+08:00||;True|2026-01-22T20:50:11.1588492+08:00||;True|2026-01-22T16:11:14.0626037+08:00||;True|2026-01-22T15:53:16.6377347+08:00||;True|2026-01-22T15:45:34.0935091+08:00||;True|2026-01-22T12:44:28.1577014+08:00||;True|2026-01-22T12:40:45.7449335+08:00||;True|2026-01-22T12:39:07.0683214+08:00||;True|2026-01-22T09:55:49.7086190+08:00||;True|2026-01-22T01:37:53.6972395+08:00||;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||;</History>
|
||||
<LastFailureDetails />
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -95,8 +95,8 @@ Cookie 及 `sec_user_id` 是同步功能的核心,需严格按步骤获取,
|
||||
|
||||
| 镜像标签 | 架构 |
|
||||
| ----------------- | -------------- |
|
||||
| `beta_2.0.9` | x86_64 (amd64) |
|
||||
| `arm_2.0.9` | ARM64 |
|
||||
| `beta_2.1.0` | x86_64 (amd64) |
|
||||
| `arm_2.1.0` | ARM64 |
|
||||
|
||||
|
||||
### 构建命令示例
|
||||
@@ -113,7 +113,7 @@ Cookie 及 `sec_user_id` 是同步功能的核心,需严格按步骤获取,
|
||||
|
||||
services:
|
||||
dysync:
|
||||
image: ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_2.0.9
|
||||
image: ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_2.1.0
|
||||
container_name: dysync2026 # 容器名称
|
||||
restart: unless-stopped # 始终重启容器,除非容器被手动停止或Docker服务停止
|
||||
ports:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -128,7 +128,7 @@
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<!-- 核心配置:仅用核心版本号2.0.9,自动拼接前缀 -->
|
||||
<!-- 核心配置:仅用核心版本号2.1.0,自动拼接前缀 -->
|
||||
<!--<Target Name="DockerBuildAndPushAfterPublish" AfterTargets="Publish">
|
||||
<Exec Command="docker build -t jianzhichu/dysync.net:beta_$(DockerCoreVersion) . && docker tag jianzhichu/dysync.net:beta_$(DockerCoreVersion) ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_$(DockerCoreVersion) && docker push ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_$(DockerCoreVersion)"
|
||||
Condition="'$(DockerArch)' == 'x86'" />
|
||||
|
||||
@@ -337,7 +337,7 @@ namespace dy.net.extension
|
||||
services.AddHttpClient(DouyinRequestParamManager.DY_HTTP_CLIENT, client =>
|
||||
{
|
||||
client.DefaultRequestHeaders.UserAgent.ParseAdd(DouyinRequestParamManager.DY_USER_AGENT);
|
||||
client.BaseAddress = new Uri("https://www.douyin.com");
|
||||
client.BaseAddress = new Uri(DouyinRequestParamManager.DouyinHost);
|
||||
client.Timeout = TimeSpan.FromSeconds(60); // 设置请求超时,避免无限等待
|
||||
}).ConfigurePrimaryHttpMessageHandler(IgnoreSslHandlerFactory);
|
||||
|
||||
@@ -345,7 +345,7 @@ namespace dy.net.extension
|
||||
services.AddHttpClient(DouyinRequestParamManager.DY_HTTP_CLIENT_DOWN, client =>
|
||||
{
|
||||
client.DefaultRequestHeaders.UserAgent.ParseAdd(DouyinRequestParamManager.DY_USER_AGENT);
|
||||
client.DefaultRequestHeaders.Referrer = new Uri("https://www.douyin.com");
|
||||
client.DefaultRequestHeaders.Referrer = new Uri(DouyinRequestParamManager.DouyinHost);
|
||||
client.Timeout = TimeSpan.FromMinutes(5); // 下载超时设为5分钟,合理且不泄漏
|
||||
}).ConfigurePrimaryHttpMessageHandler(IgnoreSslHandlerFactory);
|
||||
}
|
||||
|
||||
+60
-47
@@ -627,17 +627,17 @@ namespace dy.net.job
|
||||
var mp3Url = item?.Music?.PlayUrl?.UrlList?.FirstOrDefault();
|
||||
|
||||
|
||||
var outPath = await douyinMergeVideoService.MergeMultipleVideosAsync(dynamicVideos, mp3Url, savePath, cookie.Cookies);
|
||||
if (!string.IsNullOrWhiteSpace(outPath.mp4Path))
|
||||
var (mp4Path, mp3Path) = await douyinMergeVideoService.MergeMultipleVideosAsync(dynamicVideos, mp3Url, savePath, cookie.Cookies);
|
||||
if (!string.IsNullOrWhiteSpace(mp4Path))
|
||||
{
|
||||
if (File.Exists(outPath.mp4Path))
|
||||
if (File.Exists(mp4Path))
|
||||
{
|
||||
dynamicVideo.VideoSavePath = outPath.mp4Path;
|
||||
dynamicVideo.VideoSavePath = mp4Path;
|
||||
if (!config.KeepDynamicVideo)
|
||||
{
|
||||
if (File.Exists(outPath.mp3Path))
|
||||
if (File.Exists(mp3Path))
|
||||
{
|
||||
File.Delete(outPath.mp3Path);
|
||||
File.Delete(mp3Path);
|
||||
}
|
||||
//不保留原视频-删除
|
||||
foreach (var opath in dynamicVideos)
|
||||
@@ -650,6 +650,8 @@ namespace dy.net.job
|
||||
}
|
||||
}
|
||||
}
|
||||
//动态视频生成nfo
|
||||
NfoFileGenerator.GenerateVideoNfoFile(dynamicVideo);
|
||||
videos.Add(dynamicVideo);
|
||||
syncCount++;
|
||||
if (syncCount + syncCount1 >= config.BatchCount)
|
||||
@@ -887,12 +889,12 @@ namespace dy.net.job
|
||||
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}][{item?.Author?.Nickname ?? ""}]-视频[{DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId)}]下载完成.");
|
||||
}
|
||||
// 下载视频封面
|
||||
await GetDownVideoCover(item, saveFolder, cookie, config, cate);
|
||||
var coverSavePath= await DownVideoCover(item, savePath, cookie, cate);
|
||||
// 下载作者头像
|
||||
var (avatarSavePath, avatarUrl) = await DownAuthorAvatar(cookie, item);
|
||||
var avatarSavePath = await DownAuthorAvatar(cookie, item);
|
||||
|
||||
// 创建视频实体
|
||||
return await CreateVideoEntity(config, cookie, item, v, savePath, saveFolder, avatarSavePath, null, cate);
|
||||
return await CreateVideoEntity(config, cookie, item, v, savePath, coverSavePath, avatarSavePath, null, cate);
|
||||
}
|
||||
|
||||
private static VideoBitRate GetBestMatchedVideoUrl(Aweme item, AppConfig config)
|
||||
@@ -968,14 +970,14 @@ namespace dy.net.job
|
||||
dynamicSavePaths.Add(v);
|
||||
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}][{item?.Author?.Nickname ?? ""}]-动态视频[{dynamicSavePath}]-00{i},下载完成.");
|
||||
}
|
||||
await Task.Delay(_random.Next(1, 4) * 1000);
|
||||
await Task.Delay(_random.Next(2, 10) * 1000);
|
||||
}
|
||||
|
||||
|
||||
//// 下载视频封面
|
||||
//await DownVideoCover(item, saveFolder, cookie, config);
|
||||
//// 下载作者头像
|
||||
var (avatarSavePath, avatarUrl) = await DownAuthorAvatar(cookie, item);
|
||||
// 下载视频封面
|
||||
var coverSavePath = await DownVideoCover(item, savePath, cookie, cate);
|
||||
// 下载作者头像
|
||||
var avatarSavePath = await DownAuthorAvatar(cookie, item);
|
||||
|
||||
// 创建视频实体
|
||||
var virtualBitRate = new VideoBitRate
|
||||
@@ -987,7 +989,7 @@ namespace dy.net.job
|
||||
DataSize = DouyinFileUtils.GetTotalFileSize(dynamicSavePaths.Select(x => x.Path).ToList()) // 合成视频的文件大小
|
||||
}
|
||||
};
|
||||
return await CreateVideoEntity(config, cookie, item, virtualBitRate, dynamicSavePaths.FirstOrDefault()?.Path, saveFolder, avatarSavePath, dynamicSavePaths, cate);
|
||||
return await CreateVideoEntity(config, cookie, item, virtualBitRate, dynamicSavePaths.FirstOrDefault()?.Path, coverSavePath, avatarSavePath, dynamicSavePaths, cate);
|
||||
}
|
||||
|
||||
|
||||
@@ -1147,11 +1149,9 @@ namespace dy.net.job
|
||||
var coverUrl = cate is not null && cate.CateType != VideoTypeEnum.dy_custom_collect
|
||||
? (item.MixInfo?.CoverUrl?.UrlList?.FirstOrDefault() ?? imageUrls.FirstOrDefault() ?? item.Music?.CoverHd?.UrlList?.FirstOrDefault())
|
||||
: imageUrls.FirstOrDefault();
|
||||
// 下载视频封面(使用第一张图片作为封面)
|
||||
if (!string.IsNullOrWhiteSpace(coverUrl))
|
||||
await DownVideoCover(coverUrl, fileNamefolder, cookie, item, config, cate);
|
||||
|
||||
// 下载作者头像
|
||||
var (avatarSavePath, avatarUrl) = await DownAuthorAvatar(cookie, item);
|
||||
var avatarSavePath = await DownAuthorAvatar(cookie, item);
|
||||
|
||||
// 为合成的视频创建一个“虚拟”的BitRate对象,以便复用CreateVideoEntity方法
|
||||
var virtualBitRate = new VideoBitRate
|
||||
@@ -1163,10 +1163,12 @@ namespace dy.net.job
|
||||
DataSize = string.IsNullOrWhiteSpace(savePath) || !File.Exists(savePath) ? 0 : new FileInfo(savePath).Length // 合成视频的文件大小
|
||||
}
|
||||
};
|
||||
|
||||
// 下载视频封面(使用第一张图片作为封面)
|
||||
string coverSavePath = await DownVideoCover(coverUrl, savePath, cookie);
|
||||
|
||||
// 创建视频实体
|
||||
var videoEntity = await CreateVideoEntity(config,
|
||||
cookie, item, virtualBitRate, savePath, fileNamefolder, avatarSavePath, null, cate);
|
||||
cookie, item, virtualBitRate, coverSavePath, fileNamefolder, avatarSavePath, null, cate);
|
||||
|
||||
// 特殊处理合成视频的字段
|
||||
videoEntity.FileHash = string.Empty; // 合成视频没有原始文件哈希
|
||||
@@ -1174,6 +1176,7 @@ namespace dy.net.job
|
||||
videoEntity.ViedoType = VideoType;
|
||||
videoEntity.IsMergeVideo = 1;// 标记为图片合成视频
|
||||
|
||||
|
||||
return videoEntity;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -1212,12 +1215,11 @@ namespace dy.net.job
|
||||
/// 从视频信息中提取封面URL并下载到指定文件夹
|
||||
/// </summary>
|
||||
/// <param name="item">视频信息</param>
|
||||
/// <param name="saveFolder">封面保存文件夹</param>
|
||||
/// <param name="savePath">视频保存路径</param>
|
||||
/// <param name="cookie">用户Cookie</param>
|
||||
/// <param name="config">应用配置</param>
|
||||
/// <param name="cate"></param>
|
||||
/// <returns>一个表示异步操作的任务</returns>
|
||||
protected async Task GetDownVideoCover(Aweme item, string saveFolder, DouyinCookie cookie, AppConfig config, DouyinCollectCate cate)
|
||||
protected async Task<string> DownVideoCover(Aweme item, string savePath, DouyinCookie cookie, DouyinCollectCate cate)
|
||||
{
|
||||
// 定义封面URL变量
|
||||
string coverUrl;
|
||||
@@ -1233,14 +1235,14 @@ namespace dy.net.job
|
||||
else
|
||||
{
|
||||
// cate为空时:只取Video封面
|
||||
coverUrl = item.Video.Cover.UrlList?.FirstOrDefault();
|
||||
coverUrl = item.Video?.Cover?.UrlList?.FirstOrDefault();
|
||||
if (string.IsNullOrWhiteSpace(coverUrl))
|
||||
{
|
||||
coverUrl = item.Images?.FirstOrDefault()?.DynamicVideo?.Cover?.UrlList?.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
// 如果封面URL为空,直接返回
|
||||
if (string.IsNullOrWhiteSpace(coverUrl)) return;
|
||||
|
||||
// 调用下载封面的方法
|
||||
await DownVideoCover(coverUrl, saveFolder, cookie, item, config, cate);
|
||||
return await DownVideoCover(coverUrl, savePath, cookie);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1250,12 +1252,12 @@ namespace dy.net.job
|
||||
/// <param name="cookie">用户Cookie</param>
|
||||
/// <param name="item">视频信息</param>
|
||||
/// <returns>一个元组,包含头像保存路径和头像URL</returns>
|
||||
protected async Task<(string savePath, string url)> DownAuthorAvatar(DouyinCookie cookie, Aweme item)
|
||||
protected async Task<string> DownAuthorAvatar(DouyinCookie cookie, Aweme item)
|
||||
{
|
||||
if (item.Author == null) return (null, null);
|
||||
if (item.Author == null) return string.Empty;
|
||||
// 优先获取高清头像
|
||||
var avatarUrl = item.Author.AvatarLarger?.UrlList?.FirstOrDefault() ?? item.Author.AvatarThumb?.UrlList?.FirstOrDefault();
|
||||
if (string.IsNullOrWhiteSpace(avatarUrl)) return (null, null);
|
||||
if (string.IsNullOrWhiteSpace(avatarUrl)) return string.Empty;
|
||||
|
||||
// 拼接头像保存路径
|
||||
var avatarSavePath = Path.Combine(GetAuthorAvatarBasePath(cookie), $"{item.Author.Uid}.jpg");
|
||||
@@ -1267,7 +1269,7 @@ namespace dy.net.job
|
||||
{
|
||||
await douyinHttpClientService.DownloadAsync(avatarUrl, avatarSavePath, cookie.Cookies);
|
||||
}
|
||||
return (avatarSavePath, avatarUrl);
|
||||
return avatarSavePath;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -1342,23 +1344,31 @@ namespace dy.net.job
|
||||
/// 根据指定的封面URL下载封面图片,并复制为fanart.jpg
|
||||
/// </summary>
|
||||
/// <param name="coverUrl">封面图片URL</param>
|
||||
/// <param name="saveFolder">封面保存文件夹</param>
|
||||
/// <param name="savePath">封面保存文件夹</param>
|
||||
/// <param name="cookie">用户Cookie</param>
|
||||
/// <param name="item">视频信息</param>
|
||||
/// <param name="config">应用配置</param>
|
||||
/// <param name="cate"></param>
|
||||
/// <returns>一个表示异步操作的任务</returns>
|
||||
private async Task DownVideoCover(string coverUrl, string saveFolder, DouyinCookie cookie, Aweme item, AppConfig config, DouyinCollectCate cate)
|
||||
private async Task<string> DownVideoCover(string coverUrl, string savePath, DouyinCookie cookie)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(coverUrl)) return;
|
||||
// 获取封面图片文件名
|
||||
var coverSavePath = Path.Combine(saveFolder, "poster.jpg");
|
||||
if (string.IsNullOrWhiteSpace(coverUrl)) return string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(savePath)) return string.Empty;
|
||||
|
||||
string directoryPath = Path.GetDirectoryName(savePath); // 获取文件所在目录,
|
||||
string newFileName = "poster.jpg";
|
||||
if (VideoType != VideoTypeEnum.dy_mix && VideoType != VideoTypeEnum.dy_series)
|
||||
{
|
||||
string fileNameWithoutExt = Path.GetFileNameWithoutExtension(savePath); // 获取无后缀的原文件名,
|
||||
newFileName = $"{fileNameWithoutExt}-poster.jpg"; // 拼接新文件名,
|
||||
}
|
||||
|
||||
var coverSavePath = Path.Combine(directoryPath, newFileName);
|
||||
|
||||
|
||||
// 如果封面文件不存在,则下载
|
||||
if (!File.Exists(coverSavePath))
|
||||
{
|
||||
await douyinHttpClientService.DownloadAsync(coverUrl, coverSavePath, cookie.Cookies);
|
||||
}
|
||||
return coverSavePath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1370,13 +1380,13 @@ namespace dy.net.job
|
||||
/// <param name="item">视频信息</param>
|
||||
/// <param name="bitRate">视频码率信息</param>
|
||||
/// <param name="savePath">视频保存路径</param>
|
||||
/// <param name="saveFolder">视频保存文件夹</param>
|
||||
/// <param name="coverSavePath">海报保存路径</param>
|
||||
/// <param name="avatorPath"></param>
|
||||
/// <param name="dynamicVideos">动态视频</param>
|
||||
/// <param name="cate">短剧、合集、自定义收藏夹</param>
|
||||
/// <returns>创建的视频实体对象</returns>
|
||||
private async Task<DouyinVideo> CreateVideoEntity(AppConfig config,
|
||||
DouyinCookie cookie, Aweme item, VideoBitRate bitRate, string savePath, string saveFolder, string avatorPath, List<DouyinDynamicVideoDto> dynamicVideos = null, DouyinCollectCate cate = null)
|
||||
DouyinCookie cookie, Aweme item, VideoBitRate bitRate, string savePath, string coverSavePath, string avatorPath, List<DouyinDynamicVideoDto> dynamicVideos = null, DouyinCollectCate cate = null)
|
||||
{
|
||||
// 获取视频标签
|
||||
var (tag1, tag2, tag3) = GetVideoTags(item);
|
||||
@@ -1401,7 +1411,7 @@ namespace dy.net.job
|
||||
VideoUrl = bitRate.PlayAddr.UrlList?.FirstOrDefault(),
|
||||
VideoCoverUrl = item.Video.Cover.UrlList?.FirstOrDefault(),
|
||||
VideoSavePath = savePath,
|
||||
VideoCoverSavePath = Path.Combine(saveFolder, "poster.jpg"),
|
||||
VideoCoverSavePath = coverSavePath,
|
||||
SyncTime = DateTime.Now,
|
||||
DyUserId = item.AuthorUserId == 0 ? item.Author?.Uid : item.AuthorUserId.ToString(),
|
||||
CookieId = cookie.Id,
|
||||
@@ -1417,8 +1427,11 @@ namespace dy.net.job
|
||||
{
|
||||
video.DynamicVideos = JsonConvert.SerializeObject(dynamicVideos);
|
||||
}
|
||||
// 生成NFO文件
|
||||
NfoFileGenerator.GenerateVideoNfoFile(video);
|
||||
else
|
||||
{
|
||||
// 生成NFO文件,动态视频 合成后重新生成,不在这里生成nfo
|
||||
NfoFileGenerator.GenerateVideoNfoFile(video);
|
||||
}
|
||||
return video;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using ClockSnowFlake;
|
||||
using dy.net.model.entity;
|
||||
using dy.net.utils;
|
||||
using SqlSugar;
|
||||
//using static Org.BouncyCastle.Math.EC.ECCurve;
|
||||
|
||||
@@ -189,6 +190,34 @@ namespace dy.net.service
|
||||
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 将所有关注博主的保存路径统一刷成博主姓名,防止博主名字改变后出现多个文件夹
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task UpdateFollowedSavePathAsync()
|
||||
{
|
||||
var existNoSavePath =await sqlSugarClient.Queryable<DouyinFollowed>().Where(x => string.IsNullOrWhiteSpace(x.SavePath)).AnyAsync();
|
||||
if (existNoSavePath)
|
||||
{
|
||||
var list = await sqlSugarClient.Queryable<DouyinFollowed>().ToListAsync();
|
||||
var noSavePathList = list.Where(x => string.IsNullOrWhiteSpace(x.SavePath));
|
||||
foreach (var item in noSavePathList)
|
||||
{
|
||||
var path = DouyinFileNameHelper.SanitizeLinuxFileName(item.UperName, "抖音号" + item.UperId, true);
|
||||
path = DouyinFileNameHelper.LimitUnifiedCount(path, 20);
|
||||
var existFollow = list.Where(x => x.UperId != item.UperId && x.SavePath == path).Any();
|
||||
if (existFollow)
|
||||
{
|
||||
item.SavePath = path + "_" + item.UperId;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.SavePath = path;
|
||||
}
|
||||
}
|
||||
await sqlSugarClient.Updateable(list).UpdateColumns(x => new { x.SavePath }).ExecuteCommandAsync();
|
||||
}
|
||||
}
|
||||
#region 测试创建数据库
|
||||
|
||||
///// <summary>
|
||||
|
||||
@@ -304,12 +304,12 @@ namespace dy.net.service
|
||||
if (File.Exists(video.path))
|
||||
{
|
||||
File.Delete(video.path); // 异步删除,提升并发性能
|
||||
Serilog.Log.Debug("视频文件删除成功:Path={0}", video);
|
||||
Serilog.Log.Debug("视频文件删除成功:Path={0}", video.path);
|
||||
|
||||
if (!video.onlyImgOrMp3)//如果是纯图片或纯音频文件,则不删除所在文件夹
|
||||
{
|
||||
//检查这个路径所在文件夹是否还有其他视频文件,如果没有则删除这个文件夹
|
||||
var dir = Path.GetDirectoryName(video.Item1);
|
||||
var dir = Path.GetDirectoryName(video.path);
|
||||
|
||||
bool hasMp4File = Directory.EnumerateFiles(dir, "*.mp4", SearchOption.TopDirectoryOnly).Any(); // 只要存在一个MP4文件就返回true;
|
||||
if (!hasMp4File)
|
||||
|
||||
@@ -60,6 +60,8 @@ namespace dy.net.utils
|
||||
result = KeepChineseLettersAndNumbers(result);
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(result))
|
||||
result = defaultName;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -119,5 +121,31 @@ namespace dy.net.utils
|
||||
var pattern = @"_\d+(?=\.)";
|
||||
return Regex.Replace(fileName, pattern, "");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 中英文混合截断:按视觉格子数限制(中文/全角符=2格,英文/半角符/数字=1格)
|
||||
/// </summary>
|
||||
/// <param name="inputStr">待处理字符串(路径/名称/备注都可)</param>
|
||||
/// <param name="maxVisualGrid">总视觉格子数(如20,核心参数)</param>
|
||||
/// <param name="addEllipsis">是否加省略号(UI展示=true,存储=false)</param>
|
||||
/// <returns>视觉长度合规的字符串</returns>
|
||||
public static string LimitUnifiedCount(this string inputStr, int maxTotalCount, bool addEllipsis = false)
|
||||
{
|
||||
// 边界防护:空字符串直接返回;传0/负数则按1处理,传20则正常取20(不影响核心需求)
|
||||
if (string.IsNullOrEmpty(inputStr)) return inputStr;
|
||||
int validMaxCount = Math.Max(maxTotalCount, 1);
|
||||
|
||||
// 总数未超,直接返回原字符串
|
||||
if (inputStr.Length <= validMaxCount) return inputStr;
|
||||
|
||||
// 总数超了,截取前N个(传20则截前20,纯中文能留20个)
|
||||
StringBuilder sb = new StringBuilder(inputStr.Substring(0, validMaxCount));
|
||||
// 截断后加省略号(UI展示用)
|
||||
if (addEllipsis) sb.Append("...");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ namespace dy.net.utils
|
||||
|
||||
string videoDirectory = Path.GetDirectoryName(video.VideoSavePath); // 视频所在目录
|
||||
string videoFileNameWithoutExt = Path.GetFileNameWithoutExtension(video.VideoSavePath); // 无扩展名的文件名
|
||||
var coverFileName = Path.GetFileName(video.VideoCoverSavePath);
|
||||
string nfoFullPath = Path.Combine(videoDirectory, $"{videoFileNameWithoutExt}.nfo"); // NFO文件完整路径
|
||||
//string postFullPath = Path.Combine(videoDirectory, "poster.jpg"); // NFO文件完整路径
|
||||
|
||||
@@ -64,9 +65,9 @@ namespace dy.net.utils
|
||||
}
|
||||
},
|
||||
Author = DouyinFileNameHelper.SanitizeLinuxFileName(video.Author, "", true),
|
||||
Poster = "poster.jpg",
|
||||
Poster = coverFileName,
|
||||
Title = video.VideoTitle,
|
||||
Thumbnail = "poster.jpg",// 使用poster作为缩略图
|
||||
Thumbnail = coverFileName,// 使用poster作为缩略图
|
||||
ReleaseDate = video.CreateTime,
|
||||
Genres = new List<string> { video.Tag1, video.Tag2, video.Tag3 }.Where(t => !string.IsNullOrWhiteSpace(t)).ToList()
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user