import { describe, expect, it } from "vitest"; import { cnToInt, evalEpisodeOffset, expandEpisodes, fileKindOf, OffsetExpressionError, type ParsedRelease, parseRelease, parseReleasePath, splitReleasePath, } from "../src/release-parser"; type Expect = Partial & { 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", source: "BluRay", isEncode: true, 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 } } }, ]; 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(1); 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(); }); });