去重优先级,视频合成,没有音频用默认音频
This commit is contained in:
@@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<_PublishTargetUrl>E:\code\dysync\bin\Release\net6.0\publish\</_PublishTargetUrl>
|
||||
<History>True|2025-12-02T06:39:58.8932130Z||;True|2025-12-02T14:36:37.1529072+08:00||;True|2025-12-02T12:58:22.0951548+08:00||;True|2025-12-02T09:30:33.7066474+08:00||;True|2025-12-02T09:30:14.5481844+08:00||;True|2025-12-02T09:30:00.3123364+08:00||;False|2025-12-02T09:29:54.4615520+08:00||;True|2025-12-02T09:13:01.7995414+08:00||;False|2025-12-02T09:12:55.8360281+08:00||;True|2025-12-02T09:12:31.0156791+08:00||;True|2025-12-02T08:55:11.3773395+08:00||;True|2025-12-02T08:53:21.3927713+08:00||;False|2025-12-02T08:48:55.8638037+08:00||;True|2025-12-02T07:29:25.2192447+08:00||;False|2025-12-02T07:29:10.2504665+08:00||;True|2025-12-02T07:28:29.0769286+08:00||;True|2025-12-01T21:53:07.6474834+08:00||;True|2025-12-01T21:44:28.1674315+08:00||;True|2025-12-01T21:18:47.2119609+08:00||;True|2025-12-01T20:51:19.9948301+08:00||;True|2025-12-01T20:50:36.9904697+08:00||;True|2025-12-01T20:44:10.1695142+08:00||;True|2025-12-01T19:27:58.0479456+08:00||;True|2025-12-01T19:16:02.2288214+08:00||;False|2025-12-01T19:15:56.0372115+08:00||;True|2025-12-01T19:15:16.4854821+08:00||;False|2025-12-01T19:15:06.7001019+08:00||;True|2025-12-01T17:10:38.9739466+08:00||;False|2025-12-01T17:10:18.8928139+08:00||;True|2025-12-01T17:03:59.3171589+08:00||;True|2025-12-01T17:03:12.5681656+08:00||;True|2025-12-01T16:42:51.4415025+08:00||;True|2025-12-01T01:29:59.6975567+08:00||;True|2025-12-01T01:29:45.1898652+08:00||;False|2025-12-01T01:29:33.1787721+08:00||;True|2025-12-01T01:27:33.1297605+08:00||;True|2025-12-01T01:27:27.3976862+08:00||;False|2025-12-01T01:27:20.5193052+08:00||;True|2025-12-01T01:11:11.0210582+08:00||;True|2025-12-01T01:04:04.3307782+08:00||;True|2025-12-01T00:55:49.8018864+08:00||;True|2025-12-01T00:27:25.1323561+08:00||;True|2025-11-30T23:58:20.7284116+08:00||;True|2025-11-30T23:57:58.3027622+08:00||;True|2025-11-30T23:57:43.0829614+08:00||;False|2025-11-30T23:57:33.5606716+08:00||;True|2025-11-30T23:54:29.6203368+08:00||;True|2025-11-30T23:54:27.5383120+08:00||;False|2025-11-30T23:54:17.0823982+08:00||;True|2025-11-30T22:41:11.4538820+08:00||;True|2025-11-30T22:38:19.7064566+08:00||;True|2025-11-30T22:37:35.2889186+08:00||;True|2025-11-30T21:42:31.1408314+08:00||;True|2025-11-30T21:42:19.5385333+08:00||;False|2025-11-30T21:42:13.3290237+08:00||;True|2025-11-30T21:33:28.0806806+08:00||;True|2025-11-30T21:16:56.7088027+08:00||;False|2025-11-30T21:16:46.1444944+08:00||;True|2025-11-30T20:21:24.4059664+08:00||;False|2025-11-30T20:21:17.6006098+08:00||;True|2025-11-30T20:20:08.2030141+08:00||;False|2025-11-30T20:16:12.3608968+08:00||;True|2025-11-30T20:15:35.6048313+08:00||;True|2025-11-30T19:45:18.7229552+08:00||;True|2025-11-30T19:45:08.6083368+08:00||;False|2025-11-30T19:44:54.7824905+08:00||;True|2025-11-30T19:44:17.0179491+08:00||;True|2025-11-30T19:44:06.7073484+08:00||;False|2025-11-30T19:43:58.2896556+08:00||;True|2025-11-30T19:42:08.2261927+08:00||;True|2025-11-30T19:28:15.7115077+08:00||;False|2025-11-30T19:28:10.2280725+08:00||;True|2025-11-30T19:23:31.7859158+08:00||;True|2025-11-30T19:23:20.7788277+08:00||;True|2025-11-30T19:17:11.8583296+08:00||;False|2025-11-30T19:10:41.8369574+08:00||;True|2025-11-30T19:02:08.8184758+08:00||;False|2025-11-30T19:01:59.4711045+08:00||;True|2025-11-30T18:16:01.8372242+08:00||;True|2025-11-30T14:50:31.9356543+08:00||;True|2025-11-30T14:48:27.1833064+08:00||;True|2025-11-30T02:06:06.6099669+08:00||;True|2025-11-30T01:49:46.4358206+08:00||;False|2025-11-30T01:49:34.4884036+08:00||;True|2025-11-30T01:43:41.1028753+08:00||;True|2025-11-30T01:38:18.2856609+08:00||;True|2025-11-30T01:23:02.4378259+08:00||;True|2025-11-30T01:08:29.4041159+08:00||;True|2025-11-30T00:56:33.4172007+08:00||;False|2025-11-30T00:56:21.2777389+08:00||;True|2025-11-27T13:37:08.6884390+08:00||;False|2025-11-27T13:36:54.2595622+08:00||;True|2025-11-27T11:03:33.8401644+08:00||;False|2025-11-27T11:02:48.8942827+08:00||;True|2025-11-26T23:48:02.3957186+08:00||;True|2025-11-26T23:43:06.8154188+08:00||;False|2025-11-26T23:42:05.9191485+08:00||;True|2025-11-26T23:30:11.1295861+08:00||;True|2025-11-26T00:00:42.1507258+08:00||;True|2025-11-26T00:00:17.0107229+08:00||;</History>
|
||||
<History>True|2025-12-02T14:11:37.4645042Z||;False|2025-12-02T22:10:47.1320259+08:00||;True|2025-12-02T14:39:58.8932130+08:00||;True|2025-12-02T14:36:37.1529072+08:00||;True|2025-12-02T12:58:22.0951548+08:00||;True|2025-12-02T09:30:33.7066474+08:00||;True|2025-12-02T09:30:14.5481844+08:00||;True|2025-12-02T09:30:00.3123364+08:00||;False|2025-12-02T09:29:54.4615520+08:00||;True|2025-12-02T09:13:01.7995414+08:00||;False|2025-12-02T09:12:55.8360281+08:00||;True|2025-12-02T09:12:31.0156791+08:00||;True|2025-12-02T08:55:11.3773395+08:00||;True|2025-12-02T08:53:21.3927713+08:00||;False|2025-12-02T08:48:55.8638037+08:00||;True|2025-12-02T07:29:25.2192447+08:00||;False|2025-12-02T07:29:10.2504665+08:00||;True|2025-12-02T07:28:29.0769286+08:00||;True|2025-12-01T21:53:07.6474834+08:00||;True|2025-12-01T21:44:28.1674315+08:00||;True|2025-12-01T21:18:47.2119609+08:00||;True|2025-12-01T20:51:19.9948301+08:00||;True|2025-12-01T20:50:36.9904697+08:00||;True|2025-12-01T20:44:10.1695142+08:00||;True|2025-12-01T19:27:58.0479456+08:00||;True|2025-12-01T19:16:02.2288214+08:00||;False|2025-12-01T19:15:56.0372115+08:00||;True|2025-12-01T19:15:16.4854821+08:00||;False|2025-12-01T19:15:06.7001019+08:00||;True|2025-12-01T17:10:38.9739466+08:00||;False|2025-12-01T17:10:18.8928139+08:00||;True|2025-12-01T17:03:59.3171589+08:00||;True|2025-12-01T17:03:12.5681656+08:00||;True|2025-12-01T16:42:51.4415025+08:00||;True|2025-12-01T01:29:59.6975567+08:00||;True|2025-12-01T01:29:45.1898652+08:00||;False|2025-12-01T01:29:33.1787721+08:00||;True|2025-12-01T01:27:33.1297605+08:00||;True|2025-12-01T01:27:27.3976862+08:00||;False|2025-12-01T01:27:20.5193052+08:00||;True|2025-12-01T01:11:11.0210582+08:00||;True|2025-12-01T01:04:04.3307782+08:00||;True|2025-12-01T00:55:49.8018864+08:00||;True|2025-12-01T00:27:25.1323561+08:00||;True|2025-11-30T23:58:20.7284116+08:00||;True|2025-11-30T23:57:58.3027622+08:00||;True|2025-11-30T23:57:43.0829614+08:00||;False|2025-11-30T23:57:33.5606716+08:00||;True|2025-11-30T23:54:29.6203368+08:00||;True|2025-11-30T23:54:27.5383120+08:00||;False|2025-11-30T23:54:17.0823982+08:00||;True|2025-11-30T22:41:11.4538820+08:00||;True|2025-11-30T22:38:19.7064566+08:00||;True|2025-11-30T22:37:35.2889186+08:00||;True|2025-11-30T21:42:31.1408314+08:00||;True|2025-11-30T21:42:19.5385333+08:00||;False|2025-11-30T21:42:13.3290237+08:00||;True|2025-11-30T21:33:28.0806806+08:00||;True|2025-11-30T21:16:56.7088027+08:00||;False|2025-11-30T21:16:46.1444944+08:00||;True|2025-11-30T20:21:24.4059664+08:00||;False|2025-11-30T20:21:17.6006098+08:00||;True|2025-11-30T20:20:08.2030141+08:00||;False|2025-11-30T20:16:12.3608968+08:00||;True|2025-11-30T20:15:35.6048313+08:00||;True|2025-11-30T19:45:18.7229552+08:00||;True|2025-11-30T19:45:08.6083368+08:00||;False|2025-11-30T19:44:54.7824905+08:00||;True|2025-11-30T19:44:17.0179491+08:00||;True|2025-11-30T19:44:06.7073484+08:00||;False|2025-11-30T19:43:58.2896556+08:00||;True|2025-11-30T19:42:08.2261927+08:00||;True|2025-11-30T19:28:15.7115077+08:00||;False|2025-11-30T19:28:10.2280725+08:00||;True|2025-11-30T19:23:31.7859158+08:00||;True|2025-11-30T19:23:20.7788277+08:00||;True|2025-11-30T19:17:11.8583296+08:00||;False|2025-11-30T19:10:41.8369574+08:00||;True|2025-11-30T19:02:08.8184758+08:00||;False|2025-11-30T19:01:59.4711045+08:00||;True|2025-11-30T18:16:01.8372242+08:00||;True|2025-11-30T14:50:31.9356543+08:00||;True|2025-11-30T14:48:27.1833064+08:00||;True|2025-11-30T02:06:06.6099669+08:00||;True|2025-11-30T01:49:46.4358206+08:00||;False|2025-11-30T01:49:34.4884036+08:00||;True|2025-11-30T01:43:41.1028753+08:00||;True|2025-11-30T01:38:18.2856609+08:00||;True|2025-11-30T01:23:02.4378259+08:00||;True|2025-11-30T01:08:29.4041159+08:00||;True|2025-11-30T00:56:33.4172007+08:00||;False|2025-11-30T00:56:21.2777389+08:00||;True|2025-11-27T13:37:08.6884390+08:00||;False|2025-11-27T13:36:54.2595622+08:00||;True|2025-11-27T11:03:33.8401644+08:00||;False|2025-11-27T11:02:48.8942827+08:00||;True|2025-11-26T23:48:02.3957186+08:00||;True|2025-11-26T23:43:06.8154188+08:00||;False|2025-11-26T23:42:05.9191485+08:00||;True|2025-11-26T23:30:11.1295861+08:00||;</History>
|
||||
<LastFailureDetails />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
|
||||
+3
-1
@@ -32,11 +32,13 @@
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.0.33",
|
||||
"qs": "^6.10.1",
|
||||
"sortablejs": "^1.15.6",
|
||||
"stepin": "2.1.42-beta",
|
||||
"vue": "^3.3.4",
|
||||
"vue-json-editor": "^1.4.3",
|
||||
"vue-request": "^2.0.4",
|
||||
"vue-router": "^4.1.6"
|
||||
"vue-router": "^4.1.6",
|
||||
"vue-sortablejs": "^1.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/container-queries": "^0.1.0",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
// 步骤2:声明父组件传递的 showSetting 事件(关键!)
|
||||
const emits = defineEmits(['showSetting']);
|
||||
import { ref, h, VNode, onMounted } from 'vue';
|
||||
import { StepinHeaderAction } from 'stepin';
|
||||
import DayNightSwitch from '@/components/switch/DayNightSwitch.vue';
|
||||
@@ -301,6 +303,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 步骤1:添加 div 作为单个根元素 -->
|
||||
<StepinHeaderAction>
|
||||
<DayNightSwitch />
|
||||
</StepinHeaderAction>
|
||||
@@ -376,4 +379,10 @@ onMounted(() => {
|
||||
color: #1890ff !important;
|
||||
}
|
||||
}
|
||||
/* 新增:根容器样式(和原布局保持一致) */
|
||||
.header-actions-root {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px; // 按钮之间间距,可根据需要调整
|
||||
}
|
||||
</style>
|
||||
@@ -138,6 +138,20 @@
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-show="formState.AutoDistinct" has-feedback label="优先级" name="PriorityLevel" :wrapper-col="{ span: 10 }">
|
||||
<!-- Tag 拖拽容器 -->
|
||||
<div class="tag-drag-container">
|
||||
<!-- Tag 拖拽容器(绑定 ref 供 Sortable 初始化) -->
|
||||
<div ref="tagContainer" class="tag-list">
|
||||
<a-tag v-for="(item, index) in tagData" :key="item.id" class="sortable-tag" color="blue">
|
||||
<!-- 用 "≡" 符号替代 a-icon 作为拖拽手柄 -->
|
||||
<span class="drag-handle" style="margin-right: 6px; cursor: move;">≡</span>
|
||||
{{ item.name }}
|
||||
</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
@@ -157,13 +171,15 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, toRaw, ref, watch, onMounted, computed } from 'vue';
|
||||
import { reactive, toRaw, ref, watch, onMounted, computed, nextTick } from 'vue';
|
||||
import type { UnwrapRef } from 'vue';
|
||||
import { Form } from 'ant-design-vue';
|
||||
import type { Rule } from 'ant-design-vue/es/form';
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import { useApiStore } from '@/store';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { Sortable } from 'sortablejs';
|
||||
|
||||
import { InfoCircleOutlined, SaveOutlined, CheckOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
// 表单引用
|
||||
@@ -200,6 +216,7 @@ interface FormState {
|
||||
FollowedTitleSeparator: string; // 分隔符
|
||||
FullFollowedTitleTemplate: string; // 新增:完整模板字符串(自动生成)
|
||||
AutoDistinct: boolean;
|
||||
PriorityLevel: string;
|
||||
}
|
||||
|
||||
// 表单初始数据
|
||||
@@ -219,6 +236,7 @@ const formState: UnwrapRef<FormState> = reactive({
|
||||
FollowedTitleSeparator: '',
|
||||
FullFollowedTitleTemplate: '', // 初始为空
|
||||
AutoDistinct: false,
|
||||
PriorityLevel: '',
|
||||
});
|
||||
|
||||
// 实时计算完整模板(可选:让用户实时预览,提交时无需重复计算)
|
||||
@@ -294,9 +312,11 @@ const getConfig = () => {
|
||||
FullFollowedTitleTemplate: fullTemplate, // 回显完整模板
|
||||
ImageViedoSaveAlone: res.data.imageViedoSaveAlone,
|
||||
AutoDistinct: res.data.autoDistinct,
|
||||
PriorityLevel: res.data.priorityLevel,
|
||||
});
|
||||
|
||||
downImgVideo.value = res.data.downImageVideoFromEnv;
|
||||
tagData.value = JSON.parse(res.data.priorityLevel);
|
||||
} else {
|
||||
message.error(res.erro || '获取配置失败', 8);
|
||||
}
|
||||
@@ -319,9 +339,48 @@ const parseTemplateToArr = (templateStr: string | null | undefined) => {
|
||||
return Array.from(new Set(validPlaceholders));
|
||||
};
|
||||
|
||||
// 拖拽容器 ref
|
||||
const tagContainer = ref(null);
|
||||
let sortableInstance = ref(null);
|
||||
// 模拟 Tag 数据(替换为你的实际数据)
|
||||
const tagData = ref([
|
||||
{ id: 1, name: '喜欢的视频', sort: 1 },
|
||||
{ id: 2, name: '收藏的视频', sort: 2 },
|
||||
{ id: 3, name: '关注的视频', sort: 3 },
|
||||
]);
|
||||
|
||||
// 重新计算所有 Tag 的 sort 值(核心方法)
|
||||
const updateTagSort = () => {
|
||||
tagData.value.forEach((item, index) => {
|
||||
item.sort = index + 1; // sort = 索引+1(保证顺序和 sort 一一对应)
|
||||
});
|
||||
};
|
||||
// 组件挂载时获取配置
|
||||
onMounted(() => {
|
||||
onMounted(async () => {
|
||||
getConfig();
|
||||
// 等待 Form 组件完全渲染(关键:解决 DOM 未挂载问题)
|
||||
await nextTick();
|
||||
if (tagContainer.value) {
|
||||
sortableInstance.value = new Sortable(tagContainer.value, {
|
||||
handle: '.drag-handle',
|
||||
animation: 150,
|
||||
ghostClass: 'tag-ghost',
|
||||
preventOnFilter: true,
|
||||
onEnd: (evt) => {
|
||||
// 1. 调整 Tag 顺序
|
||||
const [movedTag] = tagData.value.splice(evt.oldIndex, 1);
|
||||
tagData.value.splice(evt.newIndex, 0, movedTag);
|
||||
|
||||
// 2. 重新计算所有 Tag 的 sort 值(关键:同步 sort 和顺序)
|
||||
updateTagSort();
|
||||
|
||||
// 打印结果(验证 sort 是否同步)
|
||||
console.log('最新 Tag 数据(含 sort):', tagData.value);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
console.warn('拖拽容器未找到,请检查 ref 绑定');
|
||||
}
|
||||
});
|
||||
|
||||
// 提交表单(核心:自动生成完整模板字符串)
|
||||
@@ -342,6 +401,7 @@ const onSubmit = () => {
|
||||
...toRaw(formState),
|
||||
FullFollowedTitleTemplate: fullTemplate, // 确保提交最新拼接结果
|
||||
FollowedTitleTemplate: formState.FollowedTitleTemplate.join(''),
|
||||
PriorityLevel: JSON.stringify(tagData.value),
|
||||
};
|
||||
|
||||
// 3. 提交接口
|
||||
@@ -440,4 +500,45 @@ const onCancel = () => {
|
||||
color: #444 !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
/* 关键修改:确保拖拽容器不限制子元素 */
|
||||
.tag-drag-container {
|
||||
position: relative; /* 必须:让拖拽的 ghost 元素不被截断 */
|
||||
overflow: visible !important; /* 覆盖 Form 可能的 overflow: hidden */
|
||||
}
|
||||
|
||||
.tag-
|
||||
/* Tag 列表布局(换行显示) */
|
||||
.tag-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px; /* Tag 之间间距 */
|
||||
}
|
||||
|
||||
/* 拖拽中的 Tag 占位样式 */
|
||||
.tag-ghost {
|
||||
opacity: 0.5;
|
||||
background-color: #e6f7ff !important;
|
||||
border-color: #91d5ff !important;
|
||||
}
|
||||
|
||||
/* 拖拽手柄样式优化 */
|
||||
.drag-handle {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
user-select: none; /* 禁止选中文字 */
|
||||
}
|
||||
|
||||
.drag-handle:hover {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
/* Tag hover 效果 */
|
||||
.sortable-tag {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.sortable-tag:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
</style>
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"dbconn": "",
|
||||
"dbtype": "Sqlite",
|
||||
"tagName": "beta_1.7.1"
|
||||
"tagName": "dev_1.0"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace dy.net.dto
|
||||
{
|
||||
public class PriorityLevelDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public int Sort { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,9 @@
|
||||
<None Update="Dockerfile">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="mp3\silent_10.mp3">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
|
||||
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace dy.net.job
|
||||
|
||||
protected override string JobType => SystemStaticUtil.DY_COLLECTS;
|
||||
|
||||
protected override VideoTypeEnum VideoType => VideoTypeEnum.Collect;
|
||||
|
||||
protected override async Task BeforeProcessCookies()
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
|
||||
@@ -18,6 +18,9 @@ namespace dy.net.job
|
||||
: base(douyinCookieService, douyinHttpClientService, douyinVideoService, douyinCommonService, douyinFollowService, douyinMergeVideoService) { }
|
||||
|
||||
protected override string JobType => SystemStaticUtil.DY_FAVORITES;
|
||||
|
||||
protected override VideoTypeEnum VideoType => VideoTypeEnum.Favorite;
|
||||
|
||||
protected override async Task<List<DouyinCookie>> GetValidCookies()
|
||||
{
|
||||
return await douyinCookieService.GetAllOpendAsync(x=> !string.IsNullOrWhiteSpace(x.FavSavePath));
|
||||
|
||||
+138
-12
@@ -84,6 +84,8 @@ namespace dy.net.job
|
||||
/// </summary>
|
||||
protected abstract string JobType { get; }
|
||||
|
||||
protected abstract VideoTypeEnum VideoType { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region 构造函数
|
||||
@@ -410,20 +412,12 @@ namespace dy.net.job
|
||||
var videos = new List<DouyinVideo>();
|
||||
foreach (var item in data.AwemeList)
|
||||
{
|
||||
//去重,检查视频是否已存在
|
||||
if (config.AutoDistinct)
|
||||
//判断是否存在视频,是否根据去重规则进行去重处理。。
|
||||
bool Goon = await AutoDistinct(config, item);
|
||||
if (!Goon)
|
||||
{
|
||||
var exitVideo = await douyinVideoService.GetByAwemeId(item.AwemeId);
|
||||
if (exitVideo != null)
|
||||
{
|
||||
if (File.Exists(exitVideo.VideoSavePath))
|
||||
{
|
||||
//Log.Debug($"视频-{exitVideo.AwemeId}-[{exitVideo.VideoTitle}]已存在,跳过");
|
||||
continue;// 已存在则跳过
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
var uper = await douyinFollowService.GetByUperId(item.AuthorUserId.ToString(), cookie.MyUserId);
|
||||
if (uper != null)
|
||||
{
|
||||
@@ -448,6 +442,138 @@ namespace dy.net.job
|
||||
return videos;
|
||||
}
|
||||
|
||||
private async Task<bool> AutoDistinct(AppConfig config, Aweme item)
|
||||
{
|
||||
// 去重,检查视频是否已存在(按优先级下载)
|
||||
if (config.AutoDistinct)
|
||||
{
|
||||
// 1. 查询数据库中是否已存在该视频(通过 AwemeId 唯一标识)
|
||||
var exitVideo = await douyinVideoService.GetByAwemeId(item.AwemeId);
|
||||
if (exitVideo != null)
|
||||
{
|
||||
// 2. 已存在视频:先判断本地文件是否存在
|
||||
if (File.Exists(exitVideo.VideoSavePath))
|
||||
{
|
||||
// 3. 解析优先级配置(前端传递的 JSON 字符串)
|
||||
List<PriorityLevelDto> priLevs = new List<PriorityLevelDto>();
|
||||
if (!string.IsNullOrWhiteSpace(config.PriorityLevel))
|
||||
{
|
||||
try
|
||||
{
|
||||
// 反序列化前端传递的优先级列表(JSON 字符串转对象)
|
||||
priLevs = JsonConvert.DeserializeObject<List<PriorityLevelDto>>(config.PriorityLevel);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"解析优先级配置失败:{ex.Message}", ex);
|
||||
priLevs = new List<PriorityLevelDto>(); // 解析失败则用默认优先级
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 处理优先级:获取「最高优先级」(Sort 越小优先级越高)
|
||||
PriorityLevelDto maxPriority = null;
|
||||
if (priLevs.Any())
|
||||
{
|
||||
// 前端已配置优先级:取 Sort 最小的(1最高)
|
||||
maxPriority = priLevs.OrderBy(x => x.Sort).FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 前端未配置:使用默认优先级(喜欢 > 收藏 > 关注)
|
||||
maxPriority = new PriorityLevelDto { Id = 1, Sort = 1, Name = "喜欢的" }; // 默认「喜欢的视频」最高
|
||||
}
|
||||
|
||||
// 5. 转换为当前上下文的视频类型
|
||||
var currentVideoType = VideoType; // 当前要下载的视频类型(如:喜欢/收藏/关注)
|
||||
var maxPriorityType = (VideoTypeEnum)maxPriority.Id; // 配置的最高优先级类型
|
||||
|
||||
// 6. 获取已存在视频的类型(从数据库中 exitVideo 读取,需确保字段存在)
|
||||
var exitVideoType = exitVideo.ViedoType; // 假设数据库存储了 VideoType(1/2/3)
|
||||
|
||||
// 7. 优先级逻辑判断(核心)
|
||||
if (currentVideoType == maxPriorityType)
|
||||
{
|
||||
// 情况1:当前要下载的是「最高优先级」视频
|
||||
if (exitVideoType == currentVideoType)
|
||||
{
|
||||
// 已存在同优先级视频 → 跳过下载(避免重复)
|
||||
Log.Debug($"视频-{exitVideo.AwemeId}-[{exitVideo.VideoTitle}]已存在(同最高优先级),跳过");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 已存在「低优先级」视频 → 替换(删除旧文件,继续下载新的最高优先级视频)
|
||||
Log.Debug($"视频-{exitVideo.AwemeId}-[{exitVideo.VideoTitle}]已存在(低优先级:{exitVideoType}),替换为最高优先级:{currentVideoType}");
|
||||
|
||||
// 删除旧的低优先级文件(可选:也可保留备份,根据需求调整)
|
||||
try
|
||||
{
|
||||
File.Delete(exitVideo.VideoSavePath);
|
||||
Log.Debug($"已删除旧文件:{exitVideo.VideoSavePath}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"删除旧文件失败:{ex.Message}", ex);
|
||||
// 即使删除失败,仍继续下载(新文件会覆盖旧文件,或按路径规则重命名)
|
||||
}
|
||||
|
||||
// 继续执行下载逻辑(覆盖旧数据)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 情况2:当前要下载的是「非最高优先级」视频
|
||||
if (exitVideoType == maxPriorityType)
|
||||
{
|
||||
// 已存在「最高优先级」视频 → 跳过(不替换最高优先级)
|
||||
Log.Debug($"视频-{exitVideo.AwemeId}-[{exitVideo.VideoTitle}]已存在最高优先级视频({maxPriorityType}),当前类型({currentVideoType})优先级低,跳过");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 已存在「其他非最高优先级」视频 → 比较两者优先级
|
||||
// 获取当前类型和已存在类型的 Sort 值
|
||||
var currentSort = priLevs.FirstOrDefault(x => x.Id == (int)currentVideoType)?.Sort ?? int.MaxValue;
|
||||
var exitSort = priLevs.FirstOrDefault(x => x.Id == (int)exitVideoType)?.Sort ?? int.MaxValue;
|
||||
|
||||
if (currentSort < exitSort)
|
||||
{
|
||||
// 当前类型优先级更高 → 替换旧视频
|
||||
Log.Debug($"视频-{exitVideo.AwemeId}-[{exitVideo.VideoTitle}]已存在低优先级视频({exitVideoType}),替换为当前优先级:{currentVideoType}");
|
||||
|
||||
// 删除旧文件
|
||||
if (File.Exists(exitVideo.VideoSavePath))
|
||||
{
|
||||
File.Delete(exitVideo.VideoSavePath);
|
||||
}
|
||||
|
||||
// 继续下载
|
||||
}
|
||||
else
|
||||
{
|
||||
// 当前类型优先级更低或相等 → 跳过
|
||||
Log.Debug($"视频-{exitVideo.AwemeId}-[{exitVideo.VideoTitle}]已存在更高/同等优先级视频({exitVideoType}),当前类型({currentVideoType})跳过");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 数据库存在记录,但本地文件丢失 → 直接重新下载(无论优先级)
|
||||
Log.Debug($"视频-{exitVideo.AwemeId}-[{exitVideo.VideoTitle}]数据库存在但本地文件丢失,重新下载");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 数据库和本地均无该视频 → 直接下载
|
||||
//Log.Debug($"视频-{item.AwemeId}-[{item.Desc}]未存在,开始下载");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理单个视频
|
||||
/// 负责下载视频、封面、头像,生成NFO文件,创建视频实体等
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace dy.net.job
|
||||
|
||||
protected override string JobType => SystemStaticUtil.DY_FOLLOWEDS;
|
||||
|
||||
protected override VideoTypeEnum VideoType => VideoTypeEnum.UperPost;
|
||||
|
||||
protected override async Task<List<DouyinCookie>> GetValidCookies()
|
||||
{
|
||||
return await douyinCookieService.GetAllOpendAsync(x => !string.IsNullOrWhiteSpace(x.UpSavePath));
|
||||
|
||||
+9
-1
@@ -1,4 +1,5 @@
|
||||
using SqlSugar;
|
||||
using Newtonsoft.Json;
|
||||
using SqlSugar;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace dy.net.model
|
||||
@@ -73,5 +74,12 @@ namespace dy.net.model
|
||||
/// 自动去重-逻辑是遇到相同ID的视频直接跳过
|
||||
/// </summary>
|
||||
public bool AutoDistinct { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 去重优先级配置,json字符串格式存储
|
||||
/// </summary>
|
||||
[SugarColumn(Length = 500, IsNullable = true)]
|
||||
public string PriorityLevel { get; set; }// = "[{\"id\":1,\"name\":\"喜欢的视频\",\"sort\":1},{\"id\":2,\"name\":\"收藏的视频\",\"sort\":2},{\"id\":3,\"name\":\"关注的视频\",\"sort\":3}]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -31,6 +31,12 @@ namespace dy.net.service
|
||||
var conf = GetConfig();
|
||||
if (conf != null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(conf.PriorityLevel))
|
||||
{
|
||||
conf.PriorityLevel = "[{\"id\":1,\"name\":\"喜欢的视频\",\"sort\":1},{\"id\":2,\"name\":\"收藏的视频\",\"sort\":2},{\"id\":3,\"name\":\"关注的视频\",\"sort\":3}]";
|
||||
sqlSugarClient.Updateable(conf).ExecuteCommand();
|
||||
//兼容旧版本
|
||||
}
|
||||
return conf;
|
||||
}
|
||||
else
|
||||
@@ -50,7 +56,8 @@ namespace dy.net.service
|
||||
FollowedTitleTemplate = "",
|
||||
FullFollowedTitleTemplate = "",
|
||||
FollowedTitleSeparator = "",
|
||||
AutoDistinct = true//默认开启
|
||||
AutoDistinct = true,//默认开启
|
||||
PriorityLevel= "[{\"id\":1,\"name\":\"喜欢的视频\",\"sort\":1},{\"id\":2,\"name\":\"收藏的视频\",\"sort\":2},{\"id\":3,\"name\":\"关注的视频\",\"sort\":3}]"
|
||||
};
|
||||
sqlSugarClient.Insertable(config).ExecuteCommand();
|
||||
return config;
|
||||
@@ -83,6 +90,7 @@ namespace dy.net.service
|
||||
cc.ImageViedoSaveAlone = config.ImageViedoSaveAlone;
|
||||
cc.FollowedTitleSeparator = config.FollowedTitleSeparator;
|
||||
cc.AutoDistinct = config.AutoDistinct;
|
||||
cc.PriorityLevel = config.PriorityLevel;
|
||||
}
|
||||
|
||||
int update = await sqlSugarClient.Updateable(cc).ExecuteCommandAsync();
|
||||
|
||||
@@ -177,11 +177,11 @@ namespace dy.net.service
|
||||
Log.Error("图片URL列表为空,无法合成视频");
|
||||
return false;
|
||||
}
|
||||
if (mergeImg2Viedo && (request.AudioUrls == null || request.AudioUrls.Count == 0))
|
||||
{
|
||||
Log.Error("合成视频时音频URL列表为空");
|
||||
return false;
|
||||
}
|
||||
//if (mergeImg2Viedo && (request.AudioUrls == null || request.AudioUrls.Count == 0))
|
||||
//{
|
||||
// Log.Error("合成视频时音频URL列表为空");
|
||||
// return false;
|
||||
//}
|
||||
|
||||
string tempDir = null;
|
||||
try
|
||||
@@ -206,7 +206,7 @@ namespace dy.net.service
|
||||
|
||||
// 2. 下载音频
|
||||
string[] rawAudios = Array.Empty<string>();
|
||||
if (mergeImg2Viedo)
|
||||
if (mergeImg2Viedo&& request.AudioUrls != null && request.AudioUrls.Count>0)
|
||||
{
|
||||
var (audios, audioError) = await DownloadMediaAsync(
|
||||
request.AudioUrls, Path.Combine(tempDir, "raw-audios"), "audio_", "mp3", cookie);
|
||||
@@ -257,6 +257,13 @@ namespace dy.net.service
|
||||
// 超时控制:避免 FFmpeg 进程无限运行(300秒=5分钟)
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(300));
|
||||
// 执行合成(确保每个图片都参与)
|
||||
|
||||
if (rawAudios.Length == 0)
|
||||
{
|
||||
rawAudios= new string[] { Path.Combine(AppContext.BaseDirectory,"mp3", "silent_10.mp3") };
|
||||
Log.Debug("未检测到音频,使用默认静音音频-无声");
|
||||
}
|
||||
|
||||
string resultPath = await ffmpegHelper.CreateVideoFromImagesAndAudioAsync(
|
||||
rawImages, // 所有下载的图片(确保无遗漏)
|
||||
rawAudios[0], // 取第一个音频(可根据需求调整)
|
||||
|
||||
Reference in New Issue
Block a user