feat: 支持手动触发全量更新,清除本地多余的视频条目与文件 (#678)

This commit is contained in:
ᴀᴍᴛᴏᴀᴇʀ
2026-03-16 02:50:55 +08:00
committed by GitHub
parent 980779d5c5
commit 29f36238e3
7 changed files with 228 additions and 7 deletions

View File

@@ -6,6 +6,8 @@ import type {
Config,
DashBoardResponse,
FavoritesResponse,
FullSyncVideoSourceRequest,
FullSyncVideoSourceResponse,
QrcodeGenerateResponse as GenerateQrcodeResponse,
InsertCollectionRequest,
InsertFavoriteRequest,
@@ -253,6 +255,14 @@ class ApiClient {
return this.post<boolean>(`/video-sources/${type}/${id}/evaluate`, null);
}
async fullSyncVideoSource(
type: string,
id: number,
data: FullSyncVideoSourceRequest
): Promise<ApiResponse<FullSyncVideoSourceResponse>> {
return this.post<FullSyncVideoSourceResponse>(`/video-sources/${type}/${id}/full-sync`, data);
}
async getDefaultPath(type: string, name: string): Promise<ApiResponse<string>> {
return this.get<string>(`/video-sources/${type}/default-path`, { name });
}
@@ -327,6 +337,8 @@ const api = {
removeVideoSource: (type: string, id: number) => apiClient.removeVideoSource(type, id),
evaluateVideoSourceRules: (type: string, id: number) =>
apiClient.evaluateVideoSourceRules(type, id),
fullSyncVideoSource: (type: string, id: number, data: { delete_local: boolean }) =>
apiClient.fullSyncVideoSource(type, id, data),
getDefaultPath: (type: string, name: string) => apiClient.getDefaultPath(type, name),
testNotifier: (notifier: Notifier) => apiClient.testNotifier(notifier),
getConfig: () => apiClient.getConfig(),

View File

@@ -89,7 +89,16 @@ export interface UpdateFilteredVideoStatusResponse {
export interface ApiError {
message: string;
status?: number;
status: number;
}
export interface FullSyncVideoSourceRequest {
delete_local: boolean;
}
export interface FullSyncVideoSourceResponse {
removed_count: number;
warnings?: string[];
}
export interface StatusUpdate {

View File

@@ -18,7 +18,8 @@
InfoIcon,
Trash2Icon,
CircleCheckBigIcon,
CircleXIcon
CircleXIcon,
RefreshCwIcon
} from '@lucide/svelte/icons';
import * as Tooltip from '$lib/components/ui/tooltip/index.js';
import { toast } from 'svelte-sonner';
@@ -58,6 +59,13 @@
let removeIdx: number = 0;
let removing = false;
// 全量更新对话框状态
let showFullSyncDialog = false;
let fullSyncSource: VideoSourceDetail | null = null;
let fullSyncType = '';
let fullSyncDeleteLocal = false;
let fullSyncing = false;
// 编辑表单数据
let editForm = {
path: '',
@@ -120,6 +128,44 @@
showRemoveDialog = true;
}
function openFullSyncDialog(type: string, source: VideoSourceDetail) {
fullSyncSource = source;
fullSyncType = type;
fullSyncDeleteLocal = false;
showFullSyncDialog = true;
}
async function fullSyncVideoSource() {
if (!fullSyncSource) return;
fullSyncing = true;
try {
let response = await api.fullSyncVideoSource(fullSyncType, fullSyncSource.id, {
delete_local: fullSyncDeleteLocal
});
if (response && response.data) {
showFullSyncDialog = false;
toast.success('全量更新成功', {
description: `已移除 ${response.data.removed_count} 个不存在的视频`
});
if (response.data.warnings && response.data.warnings.length > 0) {
toast.warning('部分本地文件夹删除失败', {
description: response.data.warnings.join('\n'),
duration: 10000,
descriptionClass: 'whitespace-pre-line'
});
}
} else {
toast.error('全量更新失败');
}
} catch (error) {
toast.error('全量更新失败', {
description: (error as ApiError).message
});
} finally {
fullSyncing = false;
}
}
// 保存编辑
async function saveEdit() {
if (!editingSource) return;
@@ -410,6 +456,21 @@
<p class="text-xs">重新评估规则</p>
</Tooltip.Content>
</Tooltip.Root>
<Tooltip.Root disableHoverableContent={true}>
<Tooltip.Trigger>
<Button
size="sm"
variant="outline"
onclick={() => openFullSyncDialog(key, source)}
class="h-8 w-8 p-0"
>
<RefreshCwIcon class="h-3 w-3" />
</Button>
</Tooltip.Trigger>
<Tooltip.Content>
<p class="text-xs">全量更新视频</p>
</Tooltip.Content>
</Tooltip.Root>
{#if activeTab !== 'watch_later'}
<Tooltip.Root disableHoverableContent={true}>
<Tooltip.Trigger>
@@ -581,6 +642,44 @@
</AlertDialog.Content>
</AlertDialog.Root>
<AlertDialog.Root bind:open={showFullSyncDialog}>
<AlertDialog.Content>
<AlertDialog.Header>
<AlertDialog.Title>全量更新视频</AlertDialog.Title>
<AlertDialog.Description>
确定要全量更新视频源 <strong>"{fullSyncSource?.name}"</strong> 吗?<br />
该操作会拉取该视频源下所有当前存在的视频,移除数据库中已不存在于该源的视频及其分页数据,<span
class="text-destructive font-medium">无法撤销</span
><br />
</AlertDialog.Description>
</AlertDialog.Header>
<div class="flex items-center space-x-2 py-2">
<input
type="checkbox"
id="delete-local"
bind:checked={fullSyncDeleteLocal}
class="text-primary focus:ring-primary h-4 w-4 rounded border-gray-300"
/>
<label for="delete-local" class="text-sm font-medium"> 同时删除本地视频文件夹 </label>
</div>
<AlertDialog.Footer>
<AlertDialog.Cancel
disabled={fullSyncing}
onclick={() => {
showFullSyncDialog = false;
}}>取消</AlertDialog.Cancel
>
<AlertDialog.Action
onclick={fullSyncVideoSource}
disabled={fullSyncing}
class="bg-amber-600 hover:bg-amber-700"
>
{fullSyncing ? '全量更新中' : '确认全量更新'}
</AlertDialog.Action>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog.Root>
<!-- 添加对话框 -->
<Dialog.Root bind:open={showAddDialog}>
<Dialog.Content>