Files
UHD_Media_Tool/packages/domain/src/acquisition/priority.ts
T
rootandClaude Fable 5.1 7ba712ea55 feat(domain): pure business logic for parsing, identity, slots, acquisition, paths, organize, download state (W1-2)
- 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]>
2026-09-27 18:11:29 +08:00

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),
);
}