Merge branch 'dl3'
Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
commit
85aa9d47d0
50 files changed
+1535
-348
No files matched your search
@@ -1118,6 +1118,24 @@ export const Api = {
|
||||
body: execution.ManualAcquisitionRequest,
|
||||
response: execution.ManualAcquisitionResult,
|
||||
}),
|
||||
why: ep({
|
||||
method: "get",
|
||||
path: "/acquisition-plans/{id}/why",
|
||||
scope: "read",
|
||||
summary:
|
||||
"下载的来由:来源(订阅 / 智能体会话 / 手动)、目的(补缺 / 升级 / 换源)、被替换的版本、选中理由要点与原始评估",
|
||||
params: IdParam,
|
||||
response: board.DownloadWhy,
|
||||
}),
|
||||
cancel: ep({
|
||||
method: "post",
|
||||
path: "/acquisition-plans/{id}/cancel",
|
||||
scope: "organize.execute",
|
||||
summary:
|
||||
"取消还没提交到下载器的计划(待批准 / 已预留):释放收录版本与预算预留;已有下载任务的返回 409。待批准的计划请拒绝审批(拒绝后计划自动取消)",
|
||||
params: IdParam,
|
||||
response: execution.AcquisitionPlan,
|
||||
}),
|
||||
},
|
||||
downloadTasks: {
|
||||
...crud(
|
||||
@@ -1138,7 +1156,7 @@ export const Api = {
|
||||
path: "/download-tasks/board",
|
||||
scope: "read",
|
||||
summary:
|
||||
"下载面板:按进行中 / 已完成分组列出下载任务,附每个种子的实时速度、剩余时间、连接数与下载器总速度(实时数据短时缓存)",
|
||||
"下载面板:按进行中 / 已完成分组列出下载任务(进行中含还没提交、在等批准或下载盘空间的计划,kind=plan),附每个种子的实时速度、剩余时间、连接数与下载器总速度(实时数据短时缓存)",
|
||||
query: board.DownloadBoardQuery,
|
||||
response: board.DownloadBoard,
|
||||
}),
|
||||
|
||||
@@ -177,8 +177,8 @@ export const CAPABILITY_CATALOG: Record<KnownCapabilityId, CapabilityDescriptor>
|
||||
),
|
||||
"download.plan": d(
|
||||
"download.plan",
|
||||
"生成下载计划",
|
||||
"评估候选,生成占用收录版本(slot)与预算预留的下载计划;不提交。选定资源后、提交前用;不会提交下载,也不预留收录版本",
|
||||
"准备下载",
|
||||
"评估选定的资源能否下到这个收录版本(slot),准备一份待提交的下载(返回 planId);不提交,也不占用收录版本与预算。选定资源后、提交前用",
|
||||
"preview",
|
||||
"DownloadPlan",
|
||||
"organize.execute",
|
||||
@@ -189,7 +189,7 @@ export const CAPABILITY_CATALOG: Record<KnownCapabilityId, CapabilityDescriptor>
|
||||
"download.submit": d(
|
||||
"download.submit",
|
||||
"提交下载",
|
||||
"按已保存的计划提交到下载器;校验授权与硬阈值。只提交 download.plan 生成的计划;结果未知时先用 download.inspect / task.inspect 核对,不要重复提交",
|
||||
"把 download.plan 准备好的下载(planId)提交到下载器;校验授权与硬阈值。只提交 download.plan 返回的 planId;结果未知时先用 download.inspect / task.inspect 核对,不要重复提交",
|
||||
"execute",
|
||||
"DownloadSubmit",
|
||||
"organize.execute",
|
||||
@@ -602,6 +602,8 @@ export interface CapabilityActivity {
|
||||
noun: string;
|
||||
unit: string | null;
|
||||
hidden?: boolean;
|
||||
/** 动作后不加「了」(「准备下载 1 个资源」) */
|
||||
bare?: boolean;
|
||||
}
|
||||
|
||||
const act = (verb: string, noun: string, unit: string | null = null): CapabilityActivity => ({ verb, noun, unit });
|
||||
@@ -611,7 +613,7 @@ export const CAPABILITY_ACTIVITY: Record<KnownCapabilityId, CapabilityActivity>
|
||||
"release.search": act("搜", "资源", "次"),
|
||||
"release.inspect": act("看", "资源", "个"),
|
||||
"identity.resolve": act("识别", "作品", "个"),
|
||||
"download.plan": act("生成", "下载计划", "份"),
|
||||
"download.plan": { ...act("准备下载", "资源", "个"), bare: true },
|
||||
"download.submit": act("提交", "下载", "个"),
|
||||
"download.recheck": act("校验", "下载任务", "个"),
|
||||
"organize.plan": act("生成", "整理计划", "份"),
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { z } from "zod";
|
||||
import { DecisionKind } from "./agent.ts";
|
||||
import { CursorQuery, Id, NonNegInt, Timestamp } from "./common.ts";
|
||||
import { type DownloadStatus, DownloadTask } from "./execution.ts";
|
||||
import { type DownloadStatus, DownloadTask, PlanPurpose, PlanStatus } from "./execution.ts";
|
||||
import { OrganizationStatus } from "./organization.ts";
|
||||
|
||||
// 下载面板(DL-2):下载任务 + 下载器实时数据(速度、剩余时间、连接数)+ 分组计数 + 下载器总速度。
|
||||
// 实时数据来自 qB torrents/info 与 transfer/info,服务端短 TTL 缓存(轮询不直打 qB)。
|
||||
|
||||
/** 进行中 = 排队/提交中/下载中/暂停/无进展/出错/缺失;已完成 = 下载完成(含做种)/入库中/已入库;已移除不出现 */
|
||||
/**
|
||||
* 进行中 = 排队/提交中/下载中/暂停/无进展/出错/缺失 + 还没提交、在等条件的计划(DL-3);
|
||||
* 已完成 = 下载完成(含做种)/入库中/已入库;已移除不出现
|
||||
*/
|
||||
export const DownloadBoardState = z.enum(["active", "completed"]);
|
||||
export type DownloadBoardState = z.infer<typeof DownloadBoardState>;
|
||||
|
||||
@@ -19,6 +22,8 @@ export const DOWNLOAD_BOARD_STATUSES: Record<DownloadBoardState, readonly Downlo
|
||||
export const DownloadBoardQuery = CursorQuery.extend({
|
||||
state: DownloadBoardState.default("active"),
|
||||
instanceId: Id.optional(),
|
||||
/** 深链(DL-3):只返回这个计划对应的行——等待中的计划,或它的下载任务(不分进行中 / 已完成) */
|
||||
planId: Id.optional(),
|
||||
});
|
||||
export type DownloadBoardQuery = z.infer<typeof DownloadBoardQuery>;
|
||||
|
||||
@@ -44,6 +49,7 @@ export const DownloadLive = z.object({
|
||||
export type DownloadLive = z.infer<typeof DownloadLive>;
|
||||
|
||||
export const DownloadBoardItem = DownloadTask.extend({
|
||||
kind: z.literal("task"),
|
||||
live: DownloadLive.nullable(),
|
||||
/** 已识别的作品 */
|
||||
media: z.object({ id: Id, title: z.string(), year: z.number().int().nullable() }).nullable(),
|
||||
@@ -59,6 +65,61 @@ export const DownloadBoardItem = DownloadTask.extend({
|
||||
});
|
||||
export type DownloadBoardItem = z.infer<typeof DownloadBoardItem>;
|
||||
|
||||
/**
|
||||
* 计划在等什么(DL-3):approval = 等管理员批准(有待处理的审批);space = 下载盘超过暂停线或用量未知;
|
||||
* automation_off = 自动化总开关关闭 / 只读对账;queued = 条件已满足、等提交任务执行。
|
||||
* 每日下载额度在生成计划时核对,超额不生成计划,所以没有"等额度"的计划。
|
||||
*/
|
||||
export const PlanWait = z.enum(["approval", "space", "automation_off", "queued"]);
|
||||
export type PlanWait = z.infer<typeof PlanWait>;
|
||||
|
||||
/** 还没提交到下载器的计划,作为「进行中」的一行(id 是计划 id) */
|
||||
export const DownloadBoardPlanItem = z.object({
|
||||
kind: z.literal("plan"),
|
||||
id: Id,
|
||||
createdAt: Timestamp,
|
||||
/** 资源标题 */
|
||||
name: z.string(),
|
||||
sizeBytes: NonNegInt.nullable(),
|
||||
purpose: PlanPurpose,
|
||||
status: PlanStatus,
|
||||
media: z.object({ id: Id, title: z.string(), year: z.number().int().nullable() }).nullable(),
|
||||
wait: PlanWait,
|
||||
/** 原因码(提交任务的延后原因等;人话见 reasons 表,原码进技术信息) */
|
||||
waitReason: z.string().nullable(),
|
||||
/** 待批准时的审批(批准 / 拒绝回传 payloadHash) */
|
||||
approval: z.object({ id: Id, payloadHash: z.string(), decisionId: Id.nullable() }).nullable(),
|
||||
});
|
||||
export type DownloadBoardPlanItem = z.infer<typeof DownloadBoardPlanItem>;
|
||||
|
||||
export const DownloadBoardRow = z.discriminatedUnion("kind", [DownloadBoardItem, DownloadBoardPlanItem]);
|
||||
export type DownloadBoardRow = z.infer<typeof DownloadBoardRow>;
|
||||
|
||||
/** 下载的来由(DL-3「为什么下这个」):来源、目的、替换的版本、选中理由的原始要点 */
|
||||
export const DownloadWhy = z.object({
|
||||
planId: Id,
|
||||
source: z.discriminatedUnion("kind", [
|
||||
z.object({ kind: z.literal("subscription"), id: Id, name: z.string() }),
|
||||
z.object({ kind: z.literal("agent"), runId: Id, name: z.string() }),
|
||||
z.object({ kind: z.literal("manual") }),
|
||||
z.object({ kind: z.literal("auto") }),
|
||||
]),
|
||||
purpose: PlanPurpose,
|
||||
/** 收录版本名 */
|
||||
slotName: z.string().nullable(),
|
||||
/** 升级 / 换源时被替换的版本(规格短句) */
|
||||
replaces: z.object({ assetId: Id, label: z.string() }).nullable(),
|
||||
/** evaluation.reasons 原串(hard_conditions_met、source_tier_improved、tier:xxx…) */
|
||||
reasons: z.array(z.string()),
|
||||
/** 资源的促销类型(free / 2xfree / 50% …)与做种数 */
|
||||
promotion: z.string().nullable(),
|
||||
seeders: NonNegInt.nullable(),
|
||||
siteName: z.string().nullable(),
|
||||
/** 原始评估结果(技术信息里显示) */
|
||||
evaluation: z.record(z.string(), z.unknown()),
|
||||
});
|
||||
export type DownloadWhy = z.infer<typeof DownloadWhy>;
|
||||
|
||||
export const DownloaderTransfer = z.object({
|
||||
instanceId: Id,
|
||||
name: z.string(),
|
||||
@@ -69,7 +130,8 @@ export const DownloaderTransfer = z.object({
|
||||
export type DownloaderTransfer = z.infer<typeof DownloaderTransfer>;
|
||||
|
||||
export const DownloadBoard = z.object({
|
||||
items: z.array(DownloadBoardItem),
|
||||
/** 进行中:下载任务与等待中的计划按创建时间一起倒序;已完成只有下载任务 */
|
||||
items: z.array(DownloadBoardRow),
|
||||
nextCursor: z.string().nullable(),
|
||||
counts: z.object({ active: NonNegInt, completed: NonNegInt }),
|
||||
/** 下载器总速度(transfer/info,含非本系统管理的任务);不可达的实例速度记 0 */
|
||||
|
||||
@@ -363,6 +363,13 @@ export const LibraryDownloadView = z.object({
|
||||
});
|
||||
export type LibraryDownloadView = z.infer<typeof LibraryDownloadView>;
|
||||
|
||||
/** 没提交到下载器就结束的下载(DL-3):失败 / 取消 / 过期且没有下载任务的计划,进作品的下载历史 */
|
||||
export const LibraryDroppedPlanView = z.object({
|
||||
plan: AcquisitionPlan,
|
||||
releaseTitle: z.string().nullable(),
|
||||
});
|
||||
export type LibraryDroppedPlanView = z.infer<typeof LibraryDroppedPlanView>;
|
||||
|
||||
export const LibraryDetail = z.object({
|
||||
entity: MediaEntity,
|
||||
/** 身份映射状态(含 proposed/conflicted,前端区分展示) */
|
||||
@@ -374,6 +381,7 @@ export const LibraryDetail = z.object({
|
||||
slots: z.array(AcquisitionSlot),
|
||||
candidates: z.array(CandidateEvaluation),
|
||||
downloads: z.array(LibraryDownloadView),
|
||||
droppedPlans: z.array(LibraryDroppedPlanView).default([]),
|
||||
/**
|
||||
* 该作品的整理记录(最近 100 条,新→旧):含没有下载任务的手动整理(文件管理按路径整理)。
|
||||
* 下载历史里已按下载任务列出的记录也在这里,前端按 downloadTaskId 去重展示。
|
||||
|
||||
@@ -77,6 +77,16 @@ const REASONS: Record<string, ReasonText> = {
|
||||
space_insufficient: { text: "空间不足" },
|
||||
unidentifiable: { text: "没识别出作品" },
|
||||
user_deleted: { text: "已删除" },
|
||||
// DL-3:还没提交就结束或在等的下载(计划的 statusReason、提交任务的延后原因)
|
||||
cancelled_by_user: { text: "你取消了这个下载" },
|
||||
approval_rejected: { text: "你没批准这个下载" },
|
||||
approval_revoked: { text: "会话停止,下载没有提交" },
|
||||
approval_expired: { text: "等批准超时,下载没有提交" },
|
||||
subscription_reset: { text: "订阅重置时取消" },
|
||||
automation_disabled: { text: "自动化已关闭", action: "在设置 → 自动化里打开" },
|
||||
read_only_reconcile: { text: "只读对账中,暂不提交下载" },
|
||||
rejected: { text: "下载器拒绝了这个种子" },
|
||||
source_failed: { text: "从站点取种子失败", action: "检查站点登录后重新下载" },
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user