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]>
This commit is contained in:
rootandClaude Fable 5.1 committed 2026-09-27 18:11:29 +08:00
1 parent a6d049db81
commit 7ba712ea55
40 files changed
+5885 -2

No files matched your search

@@ -0,0 +1,38 @@
const DIGITS: Record<string, number> = {
零: 0,
〇: 0,
一: 1,
二: 2,
两: 2,
兩: 2,
三: 3,
四: 4,
五: 5,
六: 6,
七: 7,
八: 8,
九: 9,
};
const UNITS: Record<string, number> = { 十: 10, 拾: 10, 百: 100, 千: 1000 };
/** 中文数字/阿拉伯数字 → 整数;无法解析返回 undefined */
export function cnToInt(s: string): number | undefined {
const t = s.trim();
if (/^\d+$/.test(t)) return Number.parseInt(t, 10);
if (t === "" || ![...t].every((c) => c in DIGITS || c in UNITS)) return undefined;
let total = 0;
let current = 0;
for (const c of t) {
const d = DIGITS[c];
if (d !== undefined) {
current = d;
continue;
}
const u = UNITS[c]!;
total += (current || 1) * u;
current = 0;
}
return total + current;
}
export const CN_NUM = "[0-9零〇一二两兩三四五六七八九十拾百千]+";