Strip [电视剧]/[动漫]/[音乐]… prefixes into parsed.category instead of titleCn, split RSS-embedded site descriptions into the subtitle, take the first Chinese name of the subtitle as titleCn and the rest as altTitles, and stop reading 'Travis - 12 Memories' as episode 12. PARSER_VERSION 2. Co-Authored-By: Claude Opus 5.5 <[email protected]>
832 lines
31 KiB
TypeScript
832 lines
31 KiB
TypeScript
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");
|
|
});
|
|
});
|
|
|
|
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();
|
|
});
|
|
});
|