Files
UHD_Media_Tool/packages/domain/test/release-parser.test.ts
T
rootandClaude Opus 5.5 18ee466664 fix(parser): 原盘判定参考副标题线索与 PCM 音轨
标题写成 `Bluray H.264` 这类压制字样、但副标题写「自购自抓自扫」「BDMV」「原盘」「DIY」
「Blu-ray Disc」的,或带 LPCM / PCM 音轨又没有 x264 / x265 / BDRip / 压制说法的,判为原盘。
只在来源是 BluRay(或没写来源但有分辨率)时补判;「原盘压制」「自压」、WEB-DL、Remux、单个视频文件不受影响。
用「药屋少女的呢喃」真实搜索结果补正例与反例。

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
2026-10-06 15:36:15 +08:00

939 lines
36 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from "vitest";
import {
cnToInt,
evalEpisodeOffset,
expandEpisodes,
fileKindOf,
OffsetExpressionError,
type ParsedRelease,
parseRelease,
parseReleasePath,
splitReleasePath,
} from "../src/release-parser/index.ts";
type Expect = Partial<ParsedRelease> & { group?: string; groupSource?: string };
interface Sample {
id: string;
input: string;
subtitle?: string;
expect: Expect;
/** 与 MoviePilot 行为不同的地方,写明原因 */
deviation?: string;
}
function check(r: ParsedRelease, e: Expect) {
const { group, groupSource, ...rest } = e;
for (const [k, v] of Object.entries(rest)) {
expect(r[k as keyof ParsedRelease], `${r.raw} → ${k}`).toEqual(v);
}
if (group !== undefined) expect(r.releaseGroup?.name, `${r.raw} → group`).toBe(group);
if (groupSource !== undefined) expect(r.releaseGroup?.source).toBe(groupSource);
}
// M-xx 编号对应 docs/porting/moviepilot-metadata-recognition.md §9.1
const TITLES: Sample[] = [
{
id: "M-01",
input: "Oppenheimer.2023.2160p.UHD.BluRay.REMUX.DV.HDR.HEVC.TrueHD.7.1.Atmos-FraMeSToR",
expect: {
titleEn: "Oppenheimer",
year: 2023,
resolution: "2160p",
source: "REMUX",
isEncode: false,
videoCodec: "H.265",
hdr: ["DV", "HDR10"],
audio: ["TrueHD 7.1", "Atmos"],
group: "FraMeSToR",
confidence: "rule_confirmed",
},
deviation: "MP 发布组表无 FraMeSToR 时 grp=None;本项目种子表收录",
},
{
id: "M-02",
input: "奥本海默 Oppenheimer 2023 2160p UHD Blu-ray HEVC DV TrueHD 7.1-MTeam",
expect: {
title: "奥本海默",
titleCn: "奥本海默",
titleEn: "Oppenheimer",
// Blu-ray + HEVC 且没有 x265/H.265 → 原盘(对照 MP 内置规则 BLU)
source: "BluRay-Disc",
isEncode: false,
uhd: true,
hdr: ["DV"],
group: "MTeam",
groupSource: "known",
},
},
{
id: "M-03",
input: "The.Last.of.Us.S01.2023.1080p.WEB-DL.H264.DDP5.1.Atmos-ADWeb",
expect: {
titleEn: "The Last of Us",
season: 1,
episodes: undefined,
year: 2023,
audio: ["DDP 5.1", "Atmos"],
group: "ADWeb",
isSeasonPack: true,
packHint: "season_pack",
},
},
{
id: "M-04",
input: "The.Last.of.Us.S01E03.Long.Long.Time.2160p.MAX.WEB-DL.DDP5.1.Atmos.DV.HDR.H.265-FLUX",
expect: {
titleEn: "The Last of Us",
season: 1,
episodes: { start: 3, end: 3 },
webSource: "Max",
videoCodec: "H.265",
hdr: ["DV", "HDR10"],
packHint: "single",
},
},
{
id: "M-05",
input: "Breaking.Bad.S01-S05.Complete.1080p.BluRay.x264-ROVERS",
expect: { season: 1, seasonEnd: 5, packHint: "multi_season_pack", isSeasonPack: true, encoder: "x264" },
},
{
id: "M-06",
input: "庆余年 第二季 Joy of Life S02 2024 2160p WEB-DL H265 60fps DDP5.1-HHWEB",
expect: { titleCn: "庆余年", titleEn: "Joy of Life", season: 2, fps: 60, group: "HHWEB" },
},
{
id: "M-07",
input: "庆余年.Joy.of.Life.S02E01-E06.2024.2160p.WEB-DL.H265.AAC-ADWeb",
expect: { season: 2, episodes: { start: 1, end: 6 }, packHint: "multi_episode", isSeasonPack: false },
},
{
id: "M-08",
input:
"The.Lord.of.the.Rings.The.Fellowship.of.the.Ring.2001.Extended.Edition.2160p.UHD.BluRay.x265.10bit.HDR.DTS-HD.MA.6.1-CHD",
expect: {
titleEn: "The Lord of the Rings The Fellowship of the Ring",
cut: "Extended",
bitDepth: 10,
audio: ["DTS-HD MA 6.1"],
group: "CHD",
},
deviation: "MP 把 Extended.Edition 当干扰词删除;本项目保留为 cut",
},
{
id: "M-09",
input: "Kill.Bill.Vol.1.2003.Part.1.1080p.BluRay.x264-CHD",
expect: { titleEn: "Kill Bill Vol 1", year: 2003, part: "Part1" },
},
{
id: "M-10",
input: "Avatar.The.Way.of.Water.2022.2160p.WEB-DL.DDP5.1.Atmos.HDR10Plus.HEVC-CMCT",
expect: { hdr: ["HDR10+"], group: "CMCT", source: "WEB-DL" },
},
{
id: "M-11",
input: "奔跑吧 第十二季 20240426 第1期 1080p WEB-DL H264 AAC-HHWEB",
expect: {
titleCn: "奔跑吧",
season: 12,
issue: 1,
airDate: "20240426",
episodes: undefined,
packHint: "single",
mediaTypeHint: "tv",
},
deviation: "MP 把第1期当 episode、忽略 20240426;本项目 issue/airDate 分开保存",
},
{
id: "M-12",
input: "奔跑吧.Keep.Running.S12.2024.04.26.1080p.WEB-DL.H264.AAC-HHWEB",
expect: { season: 12, airDate: "20240426", year: undefined, isSeasonPack: false, packHint: "single" },
deviation: "MP 删除日期后判成整季包;本项目有日期即单期",
},
{
id: "M-13",
input: "向往的生活 第八季 全12期 2024 1080p WEB-DL",
expect: { titleCn: "向往的生活", season: 8, totalEpisodes: 12, year: 2024, isSeasonPack: true },
deviation: "MP 负向前瞻导致季号丢失",
},
{
id: "M-14",
input: "[喵萌奶茶屋&LoliHouse] 葬送的芙莉莲 / Sousou no Frieren - 12 [WebRip 1080p HEVC-10bit AAC][简繁日内封字幕]",
expect: {
titleCn: "葬送的芙莉莲",
titleEn: "Sousou no Frieren",
episodes: { start: 12, end: 12 },
season: 1,
seasonInferred: true,
group: "喵萌奶茶屋@LoliHouse",
bitDepth: 10,
source: "WEBRip",
subtitles: ["zh-Hans", "zh-Hant", "ja"],
confidence: "multi_evidence",
},
deviation: "MP 季号为空;本项目推定 1 并标 seasonInferred",
},
{
id: "M-15",
input: "Frieren.S01E12.1080p.NF.WEB-DL.DDP2.0.H.264-VARYG",
expect: { webSource: "Netflix", audio: ["DDP 2.0"], group: "VARYG", videoCodec: "H.264" },
},
{
id: "M-16",
input: "黑暗荣耀 The Glory 2022 S01 Complete 1080p NF WEB-DL x264 DDP5.1-HHWEB",
subtitle: "黑暗荣耀 第一季 全8集 宋慧乔",
expect: { season: 1, totalEpisodes: 8, isSeasonPack: true, confidence: "multi_evidence" },
},
{
id: "M-17",
input: "Dune.Part.Two.2024.2160p.UHD.BluRay.REMUX.HDR.HEVC.Atmos-MTeam",
expect: { titleEn: "Dune Part Two", part: undefined, audio: ["Atmos"], source: "REMUX" },
},
{
id: "M-18",
input: "Some.Show.S02E05E06.1080p.WEB-DL-GRP",
expect: { season: 2, episodes: { start: 5, end: 6 }, group: "GRP", groupSource: "suffix" },
},
{
id: "M-19",
input: "狂飙 The Knockout 2023 全39集 1080p WEB-DL H264 AAC-OurTV",
expect: { season: 1, seasonInferred: true, totalEpisodes: 39, packHint: "season_pack" },
},
{
id: "M-20",
input: "Some.Show.2019.1080p.WEB-DL-GRP",
subtitle: "第1-10集 全10集",
expect: { episodes: { start: 1, end: 10 }, totalEpisodes: 10 },
},
{
id: "M-21",
input: "[Nekomoe kissaten][Oshi no Ko][01-11 Fin][1080p]",
expect: { titleEn: "Oshi no Ko", episodes: { start: 1, end: 11 }, season: 1, group: "Nekomoe kissaten" },
},
{ id: "M-22", input: "Show.Name.2019-2020.1080p.WEB-DL", expect: { titleEn: "Show Name", year: 2019 } },
// ---- 国际/观众/馒头风格电影
{
id: "X-01",
input: "Blade.Runner.2049.2017.1080p.BluRay.x264-SPARKS",
expect: { titleEn: "Blade Runner 2049", year: 2017, isEncode: true },
},
{
id: "X-02",
input: "Dune.Part.Two.2024.1080p.BluRay.x264.DTS-HD.MA.7.1-ADE",
expect: { group: "ADE", audio: ["DTS-HD MA 7.1"], isEncode: true },
},
{
id: "X-03",
input: "流浪地球2 The Wandering Earth II 2023 2160p UHD BluRay REMUX HEVC DV TrueHD 7.1 Atmos-MTeam",
expect: { titleCn: "流浪地球2", titleEn: "The Wandering Earth II", source: "REMUX" },
},
{
id: "X-04",
input: "Interstellar.2014.IMAX.1080p.BluRay.x264.DTS-HD.MA.5.1-HDS",
expect: { cut: "IMAX", group: "HDS", titleEn: "Interstellar" },
},
{
id: "X-05",
input: "Blade.Runner.1982.The.Final.Cut.2160p.UHD.BluRay.x265.10bit.HDR.DTS-HD.MA.5.1-CiNEPHiLES",
expect: { cut: "Final Cut", titleEn: "Blade Runner", year: 1982 },
},
{
id: "X-06",
input: "Apocalypse.Now.1979.Theatrical.Cut.1080p.BluRay.x264-AMIABLE",
expect: { cut: "Theatrical", group: "AMIABLE", groupSource: "suffix" },
deviation: "未知组 MP 为 None;本项目末段兜底",
},
{
id: "X-07",
input: "Oppenheimer.2023.1080p.BluRay.DD+5.1.x264-EbP",
expect: { audio: ["DDP 5.1"], group: "EbP" },
},
{
id: "X-08",
input: "Oppenheimer.2023.COMPLETE.UHD.BLURAY-SURCODE",
expect: { source: "BluRay-Disc", isEncode: false, group: "SURCODE" },
},
{
id: "X-09",
input: "Some.Movie.2021.BD50.1080p.AVC.DTS-HD.MA.5.1",
expect: { source: "BluRay-Disc", isEncode: false, videoCodec: "H.264" },
},
{ id: "X-10", input: "Some.Movie.2021.1080p.HDTV.x264-GRP", expect: { source: "HDTV" } },
{
id: "X-11",
input: "Some.Movie.2021.720p.WEBRip.x264.AAC-YTS",
expect: { source: "WEBRip", resolution: "720p", audio: ["AAC"] },
},
{ id: "X-12", input: "Some.Movie.2021.DVDRip.XviD-GRP", expect: { source: "DVDRip", videoCodec: "XviD" } },
{ id: "X-13", input: "Movie.2020.3840x2160.WEB-DL.HEVC", expect: { resolution: "2160p", titleEn: "Movie" } },
{ id: "X-14", input: "Movie.2020.4K.WEB-DL.HEVC-GRP", expect: { resolution: "2160p" } },
{
id: "X-15",
input: "Mission.Impossible.Dead.Reckoning.Part.One.2023.2160p.UHD.BluRay.x265.10bit.HDR.TrueHD.7.1.Atmos-MTeam",
expect: { titleEn: "Mission Impossible Dead Reckoning Part One", part: undefined, audio: ["TrueHD 7.1", "Atmos"] },
},
{
id: "X-16",
input: "Harry.Potter.and.the.Deathly.Hallows.Part.2.2011.1080p.BluRay.x264-SPARKS",
expect: { titleEn: "Harry Potter and the Deathly Hallows Part 2", year: 2011 },
},
{
id: "X-17",
input: "Movie.Title.2021.1080p.BluRay.Remux.AVC.DTS-HD.MA.5.1-FGT",
expect: { source: "REMUX", isEncode: false },
},
{ id: "X-18", input: "The.Matrix.1999.Remastered.1080p.BluRay.x264-GRP", expect: { cut: "Remastered" } },
{ id: "X-19", input: "Movie.2020.Unrated.1080p.BluRay.x264-GRP", expect: { cut: "Unrated" } },
{
id: "X-20",
input: "Movie.Name.2019.Directors.Cut.1080p.BluRay.x264-FOO",
expect: { cut: "Director's Cut", titleEn: "Movie Name" },
},
{
id: "X-21",
input: "银翼杀手 导演剪辑版 1982 1080p BluRay x264",
expect: { titleCn: "银翼杀手", cut: "Director's Cut", year: 1982 },
},
{
id: "X-22",
input: "让子弹飞.Let.the.Bullets.Fly.2010.1080p.BluRay.x264.DTS-CMCT",
expect: { titleCn: "让子弹飞", titleEn: "Let the Bullets Fly", audio: ["DTS"], group: "CMCT" },
},
{
id: "X-23",
input: "长安三万里.Chang.An.2023.2160p.WEB-DL.H265.HDR.DDP5.1-OurTV",
expect: { titleCn: "长安三万里", hdr: ["HDR10"], group: "OurTV" },
},
{
id: "X-24",
input: "Movie.2020.1080p.BluRay.x264-GRP 12.5GB",
expect: { sizeBytes: Math.round(12.5 * 1024 ** 3), group: "GRP" },
},
{ id: "X-25", input: "Movie 2020 1080p WEB-DL 中英字幕", expect: { subtitles: ["zh", "en"] } },
{ id: "X-26", input: "Movie 2020 1080p 国语中字 WEB-DL", expect: { subtitles: ["zh"], titleEn: "Movie" } },
{
id: "X-27",
input: "[简繁英字幕] Movie 2020 1080p WEB-DL",
expect: { subtitles: ["zh-Hans", "zh-Hant", "en"], releaseGroup: undefined },
},
{ id: "X-28", input: "The Movie 2021", expect: { titleEn: "The Movie", year: 2021, confidence: "rule_confirmed" } },
{ id: "X-29", input: "The Movie 1080p WEB-DL", expect: { year: undefined, confidence: "needs_confirm" } },
// ---- 剧集
{
id: "T-01",
input: "三体 Three-Body S01E05 2023 1080p WEB-DL H264 AAC-OurTV",
expect: { titleCn: "三体", titleEn: "Three Body", season: 1, episodes: { start: 5, end: 5 } },
},
{
id: "T-02",
input: "Loki.S02E01.2160p.DSNP.WEB-DL.DDP5.1.Atmos.DV.HDR.H.265-FLUX",
expect: { webSource: "Disney+", hdr: ["DV", "HDR10"] },
},
{
id: "T-03",
input: "The.Bear.S03.1080p.DSNP.WEB-DL.DDP5.1.H.264-MWeb",
expect: { season: 3, isSeasonPack: true, group: "MWeb" },
},
{
id: "T-04",
input: "Beyond.the.Clouds.2024.S01.1080p.WEB-DL.H264.AAC-ADWeb",
expect: { titleEn: "Beyond the Clouds", year: 2024, season: 1 },
},
{
id: "T-05",
input: "The.Office.US.S05E13-E14.720p.HDTV.x264",
expect: { titleEn: "The Office US", episodes: { start: 13, end: 14 }, source: "HDTV" },
},
{ id: "T-06", input: "Friends.1x05.720p", expect: { season: 1, episodes: { start: 5, end: 5 } } },
{ id: "T-07", input: "Show.Season.2.1080p.BluRay.x264-GRP", expect: { season: 2, isSeasonPack: true } },
{
id: "T-08",
input: "Show.S01E01.Pilot.1080p.WEB-DL.mkv",
expect: { isFile: true, fileKind: "video", extension: "mkv", titleEn: "Show", packHint: "single" },
},
{ id: "T-09", input: "Show.S01E01.chs.srt", expect: { fileKind: "subtitle", subtitles: ["zh-Hans"] } },
{ id: "T-10", input: "Show.S01E01E02E03.1080p.WEB-DL", expect: { episodes: { start: 1, end: 3 } } },
{ id: "T-11", input: "Show.Name.S01.E01.1080p", expect: { season: 1, episodes: { start: 1, end: 1 } } },
{ id: "T-12", input: "Show.S01D02.1080p.BluRay", expect: { season: 1, part: "Disc2" } },
{
id: "T-13",
input: "Planet.Earth.III.S01.2160p.iP.WEB-DL.AAC2.0.HLG.H.265-GRP",
expect: { titleEn: "Planet Earth III", hdr: ["HLG"], audio: ["AAC 2.0"] },
},
{ id: "T-14", input: "天下长河 第1集 1080p WEB-DL", expect: { titleCn: "天下长河", segment: undefined } },
{
id: "T-15",
input: "庆余年 第二季 第1集 范闲入京 1080p WEB-DL",
expect: { titleCn: "庆余年", season: 2, episodes: { start: 1, end: 1 } },
},
{
id: "T-16",
input: "[tmdbid=94997] House.of.the.Dragon.S01E03.1080p.mkv",
expect: { explicitIds: [{ provider: "tmdb", id: "94997" }], season: 1, titleEn: "House of the Dragon" },
},
{ id: "T-17", input: "[tmdbid=0]Foo.2020", expect: { explicitIds: [], titleEn: "Foo" } },
// ---- 动漫
{
id: "A-01",
input: "[ANi] 葬送的芙莉蓮 - 13 [1080P][Baha][WEB-DL][AAC AVC][CHT].mp4",
expect: { group: "ANi", episodes: { start: 13, end: 13 }, subtitles: ["zh-Hant"], titleCn: "葬送的芙莉蓮" },
},
{
id: "A-02",
input: "[Lilith-Raws] Sousou no Frieren - 01 [Baha][WEB-DL][1080p][AVC AAC][CHT][MP4]",
expect: { group: "Lilith-Raws", titleEn: "Sousou no Frieren", episodes: { start: 1, end: 1 } },
},
{
id: "A-03",
input: "[UnknownSub][Some Anime][05][1080p]",
expect: { group: "UnknownSub", groupSource: "bracket", episodes: { start: 5, end: 5 }, titleEn: "Some Anime" },
},
{
id: "A-04",
input: "[Sub] Show - 26 Fin [1080p]",
expect: { episodes: { start: 26, end: 26 } },
},
{ id: "A-05", input: "Show 01-26 Fin 1080p", expect: { episodes: { start: 1, end: 26 }, isSeasonPack: true } },
// ---- 综艺
{
id: "V-01",
input: "快乐大本营 20210903 1080p WEB-DL H264 AAC-HHWEB",
expect: { airDate: "20210903", season: undefined, confidence: "needs_confirm", packHint: "single" },
},
{
id: "V-02",
input: "乘风2024 第5期 下 20240524 1080p WEB-DL",
expect: { titleCn: "乘风2024", issue: 5, segment: "lower", airDate: "20240524" },
},
{
id: "V-03",
input: "披荆斩棘 第三季 第12期 加更版 1080p WEB-DL-HHWEB",
expect: { season: 3, issue: 12, contentType: "extra", titleCn: "披荆斩棘" },
},
{
id: "V-04",
input: "歌手2024 第1期 会员版 1080p",
expect: { variantTags: ["会员版"], confidence: "needs_confirm", issue: 1 },
},
{
id: "V-05",
input: "声生不息 纯享版 20220501 1080p WEB-DL",
expect: { contentType: "performance_only", airDate: "20220501" },
},
{
id: "V-06",
input: "The.Voice.2024.05.03.1080p.WEB.h264-GRP",
expect: { titleEn: "The Voice", airDate: "20240503", source: "WEB-DL", year: undefined },
},
{
id: "V-07",
input: "Late.Night.2024.05.03.Guest.Name.1080p.WEB.h264-GRP",
expect: { titleEn: "Late Night", airDate: "20240503" },
},
{
id: "V-08",
input: "极限挑战 第十季 20240505 第2期 上 1080p WEB-DL",
expect: { season: 10, issue: 2, segment: "upper" },
},
{ id: "V-09", input: "声生不息 第2期(上) 1080p", expect: { issue: 2, segment: "upper" } },
{ id: "V-10", input: "奔跑吧 第十二季 第3期 下 1080p", expect: { issue: 3, segment: "lower", season: 12 } },
{ id: "V-11", input: "某综艺 2024年5月3日 完整版 1080p", expect: { airDate: "20240503", segment: "full" } },
{ id: "V-12", input: "某综艺 第1期 花絮", expect: { contentType: "behind_the_scenes" } },
{ id: "V-13", input: "某综艺 先导片", expect: { contentType: "preview" } },
{ id: "V-14", input: "某综艺 第3期 陪看", expect: { contentType: "reaction" } },
{ id: "V-15", input: "某综艺 第3期 正片", expect: { contentType: "main" } },
{ id: "V-16", input: "某剧 全3季 1080p", expect: { season: 1, seasonEnd: 3, packHint: "multi_season_pack" } },
{ id: "V-17", input: "某剧 第1-3季 1080p", expect: { season: 1, seasonEnd: 3 } },
{ id: "V-18", input: "某剧 第1-10集 1080p", expect: { episodes: { start: 1, end: 10 } } },
];
// W5-D:按 MP 差分报告(docs/porting/mp-rust-logic-parity.md §2)补迁的写法,编号对应报告 B/M 条目
const MP_PARITY: Sample[] = [
{
id: "B1",
input:
"【爪爪字幕组】★7月新番[欢迎来到实力至上主义的教室 第二季/Youkoso Jitsuryoku Shijou Shugi no Kyoushitsu e S2][11][1080p][HEVC][GB][MP4]",
expect: { titleCn: "欢迎来到实力至上主义的教室", season: 2, episodes: { start: 11, end: 11 }, group: "爪爪字幕组" },
},
{
id: "B1b",
input: "[GM-Team][国漫][寻剑 第1季][Sword Quest Season 1][2002][02][AVC][GB][1080P]",
expect: { titleCn: "寻剑" },
},
{
id: "B1c",
input: "【幻月字幕组】【22年日剧】【据幸存的六人所说】【04】【1080P】",
expect: { titleCn: "据幸存的六人所说" },
},
{
id: "B2",
input: "[桜都字幕组] 异世界归来的舅舅 / Isekai Ojisan [01][1080p][简体内嵌]",
expect: { titleEn: "Isekai Ojisan", episodes: { start: 1, end: 1 }, mediaTypeHint: "tv" },
},
{
id: "B3",
input: "[秋叶原冥途战争][Akiba Maid Sensou][2022][WEB-DL][1080][TV Series][第01话][LeagueWEB]",
expect: { titleCn: "秋叶原冥途战争", resolution: "1080p", group: "LeagueWEB" },
},
{
id: "B3b",
input: "[某动画][Some Anime][2022][WEB-DL][1080p][TV 03][NewSub]",
expect: { titleCn: "某动画", episodes: { start: 3, end: 3 }, group: "NewSub", groupSource: "bracket" },
},
{
id: "B4",
input:
"[Caligula.The.Ultimate.Cut.2023.2160p.UHD.Blu-ray.HEVC.DTS-HD.MA.5.1-BHYS@OurBits][DIY中字原盘] [罗马帝国艳情史][91.86GB].iso",
expect: { titleEn: "Caligula", year: 2023, resolution: "2160p", source: "BluRay-Disc", cut: "Ultimate" },
},
{
id: "B5",
input: "The Long Season 2017 2160p WEB-DL H265 120FPS AAC-XXX",
expect: { titleEn: "The Long Season", year: 2017, season: undefined },
},
{
id: "B6",
input: "2022.8.2.Twelve.Monkeys.1995.GBR.4K.REMASTERED.BluRay.1080p.X264.DTS",
expect: { titleEn: "Twelve Monkeys", year: 1995, airDate: undefined, mediaTypeHint: "unknown" },
},
{
id: "B7",
input: "[YTS.MX] The Shawshank Redemption 1994 1080p BluRay x264",
expect: { titleEn: "The Shawshank Redemption", group: "YTS.MX" },
},
{
id: "B8",
input: "[猎户不鸽发布组] 组长女儿与照料专员 / 组长女儿与保姆 Kumichou Musume to Sewagakari [09] [1080p+]",
expect: { titleCn: "组长女儿与保姆", group: "猎户不鸽发布组" },
},
{
id: "B8b",
input: "[xyx98]传颂之物/Utawarerumono/うたわれるもの[BDrip][1920x1080][TV 01-26 Fin]",
expect: { titleCn: "传颂之物", titleEn: "Utawarerumono", episodes: { start: 1, end: 26 } },
},
{ id: "B9", input: "哆啦A梦:大雄的宇宙小战争 2021 (2022) - 1080p.mp4", expect: { titleEn: undefined, year: 2022 } },
{
id: "B10",
input: "[Gal to Kyouryuu][02][BDRIP][1080P][H264_FLAC].mkv",
expect: { titleEn: "Gal to Kyouryuu", episodes: { start: 2, end: 2 }, releaseGroup: undefined },
},
{
id: "B11",
input: "The 355 2022 BluRay 1080p DTS-HD MA5.1 X265.10bit-BeiTai",
expect: { audio: ["DTS-HD MA 5.1"] },
},
{
id: "B11b",
input: "The Heart of Genius E13-14 2022 1080p WEB-DL H264 AAC",
expect: { episodes: { start: 13, end: 14 } },
},
{
id: "B11c",
input: "【极影字幕社】★1月新番 Metallic Rouge/金属口红 第13话 GB 1080P MP4",
expect: { titleEn: "Metallic Rouge", titleCn: "金属口红", group: "极影字幕社", subtitles: ["zh-Hans"] },
},
{
id: "M1",
input: "{[tmdbid=94997;type=tv;s=1;e=3-4]}House.of.the.Dragon.1080p.mkv",
expect: {
titleEn: "House of the Dragon",
explicitIds: [{ provider: "tmdb", id: "94997" }],
season: 1,
episodes: { start: 3, end: 4 },
mediaTypeHint: "tv",
},
},
{
id: "M2",
input: "Sousou no Frieren - 05 [1080p] [bangumiid=400602].mkv",
expect: { explicitIds: [{ provider: "bangumi", id: "400602" }] },
},
{
id: "M3a",
input: "[AI-Raws] 逆境無頼カイジ #13 (BD HEVC 1920x1080 FLAC)[7CFEE642].mkv",
expect: { titleCn: "逆境無頼カイジ", episodes: { start: 13, end: 13 } },
},
{ id: "M3b", input: "西部世界 12.mkv", expect: { titleCn: "西部世界", episodes: { start: 12, end: 12 } } },
{
id: "M3c",
input: "The Heart of Genius S01 13-14 2022 1080p WEB-DL H264 AAC",
expect: { season: 1, episodes: { start: 13, end: 14 } },
},
{ id: "M3d", input: "流浪地球 2.mkv", expect: { episodes: undefined } },
{
id: "M4",
input: "Mai Xiang S01 2019 2160p WEB-DL H.265 DDP2.0-HHWEB",
subtitle: "麦香 | 全36集 | 4K",
expect: { titleCn: "麦香", title: "麦香" },
},
// ID-2:副标题第一段的中文片名补作中文名(M4b 原先断言不取)
{
id: "M4b",
input: "Blade Runner 2017 1080p WEB-DL",
subtitle: "银翼杀手 | 科幻",
expect: { titleCn: "银翼杀手", titleEn: "Blade Runner" },
},
{ id: "M5", input: "Thor Love and Thunder (2022) [1080p] [WEBRip] [5.1]", expect: { audio: ["5.1"] } },
{
id: "M6",
input: "Planet.Earth.III.S01.2160p.iP.WEB-DL.AAC2.0.HLG.H.265-GRP",
expect: { webSource: "BBC iPlayer" },
},
{
id: "M6b",
input: "Some Anime S01E02 2022 1080p B-Global WEB-DL X264 AAC-AnimeS@ADWeb[2022年10月新番]",
expect: { webSource: "B-Global", titleEn: "Some Anime" },
},
{
id: "M7",
input: "Sense8 S2E1 2015-2017 1080P WEB-DL X265 AC3£cXcY@FRDS",
expect: { group: "cXcY@FRDS", audio: ["AC3"] },
},
{
id: "M7b",
input: "指环王三部曲合集 The Lord of the Rings Trilogy 2001-2003 Extended 2160p UHD BluRay REMUX",
expect: { titleCn: "指环王三部曲" },
},
{
id: "DISC",
input: "Some.Movie.2021.1080p.Blu-ray.AVC.DTS-HD.MA.5.1-GRP",
expect: { source: "BluRay-Disc", isEncode: false },
},
{ id: "DISC-x264", input: "Some.Movie.2021.1080p.BluRay.AVC.x264-GRP", expect: { source: "BluRay", isEncode: true } },
{ id: "DISC-rip", input: "Some.Movie.2021.1080p.BDRip.AVC-GRP", expect: { source: "BluRay", isEncode: true } },
{
id: "SUB-latin",
input: "Oppenheimer.2023.2160p.BluRay.x265-GRP",
subtitle: "奥本海默 CHS/ENG",
expect: { subtitles: ["zh-Hans"] },
},
{
id: "SUB-size",
input: "Oppenheimer.2023.2160p.BluRay.x265-GRP",
subtitle: "奥本海默 60 GB",
expect: { subtitles: undefined },
},
];
describe("MP 差分补迁写法", () => {
for (const s of MP_PARITY) {
it(`${s.id} ${s.input}`, () => check(parseRelease(s.input, s.subtitle ? { subtitle: s.subtitle } : {}), s.expect));
}
it("父目录只有日期不把电影合并成剧集", () => {
const r = parseReleasePath(
"/movies/Mixed.Collection.20240501.FRDS/12.Years.a.Slave.2013.BluRay.1080p.x265-FRDS/12.Years.a.Slave.2013.BluRay.1080p.x265-FRDS.mkv",
);
expect(r.mediaTypeHint).toBe("unknown");
});
});
// 原盘补判(SEARCH 准确度):站点搜索「药屋少女的呢喃」的真实结果。标题写成压制字样、但副标题说「自购自抓自扫」的是原盘;
// LPCM / PCM 音轨且没有 x264 / x265 / BDRip / 压制说法的也是原盘。普通压制、WEB-DL、Remux 不受影响。
const DISC_HINTS: Sample[] = [
{
id: "DISC-sub-self-rip",
input: "Kusuriya no Hitorigoto 2025 S02 1080p Bluray H.264 LPCM-@U2",
subtitle: "自购自抓自扫 | Team HAYAKU | Animate特典附 | TBR35041D 药屋少女的呢喃 第二季",
expect: { source: "BluRay-Disc", isEncode: false },
},
{
id: "DISC-sub-bdmv",
input: "The Apothecary Diaries 2025 S02 1080p Blu-ray H.264-GRP",
subtitle: "药屋少女的呢喃 第二季 BDMV Vol.1",
expect: { source: "BluRay-Disc" },
},
{
id: "DISC-sub-yuanpan",
input: "Some Show S01 1080p BluRay H.264 DTS-HD MA 5.1-GRP",
subtitle: "某剧 第一季 原盘 中字",
expect: { source: "BluRay-Disc" },
},
{
id: "DISC-pcm",
input: "Some Movie 2020 1080p BluRay LPCM 2.0-GRP",
expect: { source: "BluRay-Disc", isEncode: false },
},
// 已按编码字样判成原盘的不变
{
id: "DISC-avc-lpcm",
input: "Kusuriya no Hitorigoto S02 2025 Blu-ray 1080p AVC LPCM 2Audio-@U2",
subtitle: "Team HAYAKU | Animate全卷特典附 药屋少女的呢喃 第二季",
expect: { source: "BluRay-Disc" },
},
{
id: "DISC-bdmv-rip-welcome",
input: "The Apothecary Diaries Vol.3 2025 BDMV 1080p AVC LPCM 2.0-Arri-RAWS@U2",
subtitle: "U2 Exclusive, RIPs welcomed with credits | 制作RIP请注明出处 | 药屋少女的呢喃/药师少女的独语 S2",
expect: { source: "BluRay-Disc" },
},
// 反例:压制
{
id: "ENC-self-encode",
input: "Kusuriya no Hitorigoto 2023 BDRip 1080p AVC FLAC@U2",
subtitle: "药师少女的呢喃/Kusuriya no Hitorigoto/薬屋のひとりごと 1080p 自压(附相关专辑) TV 01-24 Fin+SP",
expect: { source: "BluRay", isEncode: true },
},
{
id: "ENC-x265",
input: "The Apothecary Diaries S01 2023 1080p BluRay x265 10bit FLAC 2.0-ADE",
subtitle: "药屋少女的呢喃 / 药师少女的独语 / The Apothecary Diaries / 薬屋のひとりごと 【简日|繁日|简|繁|日字幕】",
expect: { source: "BluRay", isEncode: true },
},
{
id: "ENC-x264",
input: "Kusuriya no Hitorigoto S01 Complete 2023 1080p Blu-ray x264 FLAC-jsum@U2",
subtitle: "药屋少女的呢喃 / 药师少女的独语 / 薬屋のひとりごと",
expect: { source: "BluRay", isEncode: true },
},
{
id: "ENC-av1",
input: "Kusuriya no Hitorigoto S02 1080p BluRay AV1 OPUS 2.0-7³ACG@OurBits",
subtitle: "药师少女的独语第2季/薬屋のひとりごと 第2期 | 01-24(25-48) 日语+简繁字幕",
expect: { source: "BluRay", isEncode: true },
},
{ id: "ENC-x264-lpcm", input: "Some Movie 2020 1080p BluRay x264 LPCM 2.0-GRP", expect: { source: "BluRay" } },
{
id: "ENC-from-disc",
input: "Some Movie 2020 1080p BluRay H.264 FLAC-GRP",
subtitle: "基于原盘压制 简繁字幕",
expect: { source: "BluRay" },
},
{
id: "ENC-pcm-encode-note",
input: "Some Movie 2020 1080p BluRay H.264 LPCM-GRP",
subtitle: "自压 内封字幕",
expect: { source: "BluRay" },
},
// 反例:其他来源不受副标题影响
{
id: "WEB-sub-yuanpan",
input: "Some Movie 2020 1080p WEB-DL H.264 AAC-GRP",
subtitle: "原盘 DIY 还没出,先发 WEB",
expect: { source: "WEB-DL" },
},
{
id: "REMUX-diy",
input: "The Apothecary Diaries S01 2023 1080p BluRay Remux AVC FLAC 2.0-ADE",
subtitle: "药屋少女的呢喃 【DIY / 双语简繁字幕】",
expect: { source: "REMUX" },
},
// 标题里写着 2160P 的非发布名(播放器解码信息):按字面解析,不猜
{
id: "WEB-2160-literal",
input:
"The Apothecary Diaries 2023 S01E24 2160P TV WEB-DL MPEG-4 AVC1 - Native D3D9 DXVA Decoder VLD AAC LC -Kisssub",
subtitle:
"药屋少女的呢喃 薬屋のひとりごと (2023) 2160P TVS01 [01-24](AI增强1080P转2160P) AVC1 @5160kbps AAC @128kbps",
expect: { source: "WEB-DL", resolution: "2160p" },
},
];
describe("原盘补判(副标题线索 / PCM 音轨)", () => {
for (const s of DISC_HINTS) {
it(`${s.id} ${s.input}`, () => check(parseRelease(s.input, s.subtitle ? { subtitle: s.subtitle } : {}), s.expect));
}
});
describe("parseRelease 样本表", () => {
it("样本数量 ≥ 60", () => {
expect(TITLES.length).toBeGreaterThanOrEqual(60);
});
for (const s of TITLES) {
it(`${s.id} ${s.input}${s.deviation ? ` [deviation: ${s.deviation}]` : ""}`, () => {
const r = parseRelease(s.input, s.subtitle ? { subtitle: s.subtitle } : {});
check(r, s.expect);
expect(r.parserVersion).toBe(2);
expect(r.raw).toBe(s.input);
});
}
});
// P-xx 对应迁移文档 §9.2
const PATHS: Sample[] = [
{
id: "P-01",
input: "/downloads/The.Last.of.Us.S01.2023.1080p.WEB-DL-ADWeb/The.Last.of.Us.S01E03.1080p.WEB-DL-ADWeb.mkv",
expect: { season: 1, episodes: { start: 3, end: 3 }, year: 2023, confidence: "multi_evidence" },
},
{
id: "P-02",
input: "/downloads/庆余年 第二季 Joy of Life S02 2024 2160p WEB-DL H265 DDP5.1-HHWEB/01.mp4",
expect: { title: "庆余年", season: 2, year: 2024, episodes: { start: 1, end: 1 }, seasonInferred: undefined },
},
{
id: "P-03",
input: "/downloads/庆余年 第二季 Joy of Life S02 2024 2160p WEB-DL H265 DDP5.1-HHWEB/第02集.mkv",
expect: { episodes: { start: 2, end: 2 }, season: 2, group: "HHWEB", audio: ["DDP 5.1"] },
},
{
id: "P-04",
input: "/downloads/Breaking.Bad.S01-S05.1080p.BluRay.x264-ROVERS/S03/07.mkv",
expect: { title: "Breaking Bad", season: 3, episodes: { start: 7, end: 7 }, seasonEnd: undefined },
},
{
id: "P-05",
input: "/d/Oppenheimer.2023.2160p.UHD.BluRay.REMUX-FraMeSToR/Featurettes/Making.Of.mkv",
expect: { title: "Oppenheimer", contentType: "extra", confidence: "needs_confirm" },
deviation: "MP 把附加内容识别成名为 Making Of 的电影",
},
{
id: "P-07",
input: "/downloads/The Glory 2022 S01 1080p NF WEB-DL-HHWEB/简体.srt",
expect: { auxiliaryOnly: true, title: "The Glory", confidence: "needs_confirm", fileKind: "subtitle" },
deviation: "MP 取到根目录名 Downloads",
},
{
id: "P-08",
input: "/downloads/奔跑吧 第十二季 1080p WEB-DL-HHWEB/奔跑吧.20240426.第1期.mp4",
expect: { season: 12, issue: 1, airDate: "20240426", episodes: undefined },
},
{
id: "P-09",
input: "/downloads/奔跑吧 第十二季 1080p WEB-DL-HHWEB/奔跑吧.2024.05.03.mp4",
expect: { mediaTypeHint: "tv", season: 12, airDate: "20240503", year: undefined, confidence: "multi_evidence" },
deviation: "MP 文件无类型、不与 TV 目录合并",
},
{
id: "P-10",
input: "/downloads/奔跑吧 第十二季 1080p WEB-DL-HHWEB/奔跑吧.20240503.加更版.mp4",
expect: { contentType: "extra", airDate: "20240503", season: 12 },
},
{
id: "P-11",
input: "/downloads/Kill.Bill.2003.1080p.BluRay.x264-CHD/CD1.mkv",
expect: { part: "CD1", title: "Kill Bill", year: 2003 },
deviation: "MP 因 Part 门槛丢失 CD1",
},
{
id: "P-12",
input: "/media/tv/庆余年 (2019)/Season 2/庆余年 - S02E03 - 第 3 集.mkv",
expect: { season: 2, episodes: { start: 3, end: 3 }, year: 2019 },
},
{
id: "P-13",
input:
"/dl/[LoliHouse] Sousou no Frieren [01-28][WebRip 1080p]/[LoliHouse] Sousou no Frieren - 13 [WebRip 1080p].mkv",
expect: { episodes: { start: 13, end: 13 }, titleEn: "Sousou no Frieren" },
},
{
id: "P-14",
input: "/dl/Some.Show.S01.1080p/Making.Of.Special.mkv",
expect: { mediaTypeHint: "tv", season: 1, confidence: "needs_confirm" },
deviation: "文件无剧集信号、目录是剧集:合并但降级为 needs_confirm",
},
{
id: "P-15",
input: "/dl/Collection.1080p/The.Matrix.Reloaded.2003.1080p.BluRay.x264.mkv",
expect: { year: 2003, confidence: "rule_confirmed" },
},
{
id: "P-16",
input: "/dl/The.Matrix.1999.1080p/The.Matrix.Reloaded.2003.1080p.mkv",
expect: { year: 2003, confidence: "needs_confirm" },
},
{
id: "P-17",
input: "/dl/Show.S01.1080p.WEB-DL-GRP/Sample/show.s01e01.sample.mkv",
expect: { season: 1 },
},
];
describe("parseReleasePath 目录 + 文件名合并", () => {
for (const s of PATHS) {
it(`${s.id} ${s.input}${s.deviation ? ` [deviation: ${s.deviation}]` : ""}`, () => {
check(parseReleasePath(s.input), s.expect);
});
}
it("接受已拆分的路径段", () => {
const r = parseReleasePath({ file: "E05.mkv", parent: "Show.S02.1080p.WEB-DL-GRP" });
expect(r.season).toBe(2);
expect(r.episodes).toEqual({ start: 5, end: 5 });
expect(r.title).toBe("Show");
});
it("sample 目录打证据", () => {
const r = parseReleasePath("/dl/Show.S01/Sample/x.mkv");
expect(r.evidence).toContain("dir:sample");
});
it("splitReleasePath 跳过附加内容目录", () => {
expect(splitReleasePath("/a/b/Extras/c.mkv")).toEqual({
file: "c.mkv",
parent: "b",
grandParent: "a",
extrasDir: "Extras",
});
expect(splitReleasePath("c.mkv")).toEqual({ file: "c.mkv" });
});
});
describe("集号偏移(安全求值)", () => {
it("W-03: 按表达式偏移集号", () => {
const r = parseRelease("[Sub] Kimetsu no Yaiba - 45 [1080p].mkv", { episodeOffset: "EP-44" });
expect(r.episodes).toEqual({ start: 1, end: 1 });
expect(r.evidence).toContain("episode:offset");
expect(r.confidence).toBe("multi_evidence");
});
it("负结果被拒绝,保留原集号", () => {
const r = parseRelease("Show.S01E03.1080p", { episodeOffset: "-10" });
expect(r.episodes).toEqual({ start: 3, end: 3 });
expect(r.evidence).toContain("episode:offset_rejected");
});
it("各运算符", () => {
expect(evalEpisodeOffset("EP-12", 13)).toBe(1);
expect(evalEpisodeOffset("+1", 1)).toBe(2);
expect(evalEpisodeOffset("*2", 3)).toBe(6);
expect(evalEpisodeOffset("5", 3)).toBe(8);
expect(evalEpisodeOffset("(EP+1)//2", 5)).toBe(3);
expect(evalEpisodeOffset("EP%4", 10)).toBe(2);
expect(evalEpisodeOffset("EP/2", 5)).toBe(2);
expect(evalEpisodeOffset("2**3+ep", 1)).toBe(9);
expect(evalEpisodeOffset("-EP+20", 5)).toBe(15);
expect(evalEpisodeOffset("+(EP)", 5)).toBe(5);
});
it("拒绝非白名单输入", () => {
for (const bad of ["", "EP; process.exit()", "EP+", "(EP+1", "EP/0", "EP 1", "a".repeat(80), "EP)", "2**2000"]) {
expect(() => evalEpisodeOffset(bad, 1), bad).toThrow(OffsetExpressionError);
}
});
});
describe("辅助函数", () => {
it("cnToInt", () => {
expect(cnToInt("十二")).toBe(12);
expect(cnToInt("二十")).toBe(20);
expect(cnToInt("二十三")).toBe(23);
expect(cnToInt("一百零五")).toBe(105);
expect(cnToInt("两")).toBe(2);
expect(cnToInt("08")).toBe(8);
expect(cnToInt("abc")).toBeUndefined();
expect(cnToInt("")).toBeUndefined();
});
it("fileKindOf", () => {
expect(fileKindOf("a.MKV")).toBe("video");
expect(fileKindOf("a.ass")).toBe("subtitle");
expect(fileKindOf("a.mka")).toBe("audio");
expect(fileKindOf("a.nfo")).toBe("nfo");
expect(fileKindOf("a.jpg")).toBe("image");
expect(fileKindOf("a.txt")).toBe("other");
expect(fileKindOf("a")).toBeUndefined();
});
it("expandEpisodes", () => {
expect(expandEpisodes({ start: 2, end: 4 })).toEqual([2, 3, 4]);
expect(expandEpisodes(undefined)).toEqual([]);
});
it("自定义发布组扩展", () => {
const r = parseRelease("Movie.2020.1080p.WEB-DL-MyGroup", { releaseGroups: ["MyGroup"] });
expect(r.releaseGroup).toEqual({ name: "MyGroup", source: "known" });
});
it("纯数字/Exx 文件名", () => {
expect(parseRelease("05.mkv").episodes).toEqual({ start: 5, end: 5 });
expect(parseRelease("E07.mkv").episodes).toEqual({ start: 7, end: 7 });
expect(parseRelease("EP07.mp4").episodes).toEqual({ start: 7, end: 7 });
expect(parseRelease("S03").season).toBe(3);
expect(parseRelease("Season 2").season).toBe(2);
});
it("isFile 选项", () => {
expect(parseRelease("12", { isFile: true }).episodes).toEqual({ start: 12, end: 12 });
expect(parseRelease("12").episodes).toBeUndefined();
});
});