This commit is contained in:
jianzhichu
2026-01-26 22:42:05 +08:00
parent 85986cdaa3
commit edbcc13086
19 changed files with 438 additions and 330 deletions
+4 -1
View File
@@ -169,6 +169,8 @@ namespace dy.net.Controllers
if (!cookieValid)
return ApiResult.Fail("Cookie无效,请按照文档提示重新获取有效Cookie,不要使用插件获取cookie");
dyUserCookies.StatusCode = 0;
dyUserCookies.StatusMsg = "正常";
// 4. 保存到数据库
var saved = await dyCookieService.Add(dyUserCookies);
return saved ? ApiResult.Success() : ApiResult.Fail("添加失败");
@@ -229,7 +231,8 @@ namespace dy.net.Controllers
{
return ApiResult.Fail("Cookie无效,请按照文档提示重新获取有效Cookie,不要使用插件获取cookie");
}
dyUserCookies.StatusCode = 0;
dyUserCookies.StatusMsg = "正常";
if (dyUserCookies.Id == "0")
{
dyUserCookies.Id = IdGener.GetLong().ToString();
+5 -1
View File
@@ -103,7 +103,11 @@ namespace dy.net.Controllers
return await OpenOrCloseSync(dto);
}
/// <summary>
/// 删除关注对象
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("delete")]
public async Task<IActionResult> DeleteFollow(FollowUpdateDto dto)
{
+2 -1
View File
@@ -229,8 +229,9 @@ namespace dy.net
var config = commonService.InitConfig();
//Serilog.Log.Debug("isRestart1=" + config.IsFirstRunning);
await cookieService.UpdateCookieToSupportOldVersionAsync();
//if (!isDevelopment)
if (!isDevelopment)
{
var cookie = await cookieService.GetOpendCookies();
if (cookie != null && cookie.Any())
+1 -1
View File
@@ -17,6 +17,6 @@
<SelfContained>false</SelfContained>
<!--<DockerArch>x86</DockerArch>-->
<!--<DockerArch>arm</DockerArch> -->
<!--<DockerCoreVersion>2.0.5</DockerCoreVersion>-->
<!--<DockerCoreVersion>2.0.6</DockerCoreVersion>-->
</PropertyGroup>
</Project>
@@ -3,7 +3,7 @@
<Project>
<PropertyGroup>
<_PublishTargetUrl>E:\code\dysync\bin\Release\net6.0\publish\</_PublishTargetUrl>
<History>True|2026-01-25T13:27:13.9893777Z||;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||;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||;</History>
<History>True|2026-01-26T14:30:43.5083924Z||;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||;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||;</History>
<LastFailureDetails />
</PropertyGroup>
</Project>
+3 -3
View File
@@ -95,8 +95,8 @@ Cookie 及 `sec_user_id` 是同步功能的核心,需严格按步骤获取,
| 镜像标签 | 架构 |
| ----------------- | -------------- |
| `beta_2.0.5` | x86_64 (amd64) |
| `arm_2.0.5` | ARM64 |
| `beta_2.0.6` | x86_64 (amd64) |
| `arm_2.0.6` | ARM64 |
### 构建命令示例
@@ -115,7 +115,7 @@ version: '3.8'
services:
dysync:
image: ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_2.0.5
image: ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_2.0.6
container_name: dysync2026 # 容器名称
restart: unless-stopped # 始终重启容器,除非容器被手动停止或Docker服务停止
ports:
+104 -113
View File
@@ -55,12 +55,15 @@ type DataItem = {
upSecUserIds?: string;
upSavePath?: string;
// imgSavePath?: string;
useSinglePath?: boolean; // 新增:是否全部用一个地址
// useSinglePath?: boolean; // 新增:是否全部用一个地址
useCollectFolder?: boolean;
downMix?: boolean;
downSeries?: boolean;
mixPath?: string;
seriesPath?: string;
downCollect?: boolean;
downFavorite?: boolean;
downFollowd?: boolean;
};
const loading = ref(false);
@@ -120,12 +123,15 @@ const newCookie = (cookie?: DataItem) => {
cookie.upSecUserIdsJson = undefined;
cookie.upSavePath = undefined;
// cookie.imgSavePath = undefined;
cookie.useSinglePath = false; // 新增:默认不使用单一路径
// cookie.useSinglePath = false; // 新增:默认不使用单一路径
cookie.useCollectFolder = false; //是否按收藏夹来下载。
cookie.downMix = false; //是否下载收藏夹的合集
cookie.downSeries = false; //是否下载短剧
cookie.mixPath = undefined; //合集存储路径
cookie.seriesPath = undefined; //短剧存储路径
cookie.downCollect = false;
cookie.downFavorite = false;
cookie.downFollowd = false;
return cookie;
};
@@ -139,17 +145,17 @@ const copyObject = (target: any, source?: any) => {
const form = reactive<DataItem>(newCookie());
// 新增:监听收藏路径变化,当启用单一路径时同步到其他路径
watch(
[() => form.savePath, () => form.useSinglePath],
([newSavePath, useSinglePath]) => {
if (useSinglePath && newSavePath) {
form.favSavePath = newSavePath;
form.upSavePath = newSavePath;
// form.imgSavePath = newSavePath;
}
},
{ immediate: true }
);
// watch(
// [() => form.savePath, () => form.useSinglePath],
// ([newSavePath, useSinglePath]) => {
// // if (useSinglePath && newSavePath) {
// // form.favSavePath = newSavePath;
// // form.upSavePath = newSavePath;
// // // form.imgSavePath = newSavePath;
// // }
// },
// { immediate: true }
// );
function reset() {
return newCookie(form);
@@ -181,11 +187,11 @@ function submit() {
loading.value = false;
if (res.code === 0) {
showModal.value = false;
message.success('修改成功,同步任务将在5-10秒按新配置运行...');
message.success('保存成功,同步任务将在5-10秒后重新启动...');
reset();
GetRecords();
}else{
message.success('修改失败'+res.message);
} else {
message.error('保存失败' + res.message);
}
});
})
@@ -231,9 +237,9 @@ function edit(record: DataItem) {
console.log(record);
copyObject(form, record);
// 确保useSinglePath有默认值
if (form.useSinglePath === undefined) {
form.useSinglePath = false;
}
// if (form.useSinglePath === undefined) {
// form.useSinglePath = false;
// }
showModal.value = true;
}
@@ -311,7 +317,7 @@ const removeRow = (index: number) => {
form.upSecUserIdsJson.splice(index, 1);
}
};
const rowCount = 5;
const rowCount = 10;
// 组件挂载时获取配置
onMounted(() => {
@@ -496,6 +502,10 @@ const saveDrawerData = () => {
drawerPagination.loading = false;
});
};
const switchdownCollect = (e: any) => {
if (!e) form.useCollectFolder = e;
};
</script>
<template>
@@ -520,132 +530,113 @@ const saveDrawerData = () => {
</div>
</a-form-item>
<!-- 收藏的存储路径 -->
<a-form-item label="收藏的存储路径" name="savePath">
<div style="display: flex; align-items: center; gap: 12px; width: 100%;">
<a-input v-model:value="form.savePath" class="form-item-div" />
<a-alert message="不想同步收藏的视频就空着" type="info" size="small" style="flex: 1; margin-bottom: 0;" />
</div>
</a-form-item>
<!-- 新增是否全部用一个地址开关 -->
<a-form-item v-if="form.savePath&&form.savePath.length>0" label="统一存储路径" name="useSinglePath">
<!-- 修复下载收藏视频 - 拆分Form.Item用a-form-item-rest包裹非收集字段 -->
<a-form-item label="下载收藏视频" name="downCollect">
<div style="display: flex; align-items: center; gap: 12px;">
<div class="form-item-div">
<a-switch v-model:checked="form.useSinglePath" :checked-value="true" :un-checked-value="false" size="default" />
<span style="margin-left:10px;">{{form.useSinglePath ?'':''}}</span>
<a-switch v-model:checked="form.downCollect" @change="switchdownCollect" :checked-value="true" :un-checked-value="false" size="default" />
<!-- 非收集字段用a-form-item-rest包裹 -->
<a-form-item-rest v-if="form.downCollect">
<a-form-item name="savePath" noStyle>
<a-input v-model:value="form.savePath" placeholder='请输入容器路径' class="form-item-div-input" />
</a-form-item>
</a-form-item-rest>
</div>
<a-alert message="开启后,喜欢、关注、收藏都存储在一个目录,如果是容器部署:docker-compose配置,只需要映射一个路径 (合集短剧存储路径需要另外设置映射)" :type="form.useSinglePath?'error':'info'" size="small" style="flex: 1; margin-bottom: 0;" />
<a-alert message="开启后自动下载默认收藏夹视频,记得填写映射路径(容器内部路径)" type="info" size="small" style="flex: 1; margin-bottom: 0;" />
</div>
</a-form-item>
<a-form-item v-if="form.savePath&&form.savePath.length>0" label="下载自定义收藏夹" name="useCollectFolder">
<!-- 修复自定义收藏夹 - 拆分Form.Item -->
<a-form-item v-if="form.savePath && form.savePath.length>0" label="自定义收藏夹" name="useCollectFolder">
<div style="display: flex; align-items: center; gap: 12px;">
<div class="form-item-div">
<a-switch v-model:checked="form.useCollectFolder" :checked-value="true" :un-checked-value="false" size="default" />
<span style="margin-left:10px;">{{ form.useCollectFolder ? '' : '' }}</span>
<span>存储路径=收藏的存储路径</span>
<a-button @click="openCollectFolderSetModal" shape="circle" type="dashed" style="margin-left:10px;" v-if="form.useCollectFolder">
<tag-outlined />
<a-form-item-rest v-if="form.useCollectFolder">
<a-input v-model:value="form.savePath" :disabled="form.useCollectFolder&&form.downCollect" placeholder="" class="form-item-div-input" />
<a-button @click="openCollectFolderSetModal" shape="circle" type="dashed" style="margin-left:5px;" v-if="form.useCollectFolder">
<star-outlined />
</a-button>
</a-form-item-rest>
</div>
<a-alert message="开启后按照自定义收藏进行下载(需要开启收藏文件夹同步开关)且不再下载默认收藏夹视频" :type="form.useCollectFolder?'error':'info'" size="small" style="flex: 1; margin-bottom: 0;" />
</div>
</a-form-item>
<!-- 喜欢的存储路径 -->
<a-form-item label="喜欢的存储路径" name="favSavePath">
<div style="display: flex; align-items: center; gap: 12px; width: 100%;">
<a-input v-model:value="form.favSavePath" :disabled="form.useSinglePath" placeholder="" class="form-item-div" />
<a-alert message="不想同步喜欢的视频就空着" type="info" size="small" style="flex: 1; margin-bottom: 0;" />
<a-alert message="开启后自动下载自定义分类后的收藏夹,开启后不在下载默认收藏夹视频,存储路径与默认收藏夹存储路径一致" :type="form.useCollectFolder?'error':'info'" size="small" style="flex: 1; margin-bottom: 0;" />
</div>
</a-form-item>
<!-- 关注的存储路径 -->
<a-form-item label="关注的存储路径" name="upSavePath">
<div style="display: flex; align-items: center; gap: 12px; width: 100%;">
<a-input v-model:value="form.upSavePath" :disabled="form.useSinglePath" placeholder="" class="form-item-div" />
<a-alert message="不想同步关注列表博主的视频就空着" type="info" size="small" style="flex: 1; margin-bottom: 0;" />
<!-- 修复下载喜欢视频 - 拆分Form.Item -->
<a-form-item label="下载喜欢视频" name="downFavorite">
<div style="display: flex; align-items: center; gap: 12px;">
<div class="form-item-div">
<a-switch v-model:checked="form.downFavorite" :checked-value="true" :un-checked-value="false" size="default" />
<a-form-item-rest v-if="form.downFavorite">
<a-form-item name="favSavePath" noStyle>
<a-input v-model:value="form.favSavePath" placeholder='请输入容器路径' class="form-item-div-input" />
</a-form-item>
<a-button shape="circle" @click="()=>{message.success('别点了,这只是为了好看的😄')}" type="dashed" style="margin-left:5px;">
<like-outlined />
</a-button>
</a-form-item-rest>
</div>
<a-alert message="开启后自动下载喜欢(点赞)的视频,记得填写映射路径(容器内部路径)" type="info" size="small" style="flex: 1; margin-bottom: 0;" />
</div>
</a-form-item>
<!-- 下载合集 - 新增name="mixPath" -->
<a-form-item label="下载合集" name="downMix">
<!-- 修复下载关注视频 - 拆分Form.Item -->
<a-form-item label="下载关注视频" name="downFollowd">
<div style="display: flex; align-items: center; gap: 12px;">
<div class="form-item-div">
<a-switch v-model:checked="form.downFollowd" :checked-value="true" :un-checked-value="false" size="default" />
<a-form-item-rest v-if="form.downFollowd">
<a-form-item name="upSavePath" noStyle>
<a-input v-model:value="form.upSavePath" placeholder='请输入容器路径' class="form-item-div-input" />
</a-form-item>
<a-button shape="circle" @click="()=>{message.success('别点了,这只是为了好看的😄')}" type="dashed" style="margin-left:5px;">
<heart-outlined />
</a-button>
</a-form-item-rest>
</div>
<a-alert message="开启后自动下载关注的博主视频,记得填写映射路径(容器内部路径)" type="info" size="small" style="flex: 1; margin-bottom: 0;" />
</div>
</a-form-item>
<!-- 修复下载合集视频 - 拆分Form.Item -->
<a-form-item label="下载合集视频" name="downMix">
<div style="display: flex; align-items: center; gap: 12px;">
<div class="form-item-div">
<a-switch v-model:checked="form.downMix" :checked-value="true" :un-checked-value="false" size="default" />
<span style="margin-left:10px;">{{ form.downMix ? '' : '' }}</span>
<!-- 给输入框对应的form-item添加name属性 -->
<a-form-item name="mixPath" noStyle v-if="form.downMix">
<a-input v-model:value="form.mixPath" class="form-item-div-input" />
<a-form-item-rest v-if="form.downMix">
<a-form-item name="mixPath" noStyle>
<a-input v-model:value="form.mixPath" class="form-item-div-input" placeholder='默认使用收藏夹路径' />
</a-form-item>
<a-button @click="openMixDownSetModal" shape="circle" type="dashed" style="margin-left:10px;" v-if="form.downMix"> <tag-outlined />
<a-button @click="openMixDownSetModal" shape="circle" type="dashed" style="margin-left:5px;">
<gift-outlined />
</a-button>
</a-form-item-rest>
</div>
<a-alert message="开启后会同步下载收藏的合集(需要开启合集同步开关,不设置路径默认存储到收藏,设置后记得docker里面加映射)" :type="form.downMix?'error':'info'" size="small" style="flex: 1; margin-bottom: 0;" />
<a-alert message="开启后自动下载收藏的合集视频(还需要开启合集同步开关,不填目录径默认存储到收藏目录,设置后记得docker里面加映射,注意:付费视频下载后无法播放)" :type="form.downMix?'error':'info'" size="small" style="flex: 1; margin-bottom: 0;" />
</div>
</a-form-item>
<!-- 下载短剧 - 新增name="seriesPath" -->
<a-form-item label="下载短剧" name="downSeries">
<!-- 修复下载短剧视频 - 拆分Form.Item -->
<a-form-item label="下载短剧视频" name="downSeries">
<div style="display: flex; align-items: center; gap: 12px;">
<div class="form-item-div">
<a-switch v-model:checked="form.downSeries" :checked-value="true" :un-checked-value="false" size="default" />
<span style="margin-left:10px;">{{ form.downSeries ? '' : '' }}</span>
<!-- 给输入框对应的form-item添加name属性 -->
<a-form-item name="seriesPath" noStyle v-if="form.downSeries">
<a-input v-model:value="form.seriesPath" class="form-item-div-input" />
<a-form-item-rest v-if="form.downSeries">
<a-form-item name="seriesPath" noStyle>
<a-input v-model:value="form.seriesPath" placeholder='默认使用收藏夹路径' class="form-item-div-input" />
</a-form-item>
<a-button @click="openSeriesDownSetModal" shape="circle" type="dashed" style="margin-left:10px;" v-if="form.downSeries"> <tag-outlined />
<a-button @click="openSeriesDownSetModal" shape="circle" type="dashed" style="margin-left:5px;">
<fire-outlined />
</a-button>
</a-form-item-rest>
</div>
<a-alert message="开启后会同步下载收藏的短剧(需要开启短剧同步开关,不设置路径默认存储到收藏,设置后记得docker里面加映射)" :type="form.downSeries?'error':'info'" size="small" style="flex: 1; margin-bottom: 0;" />
</div>
</a-form-item>
<!--
<a-form-item v-if="form.savePath&&form.savePath.length>0" label="下载合集" name="downMix">
<div style="display: flex; align-items: center; gap: 12px;">
<div class="form-item-div">
<a-switch v-model:checked="form.downMix" :checked-value="true" :un-checked-value="false" size="default" />
<span style="margin-left:10px;">{{ form.downMix ? '是' : '否' }}</span>
<a-input v-model:value="form.mixPath" class="form-item-div-input" />
<a-button @click="openMixDownSetModal" shape="circle" type="dashed" style="margin-left:10px;" v-if="form.downMix"> <tag-outlined />
</a-button>
</div>
<a-alert message="开启后会同步下载收藏的合集(需要开启合集同步开关,不设置路径默认存储到收藏,设置后记得docker里面加映射)," :type="form.downMix?'error':'info'" size="small" style="flex: 1; margin-bottom: 0;" />
<a-alert message="开启后自动下载收藏的短剧视频(还需要开启短剧同步开关,不填目录径默认存储到收藏目录,设置后记得docker里面加映射,注意:付费视频下载后无法播放)
" :type="form.downSeries?'error':'info'" size="small" style="flex: 1; margin-bottom: 0;" />
</div>
</a-form-item>
<a-form-item v-if="form.savePath&&form.savePath.length>0" label="下载短剧" name="downSeries">
<div style="display: flex; align-items: center; gap: 12px;">
<div class="form-item-div">
<a-switch v-model:checked="form.downSeries" :checked-value="true" :un-checked-value="false" size="default" />
<span style="margin-left:10px;">{{ form.downSeries ? '是' : '否' }}</span>
<a-input v-model:value="form.seriesPath" class="form-item-div-input" />
<a-button @click="openSeriesDownSetModal" shape="circle" type="dashed" style="margin-left:10px;" v-if="form.downSeries"> <tag-outlined />
</a-button>
</div>
<a-alert message="开启后会同步下载收藏的短剧(需要开启短剧同步开关,不设置路径默认存储到收藏,设置后记得docker里面加映射)" :type="form.downSeries?'error':'info'" size="small" style="flex: 1; margin-bottom: 0;" />
</div>
</a-form-item> -->
<!-- 图文的存储路径 -->
<!-- <a-form-item label="图文的存储路径" name="imgSavePath">
<div style="display: flex; align-items: center; gap: 12px; width: 100%;">
<a-input v-model:value="form.imgSavePath" :disabled="form.useSinglePath" style="width: 200px;" />
<a-alert message="如果系统配置页面开启了同步图文视频,且开启了单独存储,则必填!!!" type="info" size="small" style="flex: 1; margin-bottom: 0;" />
</div>
</a-form-item> -->
<!-- 同步状态开关 -->
<a-form-item label="同步状态" name="status">
<!-- 任务同步状态 - 保持原有结构仅一个Switch无警告 -->
<a-form-item label="任务同步状态" name="status">
<div style="display: flex; align-items: center; gap: 8px;">
<a-switch v-model:checked="form.status" :checked-value="1" :un-checked-value="0" size="default" />
<span>{{ form.status === 1 ? '' : '' }}</span>
@@ -825,7 +816,7 @@ const saveDrawerData = () => {
width: 300px;
}
.form-item-div-input {
width: 150px;
width: 180px;
margin-left: 10px;
}
/* Firefox 透明滚动条适配 */
+1 -1
View File
@@ -130,7 +130,7 @@
</ItemGroup>
</Target>
<!-- 核心配置:仅用核心版本号2.0.5,自动拼接前缀 -->
<!-- 核心配置:仅用核心版本号2.0.6,自动拼接前缀 -->
<!--<Target Name="DockerBuildAndPushAfterPublish" AfterTargets="Publish">
<Exec Command="docker build -t jianzhichu/dysync.net:beta_$(DockerCoreVersion) . &amp;&amp; docker tag jianzhichu/dysync.net:beta_$(DockerCoreVersion) ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_$(DockerCoreVersion) &amp;&amp; docker push ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_$(DockerCoreVersion)"
Condition="'$(DockerArch)' == 'x86'" />
-11
View File
@@ -34,17 +34,6 @@ namespace dy.net.job
return await douyinHttpClientService.SyncCollectVideos(cursor, count, cookie.Cookies);
}
//protected override bool ShouldContinueSync(DouyinCookie cookie, DouyinVideoInfoResponse data, DouyinFollowed followed , AppConfig config)
//{
// return data != null && data.HasMore == 1 && cookie.CollHasSyncd == 0;
//}
//protected override async Task HandleSyncCompletion(DouyinCookie cookie, int syncCount,DouyinFollowed followed,DouyinCollectCate cate)
//{
// cookie.CollHasSyncd = 1;
// await douyinCookieService.UpdateAsync(cookie);
// Log.Debug($"[{VideoType}]-[{cookie.UserName}],本次成功同步{syncCount}条视频");
//}
+85 -71
View File
@@ -7,6 +7,7 @@ using dy.net.utils;
using Newtonsoft.Json;
using Quartz;
using Serilog;
using System.Net;
namespace dy.net.job
{
@@ -125,8 +126,6 @@ namespace dy.net.job
/// <returns>一个表示异步操作的任务</returns>
public async Task Execute(IJobExecutionContext context)
{
//if (VideoType.GetVideoTypeDesc() != VideoType.GetVideoTypeDesc()Enum.dy_follows)
// return;
// 获取应用配置
var config = douyinCommonService.GetConfig();
@@ -180,16 +179,6 @@ namespace dy.net.job
/// <returns>视频信息对象,包含视频列表和分页信息</returns>
protected abstract Task<DouyinVideoInfoResponse> FetchVideoData(DouyinCookie cookie, string cursor, DouyinFollowed followed, DouyinCollectCate cate);
///// <summary>
///// 判断是否应该继续同步下一页数据
///// </summary>
///// <param name="cookie">用户Cookie</param>
///// <param name="data">当前获取到的视频数据</param>
///// <param name="followed">关注博主</param>
///// <returns>如果应该继续同步,则为true;否则为false</returns>
//protected bool ShouldContinueSync(DouyinCookie cookie, DouyinVideoInfoResponse data, DouyinFollowed followed, AppConfig config) {
//}
/// <summary>
/// 获取下一页数据的游标
@@ -304,13 +293,12 @@ namespace dy.net.job
{
try
{
if (VideoType != VideoTypeEnum.dy_custom_collect || cookie.UseCollectFolder)
Log.Debug($"[{VideoType.GetDesc()}][{cookie.UserName}]开始同步...");
switch (VideoType)
{
case VideoTypeEnum.dy_follows:
if(cookie.DownFollowd)
{
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]开始同步...");
//查询关注列表开启了同步的关注
var follows = await douyinFollowService.GetSyncFollows(cookie.MyUserId);
if (follows != null && follows.Any())
@@ -325,15 +313,32 @@ namespace dy.net.job
}
}
}
else
{
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]同步未开启");
}
break;
case VideoTypeEnum.dy_favorite:
case VideoTypeEnum.dy_collects:
if (VideoType == VideoTypeEnum.dy_collects && cookie.UseCollectFolder)
if(cookie.DownFavorite)
{
Log.Debug($"[{VideoType.GetDesc()}]-已开启自定义收藏夹同步...break;");
break;
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]开始同步...");
int syncCount = 0;
string cursor = "0";
bool hasMore = true;
(syncCount, cursor, hasMore) = await GetAndSaveViedos(cookie, config, syncCount, cursor, hasMore);
await HandleSyncCompletion(cookie, syncCount);
}
else
{
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]同步未开启");
}
break;
case VideoTypeEnum.dy_collects:
if (cookie.UseCollectFolder)
{
Log.Debug($"[{VideoType.GetDesc()}]-已开启自定义收藏夹同步...break;");
}
else if (cookie.DownCollect)
{
int syncCount = 0;
string cursor = "0";
@@ -341,19 +346,37 @@ namespace dy.net.job
(syncCount, cursor, hasMore) = await GetAndSaveViedos(cookie, config, syncCount, cursor, hasMore);
await HandleSyncCompletion(cookie, syncCount);
}
else
{
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]同步未开启");
}
break;
case VideoTypeEnum.dy_mix:
if (cookie.DownMix)
{
await SyncCustomListVideos(cookie, config);
}
else
{
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]同步未开启");
}
break;
case VideoTypeEnum.dy_series:
if (cookie.DownSeries)
await SyncCustomListVideos(cookie, config);
else
{
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]同步未开启");
}
break;
case VideoTypeEnum.dy_custom_collect:
if (cookie.UseCollectFolder)
await SyncCustomListVideos(cookie, config);
else
{
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]同步未开启");
}
break;
case VideoTypeEnum.ImageVideo:
default:
@@ -364,7 +387,7 @@ namespace dy.net.job
}
catch (Exception ex)
{
Log.Error(ex, $"[{VideoType.GetDesc()}][{cookie.UserName}]同步出错!!!,{ex.StackTrace}");
Log.Error(ex, $"[{cookie.UserName}][{VideoType.GetDesc()}]同步出错!!!,{ex.StackTrace}");
}
}
/// <summary>
@@ -390,7 +413,7 @@ namespace dy.net.job
}
else
{
Serilog.Log.Debug($"[{VideoType.GetDesc()}]没有查询到已开启的对象");
Serilog.Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]没有查询到已开启的对象");
}
}
@@ -403,9 +426,16 @@ namespace dy.net.job
var data = await FetchVideoData(cookie, cursor, followed, cate);
if (data == null || data.AwemeList == null || !data.AwemeList.Any())
{
Serilog.Log.Debug($"[{VideoType.GetDesc()}][{cookie.UserName}] 没有新的视频");
Serilog.Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}] 没有新的视频");
break;
}
if (data.StatusCode == 0 && cookie.StatusMsg != "正常")
{
cookie.StatusCode = data.StatusCode;
cookie.StatusMsg = "正常";
await douyinCookieService.UpdateAsync(cookie);
}
// 判断是否还有更多数据
//hasMore = ShouldContinueSync(cookie, data, followed, config);
@@ -419,7 +449,7 @@ namespace dy.net.job
if (videos != null && videos.Any())
{
// 保存视频信息到数据库
await SaveVideos(videos);
await SaveVideos(cookie,videos);
}
syncCount += syncCountx;
@@ -451,7 +481,7 @@ namespace dy.net.job
{
if (syncCount >= 15)
{
Log.Debug($"[{VideoType.GetDesc()}][{cookie.UserName}]:本次同步数量{syncCount},等下次任务继续同步");
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]:本次同步数量{syncCount},等下次任务继续同步");
return true;
}
@@ -460,7 +490,7 @@ namespace dy.net.job
{
if (syncCount >= config.BatchCount)
{
Log.Debug($"[{VideoType.GetDesc()}][{cookie.UserName}]:本次同步数量{syncCount},已达配置上限{config.BatchCount},等下次任务继续同步");
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]:本次同步数量{syncCount},已达配置上限{config.BatchCount},等下次任务继续同步");
return true;
}
@@ -480,7 +510,7 @@ namespace dy.net.job
//// 状态满足则记录日志并返回「终止循环」
//if (isSyncCompleted.Invoke(cookie))
//{
// Log.Debug($"[{VideoType.GetDesc()}][{cookie.UserName}]本次同步达到上限{config.BatchCount},停止同步!!!");
// Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]本次同步达到上限{config.BatchCount},停止同步!!!");
// return true;
//}
@@ -522,7 +552,7 @@ namespace dy.net.job
{
bool Goon = await AutoDistinct(config, exitVideo);
bool Goon = await AutoDistinct(config, exitVideo, cookie);
if (!Goon)
{
continue;
@@ -598,7 +628,7 @@ namespace dy.net.job
if (!string.IsNullOrEmpty(dynamicVideo.DynamicVideos))
{
var dynamicVideos = JsonConvert.DeserializeObject<List<DouyinDynamicVideoDto>>(dynamicVideo.DynamicVideos);
Log.Debug($"[{VideoType.GetDesc()}][{cookie.UserName}]-动态视频[{item.Desc}],下载成功 ,共{dynamicVideos?.Count}个视频...");
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]-动态视频[{item.Desc}],下载成功 ,共{dynamicVideos?.Count}个视频...");
if (dynamicVideos != null && dynamicVideos.Count > 0)
{
@@ -640,7 +670,7 @@ namespace dy.net.job
}
else
{
Log.Debug($"[{VideoType.GetDesc()}][{cookie.UserName}]-动态视频[{item.Desc}],下载失败...");
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]-动态视频[{item.Desc}],下载失败...");
}
}
else
@@ -665,7 +695,7 @@ namespace dy.net.job
return (videos, syncCount);
}
private async Task<bool> AutoDistinct(AppConfig config, DouyinVideo exitVideo)
private async Task<bool> AutoDistinct(AppConfig config, DouyinVideo exitVideo,DouyinCookie cookie)
{
// 去重,检查视频是否已存在(按优先级下载)
if (config.AutoDistinct)
@@ -724,7 +754,7 @@ namespace dy.net.job
}
catch (Exception ex)
{
Log.Error($"[{VideoType.GetDesc()}]-删除重复的文件失败:{ex.Message}", ex);
Log.Error($"[{cookie.UserName}][{VideoType.GetDesc()}]-删除重复的文件[{exitVideo.VideoTitle}]失败:{ex.Message}", ex);
// 即使删除失败,仍继续下载(新文件会覆盖旧文件,或按路径规则重命名)
}
@@ -809,7 +839,6 @@ namespace dy.net.job
/// </summary>
/// <param name="cookie">用户Cookie</param>
/// <param name="item">视频信息</param>
/// <param name="data">视频信息对象</param>
/// <param name="config">应用配置</param>
/// <param name="followed">关注</param>
/// <param name="cate"></param>
@@ -823,14 +852,14 @@ namespace dy.net.job
if (v == null)
{
Serilog.Log.Error($"[{VideoType}][{cookie.UserName}][{item.Desc}]未获取到下载地址");
Serilog.Log.Error($"[{cookie.UserName}][{VideoType.GetDesc()}][{item.Desc}]未获取到下载地址");
return null;
}
//Serilog.Log.Debug($"{v.QualityType}-{v.BitRateValue}-{v.IsH265}-{v.HdrBit}");
var videoUrl = v.PlayAddr.UrlList.Where(x => !string.IsNullOrEmpty(x))?.LastOrDefault();
if (string.IsNullOrWhiteSpace(videoUrl))
{
Serilog.Log.Error($"[{VideoType}][{cookie.UserName}][{item.Desc}]未获取到下载地址");
Serilog.Log.Error($"[{cookie.UserName}][{VideoType.GetDesc()}]未获取到下载地址");
return null;
}
// 创建保存文件夹
@@ -848,7 +877,7 @@ namespace dy.net.job
return null;
}
Log.Debug($"[{VideoType.GetDesc()}][{item?.Author?.Nickname ?? ""}]-视频[{DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId)}]开始下载...");
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}][{item?.Author?.Nickname ?? ""}]-视频[{DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId)}]开始下载...");
// 随机延迟
await Task.Delay(_random.Next(1, 4) * 1000);
@@ -856,7 +885,7 @@ namespace dy.net.job
var (Success, _) = await douyinHttpClientService.DownloadAsync(videoUrl, savePath, cookie.Cookies);
if (!Success)
{
Log.Error($"[{VideoType.GetDesc()}][{item?.Author?.Nickname ?? ""}]-视频[{DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId)}]下载失败!!!");
Log.Error($"[{cookie.UserName}][{VideoType.GetDesc()}][{item?.Author?.Nickname ?? ""}]-视频[{DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId)}]下载失败!!!");
(bool flowControl, DouyinVideo value) = await SwitchOtherUrlAddressDown(cookie, item, videoUrl, savePath);
if (!flowControl)
@@ -866,7 +895,7 @@ namespace dy.net.job
}
else
{
Log.Debug($"[{VideoType.GetDesc()}][{item?.Author?.Nickname ?? ""}]-视频[{DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId)}]下载完成.");
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}][{item?.Author?.Nickname ?? ""}]-视频[{DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId)}]下载完成.");
}
// 下载视频封面
await GetDownVideoCover(item, saveFolder, cookie, config, cate);
@@ -921,7 +950,7 @@ namespace dy.net.job
// 拼接视频保存路径
var savePath = Path.Combine(saveFolder, fileName);
Log.Debug($"[{VideoType.GetDesc()}]-动态视频[{DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId)}]开始下载...");
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]-动态视频[{DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId)}]开始下载...");
// 随机延迟
int i = 1;
@@ -943,12 +972,12 @@ namespace dy.net.job
var (Success, ActualSavePath) = await douyinHttpClientService.DownloadAsync(dynamicUrl.Path, dynamicSavePath, cookie.Cookies);
if (!Success)
{
Log.Error($"[{VideoType.GetDesc()}][{item?.Author?.Nickname ?? ""}]-动态视频[{dynamicSavePath}]-00{i},下载失败!!!");
Log.Error($"[{cookie.UserName}][{VideoType.GetDesc()}][{item?.Author?.Nickname ?? ""}]-动态视频[{dynamicSavePath}]-00{i},下载失败!!!");
}
else
{
dynamicSavePaths.Add(v);
Log.Debug($"[{VideoType.GetDesc()}][{item?.Author?.Nickname ?? ""}]-动态视频[{dynamicSavePath}]-00{i},下载完成.");
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}][{item?.Author?.Nickname ?? ""}]-动态视频[{dynamicSavePath}]-00{i},下载完成.");
}
await Task.Delay(_random.Next(1, 4) * 1000);
}
@@ -1013,17 +1042,17 @@ namespace dy.net.job
var (Success, ActualSavePath) = await douyinHttpClientService.DownloadAsync(videoUrl, savePath, cookie.Cookies, otherUrls);
if (Success)
{
Log.Debug($"[{VideoType.GetDesc()}]-尝试多个地址后,下载成功,{savePath}");
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]-尝试多个地址后,下载成功,{savePath}");
}
else
{
Log.Error($"[{VideoType.GetDesc()}]-尝试多个地址后,依旧下载失败,{item.Desc}");
Log.Error($"[{cookie.UserName}][{VideoType.GetDesc()}]-尝试多个地址后,依旧下载失败,{item.Desc}");
return (flowControl: false, value: null);
}
}
else
{
Log.Error($"[{VideoType.GetDesc()}]-没有找到额外下载链接可以下载了," + item.Desc);
Log.Error($"[{cookie.UserName}][{VideoType.GetDesc()}]-没有找到额外可以尝试下载链接了," + item.Desc);
return (flowControl: false, value: null);
}
@@ -1098,7 +1127,7 @@ namespace dy.net.job
var mergeResult = await douyinMergeVideoService.MergeToVideo(cookie.Cookies, AppContext.BaseDirectory, reqParams, savePath, fileNamefolder, config.DownImageVideo, config.DownImage, config.DownMp3);
if (!mergeResult)
{
Log.Error($"[{VideoType.GetDesc()}]-图文视频-[{DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId)}]合成失败!!!");
Log.Error($"[{cookie.UserName}][{VideoType.GetDesc()}]-图文视频-[{DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId)}]合成失败!!!");
return null;
}
@@ -1110,13 +1139,13 @@ namespace dy.net.job
// 检查合成后的视频文件是否有效
if (!File.Exists(savePath) || new FileInfo(savePath).Length <= 0)
{
Log.Error($"[{VideoType.GetDesc()}]-图文视频-[{DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId)}]合成失败!!!");
Log.Error($"[{cookie.UserName}][{VideoType.GetDesc()}]-图文视频-[{DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId)}]合成失败!!!");
// 清理无效的文件和文件夹
if (Directory.Exists(fileNamefolder))
{
File.Delete(savePath);
Directory.Delete(fileNamefolder, true);
Log.Error($"[{VideoType.GetDesc()}]-图文视频-删除合成失败的视频文件和目录...");
Log.Error($"[{cookie.UserName}][{VideoType.GetDesc()}]-图文视频-删除合成失败的视频文件和目录...");
}
return null;
}
@@ -1160,7 +1189,7 @@ namespace dy.net.job
}
catch (Exception ex)
{
Log.Error(ex, $"[{VideoType.GetDesc()}]-图片视频同步-处理图片集并合成视频时出错");
Log.Error(ex, $"[{cookie.UserName}][{VideoType.GetDesc()}]-图片视频同步-处理图片集并合成视频时出错");
return null;
}
}
@@ -1169,9 +1198,10 @@ namespace dy.net.job
/// 保存视频信息到数据库
/// 批量插入视频实体列表到数据库中
/// </summary>
/// <param name="cookie"></param>
/// <param name="videos">要保存的视频实体列表</param>
/// <returns>保存成功的视频数量</returns>
protected async Task<int> SaveVideos(List<DouyinVideo> videos)
protected async Task<int> SaveVideos(DouyinCookie cookie, List<DouyinVideo> videos)
{
if (!videos.Any()) return 0;
try
@@ -1181,9 +1211,9 @@ namespace dy.net.job
}
catch (Exception ex)
{
Log.Error(ex, $"[{VideoType.GetDesc()}]-批量保存视频到数据库失败");
Log.Error(ex, $"[{cookie.UserName}][{VideoType.GetDesc()}]-批量保存视频到数据库失败");
// 清理保存失败的视频文件
await CleanupFailedVideos(videos);
await CleanupFailedVideos(cookie,videos);
return 0;
}
}
@@ -1284,11 +1314,12 @@ namespace dy.net.job
/// 清理保存失败的视频文件
/// 当数据库保存失败时,删除已下载的视频文件和文件夹
/// </summary>
/// <param name="cookie"></param>
/// <param name="videos">保存失败的视频实体列表</param>
/// <returns>一个表示异步操作的任务</returns>
private async Task CleanupFailedVideos(List<DouyinVideo> videos)
private async Task CleanupFailedVideos(DouyinCookie cookie, List<DouyinVideo> videos)
{
Log.Debug($"[{VideoType.GetDesc()}]-数据库保存失败,开始清理本次下载的视频目录...");
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]-数据库保存失败,开始清理本次下载的视频目录...");
foreach (var video in videos)
{
@@ -1306,12 +1337,12 @@ namespace dy.net.job
{
Directory.Delete(directory);
}
Log.Debug($"[{VideoType.GetDesc()}]-清理失败视频文件成功: {video.VideoSavePath}!!!");
Log.Debug($"[{cookie.UserName}][{VideoType.GetDesc()}]-清理失败视频文件成功: {video.VideoSavePath}!!!");
}
catch (Exception ex)
{
Log.Warning(ex, $"[{VideoType.GetDesc()}]-清理失败视频文件出错: {video.VideoSavePath}!!!");
Log.Warning(ex, $"[{cookie.UserName}][{VideoType.GetDesc()}]-清理失败视频文件出错: {video.VideoSavePath}!!!");
}
});
}
@@ -1403,23 +1434,6 @@ namespace dy.net.job
return video;
}
/// <summary>
///
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
//private string GetVideoSimplifyTitle(Aweme item)
//{
// var config = douyinCommonService.GetConfig();
// string simplifiedTitle = string.Empty;
// if (config?.UperUseViedoTitle ?? false)
// {
// simplifiedTitle = DouyinFileNameHelper.SanitizeLinuxFileName(item.Desc, item.AwemeId);
// }
// return simplifiedTitle;
//}
#endregion
+13 -17
View File
@@ -115,7 +115,7 @@ namespace dy.net.job
LOG_TAG_SERIES);
}
Log.Debug($"[{ck.UserName}]所有[基础数据(list)]同步完成,包括 [收藏列表、关注列表、合集列表、短剧列表] ");
Log.Debug($"[{ck.UserName}][基础数据]同步完成,包括 [收藏列表、关注列表、合集列表、短剧列表],这不是同步视频!!! ");
}
}
@@ -189,7 +189,7 @@ namespace dy.net.job
{
if (string.IsNullOrWhiteSpace(cookie.SecUserId))
{
Log.Debug($"[{cookie.UserName}][{LOG_TAG_FOLLOW}]:未设置SecUserId跳过");
Log.Debug($"[{cookie.UserName}][{LOG_TAG_FOLLOW}]:未设置SecUserId如果要同步关注列表(你关注的博主),需要在授权页面填写你的sce_user_id");
return;
}
@@ -197,29 +197,25 @@ namespace dy.net.job
string offset = "0";
bool hasMore = true;
int total = 0;
FollowErrorDto currentError = null;
try
{
while (hasMore)
{
var data = await _douyinService.SyncMyFollows(
DEFAULT_FOLLOW_COUNT, offset, cookie.SecUserId, cookie.Cookies,
async (err) =>
{
currentError = err;
cookie.StatusMsg = err.StatusCode == INVALID_COOKIE_STATUS_CODE ? "无效" : "正常";
cookie.StatusCode = err.StatusCode;
await _dyCookieService.UpdateAsync(cookie);
});
DEFAULT_FOLLOW_COUNT, offset, cookie.SecUserId, cookie.Cookies);
if (currentError != null && currentError.StatusCode != 0)
if (data == null)
{
Log.Error($"[{cookie.UserName}] - {LOG_TAG_FOLLOW}:错误 状态码:{currentError.StatusCode} 信息:{currentError.StatusMsg}");
if (currentError.StatusCode == INVALID_COOKIE_STATUS_CODE) break;
Log.Error($"[{cookie.UserName}] - {LOG_TAG_FOLLOW}同步异常,请检查cookie");
break;
}
else
{
cookie.StatusCode = data.StatusCode;
cookie.StatusMsg = data.StatusCode == 0 ? "正常" : "异常";
await _dyCookieService.UpdateAsync(cookie);
}
if (data == null) break;
if (string.IsNullOrWhiteSpace(cookie.MyUserId))
{
@@ -243,7 +239,7 @@ namespace dy.net.job
if (followList.Any())
{
var (add, update, succ) = await _followService.Sync(followList, cookie);
Log.Debug($"[{cookie.UserName}][{LOG_TAG_FOLLOW}]:同步完成 新增:{add} 更新:{update} 成功:{succ} 总关注数:{total}");
Log.Debug($"[{cookie.UserName}][{LOG_TAG_FOLLOW}]:同步完成 新增:{add} 更新:{update} 成功:{succ} 总关注数:{total},这是同步关注列表,这不是同步视频!!!");
}
await _douyinCommonService.SetConfigNotFirstRunning();
}
-24
View File
@@ -1,24 +0,0 @@
using Newtonsoft.Json;
namespace dy.net.model.dto
{
public class FollowErrorDto
{
/// <summary>
///
/// </summary>
[JsonProperty("followings")]
public object Followings { get; set; }
/// <summary>
/// 8 - 用户未登录
/// </summary>
[JsonProperty("status_code")]
public int StatusCode { get; set; }
/// <summary>
/// 用户未登录
/// </summary>
[JsonProperty("status_msg")]
public string StatusMsg { get; set; }
}
}
+2
View File
@@ -50,4 +50,6 @@
Description = description ?? throw new ArgumentNullException(nameof(description), "任务描述不能为空");
}
}
}
+8
View File
@@ -38,6 +38,14 @@
/// </summary>
//[Description("收藏短剧")]
dy_series = 7,
/// <summary>
/// 同步关注列表--
/// </summary>
dy_followuser=1000,
/// <summary>
/// 同步关注列表一次
/// </summary>
dy_followuser_once = 2000,
}
+13
View File
@@ -43,6 +43,19 @@ namespace dy.net.model.entity
/// </summary>
public bool DownSeries { get; set; }
/// <summary>
/// 是否下载收藏夹视频
/// </summary>
public bool DownCollect { get; set; }
/// <summary>
/// 是否下载点赞(喜欢的)视频
/// </summary>
public bool DownFavorite { get; set; }
/// <summary>
/// 是否下载关注的博主的作品
/// </summary>
public bool DownFollowd { get; set; }
/// <summary>
/// 1 开启同步 0 关闭同步
/// </summary>
public int Status { get; set; }
+18
View File
@@ -72,6 +72,24 @@ namespace dy.net.service
return _cookieRepository.Insert(cookie);
}
/// <summary>
/// 新增字段,兼容旧版本
/// </summary>
/// <returns></returns>
public async Task<bool> UpdateCookieToSupportOldVersionAsync()
{
var cookies = _cookieRepository.GetAll();
foreach (var item in cookies)
{
item.DownCollect = !string.IsNullOrWhiteSpace(item.SavePath) && !item.UseCollectFolder;
item.DownFavorite = !string.IsNullOrWhiteSpace(item.FavSavePath);
item.DownFollowd = !string.IsNullOrWhiteSpace(item.UpSavePath);
}
await _cookieRepository.UpdateRangeAsync(cookies);
return true;
}
// 查询单个
public async Task<DouyinCookie> GetByIdAsync(string id)
{
+7
View File
@@ -125,6 +125,13 @@ namespace dy.net.service
/// <returns></returns>
public async Task<bool> OpenOrCloseSync(FollowUpdateDto dto)
{
var followdOpendCount = await _followRepository.CountAsync(x => x.OpenSync);
if (followdOpendCount > 20)
{
throw new Exception("为了减少被风控的可能,关注列表最多同时开启20个,抱歉");
}
var followed = await _followRepository.GetByIdAsync(dto.Id);
if (followed != null)
{
+3 -27
View File
@@ -642,9 +642,8 @@ namespace dy.net.service
/// <param name="offset"></param>
/// <param name="secUserId"></param>
/// <param name="cookie"></param>
/// <param name="callBack"></param>
/// <returns></returns>
public async Task<DouyinFollowInfoResponse> SyncMyFollows(string count, string offset, string secUserId, string cookie, Action<FollowErrorDto> callBack)
public async Task<DouyinFollowInfoResponse> SyncMyFollows(string count, string offset, string secUserId, string cookie)
{
#region
@@ -686,29 +685,6 @@ namespace dy.net.service
{
var data = await respose.Content.ReadAsStringAsync();
var res = JsonConvert.DeserializeObject<DouyinFollowInfoResponse>(data);
if (res != null)
{
if (res.Followings == null)
{
var err = JsonConvert.DeserializeObject<FollowErrorDto>(data);
if (err != null)
{
Serilog.Log.Error($"SyncMyFollows error: {err.StatusMsg}");
callBack?.Invoke(err);
}
Serilog.Log.Error($"SyncMyFollows error: 关注列表为空 :{data}");
}
else
{
callBack?.Invoke(new FollowErrorDto { StatusCode = 0 });
}
}
else
{
Serilog.Log.Error($"SyncMyFollows error: 反序列化结果为空,接口返回数据:{data}");
callBack?.Invoke(new FollowErrorDto { StatusCode = 8, StatusMsg = "未知" });
}
return res;
}
else
@@ -739,12 +715,12 @@ namespace dy.net.service
if (!string.IsNullOrWhiteSpace(douyinCookie.SecUserId))
{
var res = await SyncMyFollows("1", "1", douyinCookie.SecUserId, douyinCookie.Cookies, null);
var res = await SyncMyFollows("1", "10", douyinCookie.SecUserId, douyinCookie.Cookies);
return res != null && res.StatusCode == 0;
}
else
{
var res = await SyncCollectVideos("0", "1", douyinCookie.Cookies);
var res = await SyncCollectVideos("0", "10", douyinCookie.Cookies);
return res != null && res.StatusCode == 0;
}
}
+162 -52
View File
@@ -1,5 +1,6 @@
using dy.net.job;
using dy.net.model.dto;
using dy.net.model.entity;
using dy.net.utils;
using Quartz;
using Serilog;
@@ -12,16 +13,17 @@ namespace dy.net.service
public class DouyinQuartzJobService
{
private readonly ISchedulerFactory _schedulerFactory;
private readonly DouyinCookieService douyinCookieService;
private const string DefaultJobGroup = "dysync.net";
private const int DefaultIntervalMinutes = 30;
private const int DefaultCronStartDelaySeconds = 30;
private const int DefaultSimpleStartDelaySeconds = 3;
// 任务配置信息(修复了series任务的Key重复问题,确保每个任务Key唯一)
public static Dictionary<string, JobConfig> JobConfigs { get; } = new()
public static Dictionary<VideoTypeEnum, JobConfig> JobConfigs { get; } = new()
{
{
VideoTypeEnum.dy_collects.GetDesc(),
VideoTypeEnum.dy_collects,
new JobConfig(
typeof(DouyinCollectSyncJob),
"dy.job.key.collect",
@@ -29,7 +31,7 @@ namespace dy.net.service
"抖音收藏同步任务")
},
{
VideoTypeEnum.dy_favorite.GetDesc(),
VideoTypeEnum.dy_favorite,
new JobConfig(
typeof(DouyinFavoritSyncJob),
"dy.job.key.favorite",
@@ -37,7 +39,7 @@ namespace dy.net.service
"抖音点赞同步任务")
},
{
VideoTypeEnum.dy_follows.GetDesc(),
VideoTypeEnum.dy_follows,
new JobConfig(
typeof(DouyinFollowedSyncJob),
"dy.job.key.followed",
@@ -45,7 +47,7 @@ namespace dy.net.service
"抖音关注博主作品同步任务")
},
{
"关注列表",
VideoTypeEnum.dy_followuser,
new JobConfig(
typeof(DouyinFollowsAndCollnectsSyncJob),
"dy.job.key.follow_user",
@@ -53,7 +55,7 @@ namespace dy.net.service
"抖音关注列表同步任务")
},
{
VideoTypeEnum.dy_custom_collect.GetDesc(),
VideoTypeEnum.dy_custom_collect,
new JobConfig(
typeof(DouyinCollectCustomSyncJob),
"dy.job.key.custom_collect",
@@ -61,7 +63,7 @@ namespace dy.net.service
"抖音自定义收藏夹列表同步任务")
},
{
VideoTypeEnum.dy_mix.GetDesc(),
VideoTypeEnum.dy_mix,
new JobConfig(
typeof(DouyinMixSyncJob),
"dy.job.key.mix",
@@ -69,7 +71,7 @@ namespace dy.net.service
"抖音收藏夹合集同步任务")
},
{
VideoTypeEnum.dy_series.GetDesc(),
VideoTypeEnum.dy_series,
new JobConfig(
typeof(DouyinSeriesSyncJob),
"dy.job.key.series",
@@ -77,7 +79,7 @@ namespace dy.net.service
"抖音收藏夹短剧同步任务")
},
{
"sync_follow_user_once",
VideoTypeEnum.dy_followuser_once,
new JobConfig(
typeof(DouyinFollowsAndCollnectsSyncJob),
"dy.job.key.sync_follow_user_once",
@@ -86,68 +88,160 @@ namespace dy.net.service
}
};
public DouyinQuartzJobService(ISchedulerFactory schedulerFactory)
public DouyinQuartzJobService(ISchedulerFactory schedulerFactory,DouyinCookieService douyinCookieService)
{
_schedulerFactory = schedulerFactory ?? throw new ArgumentNullException(nameof(schedulerFactory));
this.douyinCookieService = douyinCookieService;
}
/// <summary>
/// 启所有抖音相关定时任务(所有任务独立执行)
/// 初始化或重启所有抖音定时任务
/// </summary>
/// <param name="expression">Cron表达式或间隔分钟数(所有任务使用相同的执行频率</param>
/// <returns>是否启动成功</returns>
public async Task<bool> InitOrReStartAllJobs(string expression)
/// <param name="cronExpression">定时任务表达式(分钟数</param>
/// <returns>是否成功初始化</returns>
public async Task<bool> InitOrReStartAllJobs(string cronExpression)
{
if (string.IsNullOrWhiteSpace(expression))
{
Log.Debug("定时任务表达式为空,使用默认配置({DefaultMinutes}分钟)", DefaultIntervalMinutes);
expression = DefaultIntervalMinutes.ToString();
}
try
{
var scheduler = await _schedulerFactory.GetScheduler();
// 1. 获取并验证Cookie
var validCookies = await douyinCookieService.GetOpendCookiesAsync();
if (validCookies == null || !validCookies.Any())
{
Serilog.Log.Debug("没有有效的抖音Cookie,无法启动定时任务");
return false;
}
// 2. 处理定时任务表达式
var taskIntervalExpression = ResolveTaskExpression(cronExpression);
// 3. 获取调度器并清理现有任务
var scheduler = await _schedulerFactory.GetScheduler();
if (scheduler == null)
{
Log.Error("获取任务调度器失败,无法初始化定时任务");
return false;
}
// 移除所有已存在的任务(避免重复调度)
await RemoveAllExistingJobs(scheduler);
// 4. 检查各类型任务的启用条件
var taskEnableConditions = GetTaskEnableConditions(validCookies);
// 执行任务启动逻辑
// 5. 启动符合条件的定时任务
int successfullyStartedJobs = 0;
foreach (var jobKey in JobConfigs.Keys)
{
if (jobKey == "sync_follow_user_once")
// 跳过一次性关注用户任务
if (jobKey == VideoTypeEnum.dy_followuser_once)
continue;
if (jobKey == "follow_user") expression = "60";
var startSuccess = await StartJobAsync(jobKey, expression);
if (startSuccess)
{
Log.Debug($"成功启动任务:{jobKey},执行频率:{expression}");
}
else
{
Log.Error($"启动任务失败:{jobKey}");
}
}
Log.Information($"共启动 {JobConfigs.Count - 1} 个定时任务");
// 处理关注用户任务(固定60分钟执行频率)
if (jobKey == VideoTypeEnum.dy_followuser)
{
bool startSuccess = await StartSingleJobAsync(jobKey, "60");
if (startSuccess) successfullyStartedJobs++;
continue;
}
// 根据不同任务类型和启用条件启动任务
bool isTaskEnabled = jobKey switch
{
VideoTypeEnum.dy_favorite => taskEnableConditions.IsFavoriteEnabled,
VideoTypeEnum.dy_collects => taskEnableConditions.IsCollectEnabled,
VideoTypeEnum.dy_follows => taskEnableConditions.IsFollowedEnabled,
VideoTypeEnum.dy_custom_collect => taskEnableConditions.IsCustomCollectEnabled,
VideoTypeEnum.dy_mix => taskEnableConditions.IsMixEnabled,
VideoTypeEnum.dy_series => taskEnableConditions.IsSeriesEnabled,
_ => false
};
if (isTaskEnabled)
{
bool startSuccess = await StartSingleJobAsync(jobKey, taskIntervalExpression);
if (startSuccess) successfullyStartedJobs++;
}
}
// 6. 输出任务启动统计日志
Log.Information($"定时任务初始化完成,共尝试启动 {JobConfigs.Count - 1} 个任务,成功启动 {successfullyStartedJobs} 个");
//await StartJobAsync(VideoTypeEnum.dy_custom_collect.GetDesc(), expression);
//await StartFollowJobOnceAsync();
return true;
}
catch (Exception ex)
{
Log.Error(ex, "【任务服务】初始化所有定时任务异常");
Log.Error(ex, "【quartz】初始化所有抖音定时任务时发生异常");
return false;
}
}
/// <summary>
/// 解析任务执行表达式,为空时使用默认值
/// </summary>
/// <param name="inputExpression">输入的表达式</param>
/// <returns>处理后的表达式</returns>
private static string ResolveTaskExpression(string inputExpression)
{
if (string.IsNullOrWhiteSpace(inputExpression))
{
Log.Debug("定时任务表达式为空,使用默认配置({DefaultMinutes}分钟)", DefaultIntervalMinutes);
return DefaultIntervalMinutes.ToString();
}
return inputExpression;
}
/// <summary>
/// 获取各类型任务的启用条件
/// </summary>
/// <param name="cookies">有效的抖音Cookie列表</param>
/// <returns>任务启用条件集合</returns>
private static TaskEnableConditions GetTaskEnableConditions(IEnumerable<DouyinCookie> cookies)
{
return new TaskEnableConditions
{
IsCollectEnabled = cookies.Any(x => x.DownCollect && !x.UseCollectFolder && !string.IsNullOrWhiteSpace(x.SavePath)),
IsFavoriteEnabled = cookies.Any(x => x.DownFavorite && !string.IsNullOrWhiteSpace(x.FavSavePath)),
IsFollowedEnabled = cookies.Any(x => x.DownFollowd && !string.IsNullOrWhiteSpace(x.UpSavePath)),
IsMixEnabled = cookies.Any(x => x.DownMix && !string.IsNullOrWhiteSpace(x.MixPath) && !string.IsNullOrWhiteSpace(x.SavePath)),
IsSeriesEnabled = cookies.Any(x => x.DownSeries && !string.IsNullOrWhiteSpace(x.SeriesPath) && !string.IsNullOrWhiteSpace(x.SavePath)),
IsCustomCollectEnabled = cookies.Any(x => x.UseCollectFolder && !string.IsNullOrWhiteSpace(x.SavePath))
};
}
/// <summary>
/// 启动单个定时任务(封装重复的启动逻辑)
/// </summary>
/// <param name="jobKey">任务类型</param>
/// <param name="expression">执行频率表达式</param>
/// <returns>是否启动成功</returns>
private async Task<bool> StartSingleJobAsync(VideoTypeEnum jobKey, string expression)
{
try
{
bool startSuccess = await StartJobAsync(jobKey, expression);
if (startSuccess)
{
Log.Debug($"【quartz】成功启动任务:{jobKey},执行频率:{expression}分钟");
}
else
{
Log.Error($"【quartz】启动任务失败:{jobKey}");
}
return startSuccess;
}
catch (Exception ex)
{
Log.Error(ex, $"【quartz】启动任务 {jobKey} 时发生异常");
return false;
}
}
/// <summary>
/// 启动关注同步任务(单次执行)
/// </summary>
public async Task<bool> StartFollowJobOnceAsync()
{
return await StartOneTimeJobAsync("sync_follow_user_once");
return await StartOneTimeJobAsync(VideoTypeEnum.dy_followuser_once);
}
/// <summary>
@@ -160,7 +254,7 @@ namespace dy.net.service
{
if (await scheduler.CheckExists(jobKey))
{
Log.Information("【任务服务】移除已存在的任务: {JobKey}", jobKey);
Log.Debug("【quartz】移除已存在的任务: {JobKey}", jobKey);
await scheduler.DeleteJob(jobKey);
}
}
@@ -172,11 +266,11 @@ namespace dy.net.service
/// <param name="configKey">任务配置Key(如:collect、favorite</param>
/// <param name="expression">定时表达式(Cron或间隔分钟数)</param>
/// <returns>是否启动成功</returns>
public async Task<bool> StartJobAsync(string configKey, string expression)
public async Task<bool> StartJobAsync(VideoTypeEnum configKey, string expression)
{
if (!JobConfigs.TryGetValue(configKey, out var jobConfig))
{
Log.Error("【任务服务】找不到任务配置: {ConfigKey}", configKey);
Log.Error("【quartz】找不到任务配置: {ConfigKey}", configKey);
return false;
}
@@ -201,7 +295,7 @@ namespace dy.net.service
// 调度任务
await scheduler.ScheduleJob(jobDetail, trigger);
Log.Information("【任务服务】启动任务成功 - 任务描述: {JobDescription}, 执行频率: {Expression}",
Log.Information("【quartz】启动任务成功 - 任务描述: {JobDescription}, 执行频率: {Expression}",
jobConfig.Description,
expression);
@@ -209,7 +303,7 @@ namespace dy.net.service
}
catch (Exception ex)
{
Log.Error(ex, "【任务服务】启动任务失败 - 任务描述: {JobDescription}", jobConfig.Description);
Log.Error(ex, "【quartz】启动任务失败 - 任务描述: {JobDescription}", jobConfig.Description);
return false;
}
}
@@ -217,11 +311,11 @@ namespace dy.net.service
/// <summary>
/// 启动单次执行任务
/// </summary>
private async Task<bool> StartOneTimeJobAsync(string configKey)
private async Task<bool> StartOneTimeJobAsync(VideoTypeEnum configKey)
{
if (!JobConfigs.TryGetValue(configKey, out var jobConfig))
{
Log.Error("【任务服务】找不到任务配置: {ConfigKey}", configKey);
Log.Error("【quartz】找不到任务配置: {ConfigKey}", configKey);
return false;
}
@@ -246,13 +340,13 @@ namespace dy.net.service
.Build();
await scheduler.ScheduleJob(jobDetail, trigger);
Log.Debug("【任务服务】启动单次任务成功 - 任务描述: {JobDescription}", jobConfig.Description);
Log.Debug("【quartz】启动单次任务成功 - 任务描述: {JobDescription}", jobConfig.Description);
return true;
}
catch (Exception ex)
{
Log.Error(ex, "【任务服务】启动单次任务失败 - 任务描述: {JobDescription}", jobConfig.Description);
Log.Error(ex, "【quartz】启动单次任务失败 - 任务描述: {JobDescription}", jobConfig.Description);
return false;
}
}
@@ -288,8 +382,8 @@ namespace dy.net.service
}
// 无效表达式,使用默认配置
Log.Warning("【任务服务】无效的任务表达式: {Expression},使用默认间隔{DefaultMinutes}分钟",
expression, DefaultIntervalMinutes);
//Log.Debug("【任务服务】无效的任务表达式: {Expression},使用默认间隔{DefaultMinutes}分钟",
// expression, DefaultIntervalMinutes);
return TriggerBuilder.Create()
.WithIdentity(triggerKey)
@@ -308,9 +402,25 @@ namespace dy.net.service
{
if (await scheduler.CheckExists(jobKey))
{
Log.Information("【任务服务】移除已存在的任务: {JobKey}", jobKey);
Log.Debug("【quartz】移除已存在的任务: {JobKey}", jobKey);
await scheduler.DeleteJob(jobKey);
}
}
/// <summary>
/// 任务启用条件模型
/// </summary>
private class TaskEnableConditions
{
public bool IsCollectEnabled { get; set; }
public bool IsFavoriteEnabled { get; set; }
public bool IsFollowedEnabled { get; set; }
public bool IsMixEnabled { get; set; }
public bool IsSeriesEnabled { get; set; }
public bool IsCustomCollectEnabled { get; set; }
}
}
}