- release-parser: title/file/dir parsing with MP parity + deviations (airDate/issue, cut, release group suffix fallback, dir+file merge, safe episode offset) - identity: title normalization (NFKC, trad->simp map, season suffix) and evidence-level candidate ranking (year tolerance ±1, no probabilities) - slot: hard/soft/upgrade evaluation (missing vs unmet fields), state machine, movie/unit completion (season pack must be covered by actual file list) - acquisition: new_title/catch_up/upgrade modes, priority order, budget, cache volume hard threshold with resume watermark (no double counting) - path: immutable path bindings, restricted naming templates, sanitize, conflict detection, downloader<->host path mapping - organize: planOrganize with sidecar pairing, sample exclusion, existing target policy, crossDevice marking, H&R -> conflict for move - download-state: qB state mapping, completion, no-progress evidence 230 tests, ~97% statement coverage. Co-Authored-By: Claude Fable 5.1 <[email protected]>
38 lines
1.5 KiB
TypeScript
38 lines
1.5 KiB
TypeScript
import type { AcquisitionMode } from "./mode";
|
|
|
|
/** 默认:用户明确立即处理 > 追更补缺 > 新作品 > 已有版本升级(REQ §6.8) */
|
|
export type PriorityClass = "immediate" | "catch_up" | "new_title" | "upgrade";
|
|
export const DEFAULT_PRIORITY_ORDER: readonly PriorityClass[] = ["immediate", "catch_up", "new_title", "upgrade"];
|
|
|
|
export interface QueueItem {
|
|
id: string;
|
|
priorityClass: PriorityClass;
|
|
/** 入队时间 ISO;同优先级先进先出,避免饥饿 */
|
|
enqueuedAt: string;
|
|
}
|
|
|
|
export function priorityOf(mode: AcquisitionMode, immediate = false): PriorityClass {
|
|
return immediate ? "immediate" : mode;
|
|
}
|
|
|
|
export function validatePriorityOrder(order: readonly PriorityClass[]): void {
|
|
const all = new Set<PriorityClass>(DEFAULT_PRIORITY_ORDER);
|
|
if (order.length !== all.size || new Set(order).size !== all.size || !order.every((o) => all.has(o)))
|
|
throw new Error(`优先级配置必须恰好包含 ${[...all].join("/")} 各一次`);
|
|
}
|
|
|
|
/** 按优先级排序(稳定、纯函数)。优先级只影响资源分配,不越过阈值/HR/权限 */
|
|
export function sortByPriority<T extends QueueItem>(
|
|
items: readonly T[],
|
|
order: readonly PriorityClass[] = DEFAULT_PRIORITY_ORDER,
|
|
): T[] {
|
|
validatePriorityOrder(order);
|
|
const rank = new Map(order.map((c, i) => [c, i]));
|
|
return [...items].sort(
|
|
(a, b) =>
|
|
rank.get(a.priorityClass)! - rank.get(b.priorityClass)! ||
|
|
a.enqueuedAt.localeCompare(b.enqueuedAt) ||
|
|
a.id.localeCompare(b.id),
|
|
);
|
|
}
|