feat(settings): configure transfer failure aggregation

This commit is contained in:
jxxghp
2026-08-22 11:46:41 +08:00
parent 19e8e06798
commit 95bdeb6ebf
5 changed files with 31 additions and 2 deletions
+3
View File
@@ -2276,6 +2276,9 @@ export default {
transferMaxFailedRetries: 'Transfer Retries on Failure', transferMaxFailedRetries: 'Transfer Retries on Failure',
transferMaxFailedRetriesHint: 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.', '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', fsProxyEnabled: 'Isolate Local File Operations',
fsProxyEnabledHint: 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.', '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.',
+3
View File
@@ -2237,6 +2237,9 @@ export default {
transferMaxFailedRetries: '整理失败重试次数', transferMaxFailedRetries: '整理失败重试次数',
transferMaxFailedRetriesHint: transferMaxFailedRetriesHint:
'自动整理失败后允许重试的最大次数,避免一次网络或识别抖动让文件永久漏整理;次数用尽后需手动整理或删除整理记录。取值 1-10,整理成功或删除记录时计数清零,保存后即时生效', '自动整理失败后允许重试的最大次数,避免一次网络或识别抖动让文件永久漏整理;次数用尽后需手动整理或删除整理记录。取值 1-10,整理成功或删除记录时计数清零,保存后即时生效',
transferFailureNotificationAggregation: '按媒体聚合整理失败通知',
transferFailureNotificationAggregationHint:
'开启后,同一媒体同一季在短时间内产生的整理失败会合并发送,并展示失败数量、原因统计和批量处理入口;关闭时逐条发送',
fsProxyEnabled: '本地文件操作隔离', fsProxyEnabled: '本地文件操作隔离',
fsProxyEnabledHint: fsProxyEnabledHint:
'在独立子进程中执行本地文件的读取、复制、移动与删除。CloudDrive2 等 FUSE 挂载失去响应时,这类操作会永久卡住且无法中断,进而拖死整理队列与目录监控;隔离后可在超时后强制回收并转为普通错误,由既有的重试机制接管。关闭后退回直接调用,行为与旧版一致', '在独立子进程中执行本地文件的读取、复制、移动与删除。CloudDrive2 等 FUSE 挂载失去响应时,这类操作会永久卡住且无法中断,进而拖死整理队列与目录监控;隔离后可在超时后强制回收并转为普通错误,由既有的重试机制接管。关闭后退回直接调用,行为与旧版一致',
+3
View File
@@ -2236,6 +2236,9 @@ export default {
transferMaxFailedRetries: '整理失敗重試次數', transferMaxFailedRetries: '整理失敗重試次數',
transferMaxFailedRetriesHint: transferMaxFailedRetriesHint:
'自動整理失敗後允許重試的最大次數,避免一次網路或辨識抖動讓檔案永久漏整理;次數用盡後需手動整理或刪除整理記錄。取值 1-10,整理成功或刪除記錄時計數歸零,保存後即時生效', '自動整理失敗後允許重試的最大次數,避免一次網路或辨識抖動讓檔案永久漏整理;次數用盡後需手動整理或刪除整理記錄。取值 1-10,整理成功或刪除記錄時計數歸零,保存後即時生效',
transferFailureNotificationAggregation: '按媒體聚合整理失敗通知',
transferFailureNotificationAggregationHint:
'開啟後,同一媒體同一季在短時間內產生的整理失敗會合併發送,並顯示失敗數量、原因統計和批量處理入口;關閉時逐條發送',
fsProxyEnabled: '本機檔案操作隔離', fsProxyEnabled: '本機檔案操作隔離',
fsProxyEnabledHint: fsProxyEnabledHint:
'在獨立子行程中執行本機檔案的讀取、複製、移動與刪除。CloudDrive2 等 FUSE 掛載失去回應時,這類操作會永久卡住且無法中斷,進而拖死整理佇列與目錄監控;隔離後可在逾時後強制回收並轉為一般錯誤,由既有的重試機制接手。關閉後退回直接呼叫,行為與舊版一致', '在獨立子行程中執行本機檔案的讀取、複製、移動與刪除。CloudDrive2 等 FUSE 掛載失去回應時,這類操作會永久卡住且無法中斷,進而拖死整理佇列與目錄監控;隔離後可在逾時後強制回收並轉為一般錯誤,由既有的重試機制接手。關閉後退回直接呼叫,行為與舊版一致',
@@ -145,6 +145,7 @@ const SystemSettings = ref<any>({
TRANSFER_THREADS: 1, TRANSFER_THREADS: 1,
MONITOR_NETWORK_FAST_MODE: false, MONITOR_NETWORK_FAST_MODE: false,
TRANSFER_MAX_FAILED_RETRIES: 3, TRANSFER_MAX_FAILED_RETRIES: 3,
TRANSFER_FAILURE_NOTIFICATION_AGGREGATION: false,
FS_PROXY_ENABLED: true, FS_PROXY_ENABLED: true,
FS_PROXY_TIMEOUT: 30, FS_PROXY_TIMEOUT: 30,
FS_PROXY_STALL_TIMEOUT: 120, FS_PROXY_STALL_TIMEOUT: 120,
@@ -2712,6 +2713,14 @@ watch(currentLlmSnapshotKey, (snapshotKey, previousSnapshotKey) => {
prepend-inner-icon="mdi-refresh-alert" prepend-inner-icon="mdi-refresh-alert"
/> />
</VCol> </VCol>
<VCol cols="12" md="6">
<VSwitch
v-model="SystemSettings.Advanced.TRANSFER_FAILURE_NOTIFICATION_AGGREGATION"
:label="t('setting.system.transferFailureNotificationAggregation')"
:hint="t('setting.system.transferFailureNotificationAggregationHint')"
persistent-hint
/>
</VCol>
<VCol v-if="SystemSettings.Advanced.FS_PROXY_ENABLED" cols="12" md="6"> <VCol v-if="SystemSettings.Advanced.FS_PROXY_ENABLED" cols="12" md="6">
<VTextField <VTextField
v-model.number="SystemSettings.Advanced.FS_PROXY_TIMEOUT" v-model.number="SystemSettings.Advanced.FS_PROXY_TIMEOUT"
@@ -1194,7 +1194,10 @@ describe('AccountSettingSystem', () => {
}) })
it('round-trips every advanced laboratory control when Rust is available', async () => { 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() await renderSettings()
const dialog = await openAdvancedTab('实验室') const dialog = await openAdvancedTab('实验室')
await fireEvent.update(dialog.getByLabelText('本地插件仓库路径'), '/plugins/local') await fireEvent.update(dialog.getByLabelText('本地插件仓库路径'), '/plugins/local')
@@ -1202,7 +1205,14 @@ describe('AccountSettingSystem', () => {
await fireEvent.update(dialog.getByLabelText('整理失败重试次数'), '6') await fireEvent.update(dialog.getByLabelText('整理失败重试次数'), '6')
await fireEvent.update(dialog.getByLabelText('文件操作超时(秒)'), '45') await fireEvent.update(dialog.getByLabelText('文件操作超时(秒)'), '45')
await fireEvent.update(dialog.getByLabelText('文件复制停滞超时(秒)'), '180') 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.getByLabelText(label))
} }
await fireEvent.click(dialog.getByRole('button', { name: '保存' })) await fireEvent.click(dialog.getByRole('button', { name: '保存' }))
@@ -1218,6 +1228,7 @@ describe('AccountSettingSystem', () => {
PLUGIN_AUTO_RELOAD: true, PLUGIN_AUTO_RELOAD: true,
PLUGIN_LOCAL_REPO_PATHS: '/plugins/local', PLUGIN_LOCAL_REPO_PATHS: '/plugins/local',
RUST_ACCEL: true, RUST_ACCEL: true,
TRANSFER_FAILURE_NOTIFICATION_AGGREGATION: false,
TRANSFER_MAX_FAILED_RETRIES: 6, TRANSFER_MAX_FAILED_RETRIES: 6,
TRANSFER_THREADS: 4, TRANSFER_THREADS: 4,
}), }),