From 95bdeb6ebf2b8ac1a9ece8545fde809f814c3aac Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sat, 22 Aug 2026 11:46:41 +0800 Subject: [PATCH] feat(settings): configure transfer failure aggregation --- src/locales/en-US.ts | 3 +++ src/locales/zh-CN.ts | 3 +++ src/locales/zh-TW.ts | 3 +++ src/views/setting/AccountSettingSystem.vue | 9 +++++++++ .../__tests__/AccountSettingSystem.spec.ts | 15 +++++++++++++-- 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index 18cb21c0..47a0651c 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -2276,6 +2276,9 @@ export default { transferMaxFailedRetries: 'Transfer Retries on Failure', transferMaxFailedRetriesHint: 'Maximum automatic retries after a failed transfer, so a transient network or recognition glitch cannot permanently skip a file. Once exhausted, transfer it manually or delete the transfer history. Range 1-10; the counter resets on a successful transfer or when the record is deleted. Takes effect immediately after saving.', + transferFailureNotificationAggregation: 'Aggregate Transfer Failure Notifications by Media', + transferFailureNotificationAggregationHint: + 'Combine transfer failures for the same media and season within a short window, including the failure count, reason summary, and a batch-processing link. Disable to send one notification per file.', fsProxyEnabled: 'Isolate Local File Operations', fsProxyEnabledHint: 'Run local file reads, copies, moves and deletes in a separate subprocess. When a FUSE mount such as CloudDrive2 stops responding, these calls hang forever and cannot be interrupted, which in turn stalls the transfer queue and directory monitoring. Isolated, they can be force-reclaimed after a timeout and surface as ordinary errors that the existing retry logic handles. Disable to fall back to direct calls, matching the previous behaviour.', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index c0df10d3..d200d0d6 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -2237,6 +2237,9 @@ export default { transferMaxFailedRetries: '整理失败重试次数', transferMaxFailedRetriesHint: '自动整理失败后允许重试的最大次数,避免一次网络或识别抖动让文件永久漏整理;次数用尽后需手动整理或删除整理记录。取值 1-10,整理成功或删除记录时计数清零,保存后即时生效', + transferFailureNotificationAggregation: '按媒体聚合整理失败通知', + transferFailureNotificationAggregationHint: + '开启后,同一媒体同一季在短时间内产生的整理失败会合并发送,并展示失败数量、原因统计和批量处理入口;关闭时逐条发送', fsProxyEnabled: '本地文件操作隔离', fsProxyEnabledHint: '在独立子进程中执行本地文件的读取、复制、移动与删除。CloudDrive2 等 FUSE 挂载失去响应时,这类操作会永久卡住且无法中断,进而拖死整理队列与目录监控;隔离后可在超时后强制回收并转为普通错误,由既有的重试机制接管。关闭后退回直接调用,行为与旧版一致', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index 2f4e24cd..46af9dcf 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -2236,6 +2236,9 @@ export default { transferMaxFailedRetries: '整理失敗重試次數', transferMaxFailedRetriesHint: '自動整理失敗後允許重試的最大次數,避免一次網路或辨識抖動讓檔案永久漏整理;次數用盡後需手動整理或刪除整理記錄。取值 1-10,整理成功或刪除記錄時計數歸零,保存後即時生效', + transferFailureNotificationAggregation: '按媒體聚合整理失敗通知', + transferFailureNotificationAggregationHint: + '開啟後,同一媒體同一季在短時間內產生的整理失敗會合併發送,並顯示失敗數量、原因統計和批量處理入口;關閉時逐條發送', fsProxyEnabled: '本機檔案操作隔離', fsProxyEnabledHint: '在獨立子行程中執行本機檔案的讀取、複製、移動與刪除。CloudDrive2 等 FUSE 掛載失去回應時,這類操作會永久卡住且無法中斷,進而拖死整理佇列與目錄監控;隔離後可在逾時後強制回收並轉為一般錯誤,由既有的重試機制接手。關閉後退回直接呼叫,行為與舊版一致', diff --git a/src/views/setting/AccountSettingSystem.vue b/src/views/setting/AccountSettingSystem.vue index feb03c46..4a26c904 100644 --- a/src/views/setting/AccountSettingSystem.vue +++ b/src/views/setting/AccountSettingSystem.vue @@ -145,6 +145,7 @@ const SystemSettings = ref({ TRANSFER_THREADS: 1, MONITOR_NETWORK_FAST_MODE: false, TRANSFER_MAX_FAILED_RETRIES: 3, + TRANSFER_FAILURE_NOTIFICATION_AGGREGATION: false, FS_PROXY_ENABLED: true, FS_PROXY_TIMEOUT: 30, FS_PROXY_STALL_TIMEOUT: 120, @@ -2712,6 +2713,14 @@ watch(currentLlmSnapshotKey, (snapshotKey, previousSnapshotKey) => { prepend-inner-icon="mdi-refresh-alert" /> + + + { }) it('round-trips every advanced laboratory control when Rust is available', async () => { - systemEnv.RUST_ACCEL_AVAILABLE = true + Object.assign(systemEnv, { + RUST_ACCEL_AVAILABLE: true, + TRANSFER_FAILURE_NOTIFICATION_AGGREGATION: true, + }) await renderSettings() const dialog = await openAdvancedTab('实验室') await fireEvent.update(dialog.getByLabelText('本地插件仓库路径'), '/plugins/local') @@ -1202,7 +1205,14 @@ describe('AccountSettingSystem', () => { await fireEvent.update(dialog.getByLabelText('整理失败重试次数'), '6') await fireEvent.update(dialog.getByLabelText('文件操作超时(秒)'), '45') await fireEvent.update(dialog.getByLabelText('文件复制停滞超时(秒)'), '180') - for (const label of ['插件热加载', '本地文件操作隔离', '编码探测性能模式', 'Rust 加速', '网络存储快速监控']) { + for (const label of [ + '插件热加载', + '按媒体聚合整理失败通知', + '本地文件操作隔离', + '编码探测性能模式', + 'Rust 加速', + '网络存储快速监控', + ]) { await fireEvent.click(dialog.getByLabelText(label)) } await fireEvent.click(dialog.getByRole('button', { name: '保存' })) @@ -1218,6 +1228,7 @@ describe('AccountSettingSystem', () => { PLUGIN_AUTO_RELOAD: true, PLUGIN_LOCAL_REPO_PATHS: '/plugins/local', RUST_ACCEL: true, + TRANSFER_FAILURE_NOTIFICATION_AGGREGATION: false, TRANSFER_MAX_FAILED_RETRIES: 6, TRANSFER_THREADS: 4, }),