feat(parser,sites): W5-D port title/zhconv/site parsing gaps found by MP differential
Title recognition (parseRelease / parseReleasePath):
- anime prefix tags (★N月新番, [国漫], 【22年日剧】), category lead bracket, `A / B` cn aliases
- `Name [01]`, `[TV 04]`, `#13`, `名称 12.mkv`, `S01 13-14`, `E13-14` episodes
- lead bracket as title vs group, trailing [Group], YTS.MX/Hall_of_C style groups, 字幕组 suffix
- `[dot.release.name][…]` unbracketing, `Season 2017` as year, leading date not airDate,
date-only parent dir does not merge as TV
- original disc: Blu-ray + AVC/HEVC/VC-1/MPEG-2 without x26x/H.26x -> BluRay-Disc
- `{[tmdbid=;type=;s=;e=]}` explicit block, bangumi/anilist ids
- bare [1080]/[5.1], DTS-HD MA5.1, £ multi-group, 合集, pinyin title -> cn from subtitle,
streaming platform table, CHS/CHT/GB/BIG5 in subtitle
zh-map: add 迴滾魷瑯臥駭; document one-to-many chars kept out.
Sites: tbody branch + header-row tightening via movieclaw-import rowSelector (18 definitions),
row-level case/hr/labels include the row itself, subtitle branch (searchType: "subtitles"),
parseSizeMp banker's rounding.
Differential snapshots regenerated; MP test sample sources registered in ledger.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
8352e5e881
commit
36dc048e89
41 files changed
+3111
-2372
No files matched your search
@@ -380,6 +380,20 @@
|
||||
"sha256": "e7952ed40b7b5a66d95fe1b07e1d3915601b47c2936ff10ec6880c8a030d0b8f",
|
||||
"ours": ["packages/adapters/src/sites/user-parsers/types.ts"],
|
||||
"source": "tag"
|
||||
},
|
||||
{
|
||||
"upstream": "tests/cases/meta.py",
|
||||
"commit": "e6820a83ab05e8a00eb0e14adf0f357faa965c7e",
|
||||
"sha256": "3af375cf2c433db9f2dbfd78cb5798718edf26d7414d4ace08a49a02d80a7884",
|
||||
"ours": ["packages/domain/test/differential/meta-mp.json", "packages/domain/test/differential/meta-samples.json"],
|
||||
"source": "manual"
|
||||
},
|
||||
{
|
||||
"upstream": "tests/test_rust_accel.py",
|
||||
"commit": "e6820a83ab05e8a00eb0e14adf0f357faa965c7e",
|
||||
"sha256": "0259f245d27832c2ef685178a180dc7ea33f9106b5b5029f6737f199c556bbec",
|
||||
"ours": ["packages/adapters/test/differential/indexer.json", "scripts/mp-sync/gen-differential.py"],
|
||||
"source": "manual"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -59,6 +59,16 @@
|
||||
},
|
||||
{ "upstream": "app/modules/filemanager/transhandler.py", "ours": ["packages/adapters/src/fs/move.ts"] },
|
||||
{ "upstream": "app/modules/emby/emby.py", "ours": ["packages/adapters/src/emby/client.ts"] },
|
||||
{ "upstream": "app/application/mediaserver.py", "ours": ["packages/adapters/src/emby/client.ts"] }
|
||||
{ "upstream": "app/application/mediaserver.py", "ours": ["packages/adapters/src/emby/client.ts"] },
|
||||
{
|
||||
"upstream": "tests/cases/meta.py",
|
||||
"ours": ["packages/domain/test/differential/meta-samples.json", "packages/domain/test/differential/meta-mp.json"],
|
||||
"note": "MP 自带标题识别用例:76 条标题字符串作差分输入语料(事实数据,不复制断言代码);上游变化时核对新增/修改的用例并重跑 gen-differential.py meta"
|
||||
},
|
||||
{
|
||||
"upstream": "tests/test_rust_accel.py",
|
||||
"ours": ["scripts/mp-sync/gen-differential.py", "packages/adapters/test/differential/indexer.json"],
|
||||
"note": "字幕页 HTML 与字幕字段配置取自 test_rust_subtitle_parser_is_several_times_faster_than_python,作 hhanclub-subtitles@inline 差分样本"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -238,7 +238,7 @@ function convertNexus(y, file) {
|
||||
batch: { delimiter: " ", space_replace: "_" },
|
||||
browse: { path: `${s.torrent_list_path}?page={page}`, start: Number(s.page_offset) === -1 ? 0 : 1 },
|
||||
category: { movie: item("movie"), tv: item("tv"), music: item("music") },
|
||||
torrents: { list: { selector: s.torrent_row_css }, fields },
|
||||
torrents: { list: { selector: rowSelector(s.torrent_row_css) }, fields },
|
||||
umt: {
|
||||
version: `${id}@mc-${REF}`,
|
||||
kind: "nexusphp",
|
||||
@@ -343,6 +343,32 @@ function convertApi(y, file) {
|
||||
function isObj(v) {
|
||||
return v && typeof v === "object" && !Array.isArray(v);
|
||||
}
|
||||
/**
|
||||
* 列表行选择器规范化(W5-D,见 docs/porting/mp-rust-logic-parity.md §5):
|
||||
* - `table.torrents > tr…` 在显式带 <tbody> 的页面上取不到行(cheerio 与 MP PyQuery 同),补 `> tbody > tr…` 分支;
|
||||
* - 不带条件的 `table.torrents > tr` / `table.torrents tr` 会命中表头行,产出标题为空的垃圾行,收紧为"含 td.rowfollow 数据单元格的行"(NexusPHP 表头是 td.colhead;MP PyQuery 的 :has 只接受简单选择器,不能写属性选择器)。
|
||||
*/
|
||||
const ROW_HAS_DETAILS = ":has(td.rowfollow)";
|
||||
function rowSelector(css) {
|
||||
if (!css || !/^\s*table\.torrents[\s>]/.test(css)) return css;
|
||||
const parts = [];
|
||||
const add = (p) => {
|
||||
if (!parts.includes(p)) parts.push(p);
|
||||
};
|
||||
for (const raw of String(css).split(",")) {
|
||||
const part = raw.trim();
|
||||
const m = /^table\.torrents\s*(?:>\s*)?(?:tbody\s*>\s*)?tr(.*)$/.exec(part);
|
||||
if (!m) {
|
||||
add(part);
|
||||
continue;
|
||||
}
|
||||
const cond = m[1].trim() === "" ? ROW_HAS_DETAILS : m[1].trim();
|
||||
add(`table.torrents > tr${cond}`);
|
||||
add(`table.torrents > tbody > tr${cond}`);
|
||||
}
|
||||
return parts.join(", ");
|
||||
}
|
||||
|
||||
function deepMerge(base, over, path = "") {
|
||||
if (!isObj(base) || !isObj(over)) return over;
|
||||
const out = { ...base };
|
||||
|
||||
Reference in new issue
Block a user