merge: fix/h-visibility
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
commit
385933968f
20 files changed
+1045
-100
No files matched your search
@@ -120,6 +120,11 @@ export interface AddTorrentRequest {
|
||||
category?: string;
|
||||
tags?: string[];
|
||||
paused?: boolean;
|
||||
/**
|
||||
* qB ≥ 4.5 的停止条件:MetadataReceived = 磁力拿到元数据后自动暂停(暂停添加的磁力不会去取元数据);
|
||||
* 旧版本忽略该参数、照常开始下载
|
||||
*/
|
||||
stopCondition?: "MetadataReceived" | "FilesChecked";
|
||||
rename?: string;
|
||||
contentLayout?: "Original" | "Subfolder" | "NoSubfolder";
|
||||
sequential?: boolean;
|
||||
@@ -516,6 +521,7 @@ export class QbClient implements DownloaderClient {
|
||||
const paused = req.paused ? "true" : "false";
|
||||
form.append("paused", paused); // <= 4.x
|
||||
form.append("stopped", paused); // >= 5.x
|
||||
if (req.stopCondition) form.append("stopCondition", req.stopCondition);
|
||||
if (req.rename) form.append("rename", req.rename);
|
||||
form.append("contentLayout", req.contentLayout ?? "Original");
|
||||
form.append("autoTMM", "false");
|
||||
|
||||
@@ -149,6 +149,10 @@ export const DecisionKind = z.enum([
|
||||
"identity_unresolved",
|
||||
/** 资源已绑定作品但集数待确认(无法换算 / 映射规则失效):按作品 + 来源季 + 发布组合并,附建议规则 */
|
||||
"episode_mapping",
|
||||
/** 下载器连续不可达超过一段时间(按实例去重,恢复后自动关闭) */
|
||||
"downloader_offline",
|
||||
/** 下载目录读不到容量(路径映射 / 挂载问题,按卷去重,恢复后自动关闭) */
|
||||
"cache_volume_unknown",
|
||||
]);
|
||||
export type DecisionKind = z.infer<typeof DecisionKind>;
|
||||
export const DecisionStatus = z.enum(["open", "investigating", "awaiting_user", "resolved", "dismissed", "expired"]);
|
||||
|
||||
@@ -70,7 +70,8 @@ export type DownloadBoardItem = z.infer<typeof DownloadBoardItem>;
|
||||
* automation_off = 自动化总开关关闭 / 只读对账;queued = 条件已满足、等提交任务执行。
|
||||
* 每日下载额度在生成计划时核对,超额不生成计划,所以没有"等额度"的计划。
|
||||
*/
|
||||
export const PlanWait = z.enum(["approval", "space", "automation_off", "queued"]);
|
||||
/** volume_unknown:下载目录读不到容量(配置问题,不是空间不够) */
|
||||
export const PlanWait = z.enum(["approval", "space", "volume_unknown", "automation_off", "queued"]);
|
||||
export type PlanWait = z.infer<typeof PlanWait>;
|
||||
|
||||
/** 还没提交到下载器的计划,作为「进行中」的一行(id 是计划 id) */
|
||||
|
||||
@@ -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<typeof DownloaderPolicy>;
|
||||
|
||||
@@ -66,7 +66,7 @@ const REASONS: Record<string, ReasonText> = {
|
||||
not_found: { text: "没找到" },
|
||||
precondition: { text: "前置条件不满足" },
|
||||
internal: { text: "内部错误" },
|
||||
volume_state_unknown: { text: "下载盘用量未知", action: "在下载面板检查下载盘" },
|
||||
volume_state_unknown: { text: "下载目录读不到容量", action: "检查下载器的路径映射和容器挂载" },
|
||||
hard_threshold_reached: { text: "下载盘已到暂停线" },
|
||||
projected_over_threshold: { text: "下完会超过下载盘暂停线" },
|
||||
awaiting_resume_watermark: { text: "等下载盘降到恢复线" },
|
||||
@@ -85,6 +85,8 @@ const REASONS: Record<string, ReasonText> = {
|
||||
download_failed: { text: "下载失败" },
|
||||
download_stalled: { text: "下载无进展" },
|
||||
space_insufficient: { text: "空间不足" },
|
||||
downloader_offline: { text: "下载器连不上" },
|
||||
cache_volume_unknown: { text: "下载目录读不到容量" },
|
||||
unidentifiable: { text: "没识别出作品" },
|
||||
user_deleted: { text: "已删除" },
|
||||
// DL-3:还没提交就结束或在等的下载(计划的 statusReason、提交任务的延后原因)
|
||||
|
||||
@@ -20,7 +20,7 @@ describe("reasonText(DEAI-2 原因码人话化)", () => {
|
||||
|
||||
it("智能体汇报里的码(FIX-7):能力错误、带分类前缀的空间判定、站点登录", () => {
|
||||
expect(reasonText("invalid_input")?.text).toBe("参数不对");
|
||||
expect(reasonText("cache:volume_state_unknown")?.text).toBe("下载盘用量未知");
|
||||
expect(reasonText("cache:volume_state_unknown")?.text).toBe("下载目录读不到容量");
|
||||
expect(reasonText("deferred:no_downloader_instance")?.text).toBe("没有可用的下载器");
|
||||
expect(reasonText("auth_missing")?.text).toContain("Cookie");
|
||||
expect(reasonText("unidentifiable")?.text).toBe("没识别出作品");
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { Kysely } from "kysely";
|
||||
import { execDdl, oneOf } from "../src/migration-helpers.ts";
|
||||
|
||||
// 下载侧可见性:decision.kind 增加
|
||||
// - downloader_offline:下载器连续不可达超过一段时间(按实例去重,恢复后自动关闭)
|
||||
// - cache_volume_unknown:下载目录读不到容量(路径映射 / 挂载问题,按卷去重,恢复后自动关闭)
|
||||
|
||||
const OLD_DECISION_KINDS = [
|
||||
"identity_ambiguous",
|
||||
"download_stalled",
|
||||
"download_failed",
|
||||
"path_conflict",
|
||||
"space_insufficient",
|
||||
"approval_required",
|
||||
"metadata_conflict",
|
||||
"source_unreachable",
|
||||
"other",
|
||||
"identity_unresolved",
|
||||
"episode_mapping",
|
||||
];
|
||||
const NEW_DECISION_KINDS = [...OLD_DECISION_KINDS, "downloader_offline", "cache_volume_unknown"];
|
||||
|
||||
const swapDecisionKinds = (values: readonly string[]) => `
|
||||
alter table decision drop constraint decision_kind_check;
|
||||
alter table decision add constraint decision_kind_check ${oneOf("kind", values)};
|
||||
`;
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型
|
||||
export async function up(db: Kysely<any>): Promise<void> {
|
||||
await execDdl(db, swapDecisionKinds(NEW_DECISION_KINDS));
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: 同上
|
||||
export async function down(db: Kysely<any>): Promise<void> {
|
||||
await execDdl(
|
||||
db,
|
||||
`update decision set kind = 'other' where kind in ('downloader_offline', 'cache_volume_unknown');
|
||||
${swapDecisionKinds(OLD_DECISION_KINDS)}`,
|
||||
);
|
||||
}
|
||||
@@ -112,7 +112,9 @@ export type DecisionKind =
|
||||
| "source_unreachable"
|
||||
| "other"
|
||||
| "identity_unresolved"
|
||||
| "episode_mapping";
|
||||
| "episode_mapping"
|
||||
| "downloader_offline"
|
||||
| "cache_volume_unknown";
|
||||
export type DecisionStatus = "open" | "investigating" | "awaiting_user" | "resolved" | "dismissed" | "expired";
|
||||
export type DecisionSeverity = "info" | "warning" | "critical";
|
||||
export type DownloadTaskOrigin = "submitted" | "adopted";
|
||||
|
||||
Reference in new issue
Block a user