From 07d5f421433a9015635641c4292bccfdaecaa67e Mon Sep 17 00:00:00 2001 From: root Date: Sat, 3 Oct 2026 23:20:29 +0800 Subject: [PATCH] =?UTF-8?q?feat(download):=20=E4=B8=8B=E8=BD=BD=E5=99=A8?= =?UTF-8?q?=E5=8F=AF=E8=AE=BE=E3=80=8CH&R=20=E6=9C=AA=E7=9F=A5=E4=B9=9F?= =?UTF-8?q?=E5=8F=AF=E6=B8=85=E7=90=86=E3=80=8D=EF=BC=88M2=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 下载器策略加 hnrUnknownCleanable(缺省关,保持现状):保种天数到期、站点 H&R 要求未知 (M-Team、接管任务、站点没配 H&R)的任务视为可清理;H&R 明确未满足的不受影响。 空间不足事项里写明有多少任务因 H&R 未知没清理、可在哪里放行。 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei --- .../services/downloads.ts | 17 +++++++++++++---- .../web/app/components/settings/instances.tsx | 19 +++++++++++++++---- packages/contracts/src/instances.ts | 2 ++ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/apps/server/src/modules/acquisition-organize/services/downloads.ts b/apps/server/src/modules/acquisition-organize/services/downloads.ts index 47223fc9..bcb93a35 100644 --- a/apps/server/src/modules/acquisition-organize/services/downloads.ts +++ b/apps/server/src/modules/acquisition-organize/services/downloads.ts @@ -1272,8 +1272,9 @@ export async function cleanupCandidates(d: AoDeps, v: VolumeState): Promise th.cleanupTargetPercent && pct >= th.hardThresholdPercent) { entry.action = entry.removed.length > 0 ? "cleaned" : "insufficient"; // 没有可清理对象:待处理 + 阈值继续阻止新下载(不突破保留要求) + const hnrUnknown = entry.skipped.filter((x) => x.reason === "hnr_unknown").length; await upsertDecision(d, db, { kind: "space_insufficient", severity: "critical", title: `下载缓存卷 ${v.volumeKey} 空间不足`, - summary: `使用率 ${pct.toFixed(1)}%,可清理任务已用尽;新下载已暂停`, + summary: + `使用率 ${pct.toFixed(1)}%,可清理任务已用尽;新下载已暂停` + + (hnrUnknown + ? `。另有 ${hnrUnknown} 个任务因为 H&R 要求未知没有清理,确认没有 H&R 要求的话,可在下载器设置里打开「H&R 未知也可清理」` + : ""), subjectType: "cache_volume", dedupeKey: `space_insufficient:${v.volumeKey}`, evidence: { usedPercent: pct, locked: entry.skipped }, diff --git a/apps/web/app/components/settings/instances.tsx b/apps/web/app/components/settings/instances.tsx index f017896a..e9f6b474 100644 --- a/apps/web/app/components/settings/instances.tsx +++ b/apps/web/app/components/settings/instances.tsx @@ -97,16 +97,20 @@ const POLICY_FIELDS = [ ["targetPct", "cleanupTargetPercent"], ["seedDays", "seedRetentionDays"], ] as const; -function policyForm(r: DownloaderInstance): Record { +function policyForm(r: DownloaderInstance): Record { const p = ((r.settings as { policy?: Record }).policy ?? {}) as Record; - return Object.fromEntries(POLICY_FIELDS.map(([f, k]) => [f, typeof p[k] === "number" ? String(p[k]) : ""])); + return { + ...Object.fromEntries(POLICY_FIELDS.map(([f, k]) => [f, typeof p[k] === "number" ? String(p[k]) : ""])), + hnrUnknownCleanable: p.hnrUnknownCleanable === true, + }; } -function policyBody(v: FormValues): Record { - const out: Record = {}; +function policyBody(v: FormValues): Record { + const out: Record = {}; for (const [f, k] of POLICY_FIELDS) { const raw = String(v[f] ?? "").trim(); if (raw !== "" && Number.isFinite(Number(raw))) out[k] = Number(raw); } + if (v.hnrUnknownCleanable === true) out.hnrUnknownCleanable = true; return out; } @@ -202,6 +206,12 @@ function DownloadersTable() { placeholder: "留空用全局", hint: "入库后至少保留几天再允许清理;公网 BT 可填 0(站点 H&R 要求仍然遵守)", }, + { + name: "hnrUnknownCleanable", + label: "H&R 未知也可清理", + kind: "switch", + hint: "保种天数到期后,没给出 H&R 要求的任务(如 M-Team、接管的任务、站点没配 H&R)也允许清理;默认不清理", + }, { name: "isDefault", label: "设为默认下载器", @@ -223,6 +233,7 @@ function DownloadersTable() { resumePct: "", targetPct: "", seedDays: "", + hnrUnknownCleanable: false, isDefault: false, enabled: true, }, diff --git a/packages/contracts/src/instances.ts b/packages/contracts/src/instances.ts index 1be15e8d..2c65dea0 100644 --- a/packages/contracts/src/instances.ts +++ b/packages/contracts/src/instances.ts @@ -68,6 +68,8 @@ export const DownloaderPolicy = z resumePercent: z.number().min(1).max(100).optional(), cleanupTargetPercent: z.number().min(1).max(100).optional(), seedRetentionDays: z.number().int().min(0).max(365).optional(), + /** 保种天数到期、但站点 H&R 要求未知(M-Team、接管任务、站点没配 H&R)时也允许清理;缺省不允许 */ + hnrUnknownCleanable: z.boolean().optional(), }) .strict(); export type DownloaderPolicy = z.infer;