import { describe, expect, it } from "vitest";
import {
episodeNfo,
inferWorkDir,
isSeasonDirName,
planScrapeTargets,
seasonNfo,
seasonOfDirName,
seasonPosterName,
workNfo,
} from "../src/index.ts";
describe("NFO 生成", () => {
it("电影:MP 元素顺序,缺值不写,IMDb 为 default", () => {
const xml = workNfo({
kind: "movie",
title: "星际穿越",
originalTitle: "Interstellar",
year: 2014,
premiered: "2014-11-05",
overview: "穿越虫洞。",
rating: 8.46,
genres: ["科幻", "剧情", "科幻"],
countries: ["美国"],
runtime: 169,
directors: [{ tmdbId: 525, name: "克里斯托弗·诺兰" }],
actors: [{ tmdbId: 10297, name: "马修·麦康纳", role: "Cooper", profilePath: "/mm.jpg" }],
ids: { tmdb: "157336", imdb: "tt0816692" },
});
expect(xml).toBe(
[
'',
"",
" 157336",
' 157336',
" tt0816692",
' tt0816692',
" ",
" ",
' 克里斯托弗·诺兰',
" ",
" 马修·麦康纳",
" Actor",
" Cooper",
" 10297",
" https://image.tmdb.org/t/p/original/mm.jpg",
" https://www.themoviedb.org/person/10297",
" ",
" 科幻",
" 剧情",
" 8.5",
" 美国",
" 169",
" 星际穿越",
" Interstellar",
" 2014-11-05",
" 2014",
"",
"",
].join("\n"),
);
});
it("剧集 tvshow.nfo:season/episode = -1,豆瓣 id、标语、平台", () => {
const xml = workNfo({
kind: "tvshow",
title: "人生切割术",
premiered: "2022-02-17",
tagline: "工作与生活,彻底分开",
studios: ["Apple TV+"],
ids: { tmdb: "95396", tvdb: "371980", douban: "30448896" },
});
expect(xml).toContain('95396');
expect(xml).toContain('371980');
expect(xml).toContain('30448896');
expect(xml).toContain("Apple TV+");
expect(xml).toMatch(/2022<\/year>\n {2}-1<\/season>\n {2}-1<\/episode>\n {2}/);
expect(xml).not.toContain("");
expect(xml).not.toContain("");
});
it("转义与 CDATA:& < > 转义,]]> 拆段,控制字符删除", () => {
const xml = workNfo({
kind: "movie",
title: 'A & "c"\u0001',
overview: "x]]>y",
ids: { tmdb: "1" },
});
expect(xml).toContain('A & <B> "c"');
expect(xml).toContain("y]]>");
});
it("季与集", () => {
expect(seasonNfo({ season: 1, title: "第 1 季", airDate: "2022-02-17" })).toBe(
[
'',
"",
" 第 1 季",
" 2022-02-17",
" 2022-02-17",
" 2022",
" 1",
"",
"",
].join("\n"),
);
const ep = episodeNfo({ season: 1, episode: 2, overview: "参观。", airDate: "2022-02-18", rating: 8, runtime: 53 });
expect(ep).toContain("第 2 集");
expect(ep).toContain("8.0");
expect(ep).toMatch(/1<\/season>\n {2}2<\/episode>/);
expect(ep).toContain("53");
});
});
describe("刮削目标布局", () => {
const images = {
posterPath: "/p.jpg",
backdropPath: "/b.png",
seasonPosters: new Map([[1, "/s1.jpg"]]),
stills: new Map([
["1:1", "/e1.jpg"],
["1:2", null],
]),
};
it("作品目录推断", () => {
expect(inferWorkDir("tv", "人生切割术 (2022)/Season 1/a.mkv")).toBe("人生切割术 (2022)");
expect(inferWorkDir("tv", "剧集/人生切割术 (2022)/S01/a.mkv")).toBe("剧集/人生切割术 (2022)");
expect(inferWorkDir("movie", "星际穿越 (2014)/a.mkv")).toBe("星际穿越 (2014)");
expect(inferWorkDir("movie", "a.mkv")).toBeNull();
expect(seasonPosterName(0)).toBe("season-specials-poster.jpg");
});
it("剧集:tvshow / 季 / 集,季海报写两处,缺剧照不写", () => {
const t = planScrapeTargets({
kind: "tv",
workDir: "人生切割术 (2022)",
videos: [
{ path: "人生切割术 (2022)/Season 1/人生切割术 - S01E02 - 第 2 集.mkv", season: 1, episode: 2 },
{ path: "人生切割术 (2022)/Season 1/人生切割术 - S01E01 - 第 1 集.mkv", season: 1, episode: 1 },
],
images,
options: { images: true, episodeThumbs: true },
});
expect(t.map((x) => x.path)).toEqual([
"人生切割术 (2022)/tvshow.nfo",
"人生切割术 (2022)/poster.jpg",
"人生切割术 (2022)/fanart.png",
"人生切割术 (2022)/Season 1/season.nfo",
"人生切割术 (2022)/season01-poster.jpg",
"人生切割术 (2022)/Season 1/poster.jpg",
"人生切割术 (2022)/Season 1/人生切割术 - S01E01 - 第 1 集.nfo",
"人生切割术 (2022)/Season 1/人生切割术 - S01E01 - 第 1 集-thumb.jpg",
"人生切割术 (2022)/Season 1/人生切割术 - S01E02 - 第 2 集.nfo",
]);
});
it("关图片 / 关剧照", () => {
const base = {
kind: "tv" as const,
workDir: "S",
videos: [{ path: "S/Season 1/a.mkv", season: 1, episode: 1 }],
images,
};
expect(planScrapeTargets({ ...base, options: { images: false, episodeThumbs: true } }).map((x) => x.role)).toEqual([
"tvshow_nfo",
"season_nfo",
"episode_nfo",
]);
expect(
planScrapeTargets({ ...base, options: { images: true, episodeThumbs: false } }).some(
(x) => x.role === "episode_thumb",
),
).toBe(false);
});
it("电影:作品目录里写 poster/fanart;在库根时用 <视频名>-poster", () => {
const inDir = planScrapeTargets({
kind: "movie",
workDir: "星际穿越 (2014)",
videos: [{ path: "星际穿越 (2014)/星际穿越 (2014) - 1080p.mkv" }],
images,
options: { images: true, episodeThumbs: true },
});
expect(inDir.map((x) => x.path)).toEqual([
"星际穿越 (2014)/星际穿越 (2014) - 1080p.nfo",
"星际穿越 (2014)/poster.jpg",
"星际穿越 (2014)/fanart.png",
]);
const atRoot = planScrapeTargets({
kind: "movie",
workDir: null,
videos: [{ path: "x.mkv" }],
images,
options: { images: true, episodeThumbs: true },
});
expect(atRoot.map((x) => x.path)).toEqual(["x.nfo", "x-poster.jpg", "x-fanart.png"]);
});
it("剧集视频在库根:不写 tvshow / 季文件,只写集 NFO", () => {
const t = planScrapeTargets({
kind: "tv",
workDir: null,
videos: [{ path: "a.S01E01.mkv", season: 1, episode: 1 }],
images,
options: { images: true, episodeThumbs: true },
});
expect(t.map((x) => x.path)).toEqual(["a.S01E01.nfo", "a.S01E01-thumb.jpg"]);
});
it("SCR-2 季目录名:Season 1 / S01 / Specials / SPs / 第 1 季", () => {
for (const n of ["Season 1", "season 12", "S01", "Specials", "SPs", "特别篇", "第 2 季"])
expect(isSeasonDirName(n)).toBe(true);
for (const n of ["人生切割术 (2022)", "Extras", "Season"]) expect(isSeasonDirName(n)).toBe(false);
expect(seasonOfDirName("Season 3")).toBe(3);
expect(seasonOfDirName("S01")).toBe(1);
expect(seasonOfDirName("Specials")).toBe(0);
expect(seasonOfDirName("第 2 季")).toBe(2);
expect(seasonOfDirName("Extras")).toBeNull();
});
it("SCR-2 范围:选中季目录只写季级与各集(季海报仍写上级),选中单集只写该集", () => {
const videos = [{ path: "人生切割术 (2022)/Season 1/a.S01E01.mkv", season: 1, episode: 1 }];
const season = planScrapeTargets({
kind: "tv",
workDir: "人生切割术 (2022)",
videos,
images,
options: { images: true, episodeThumbs: true },
parts: { work: false },
});
expect(season.map((x) => `${x.role}:${x.path}`)).toEqual([
"season_nfo:人生切割术 (2022)/Season 1/season.nfo",
"season_poster:人生切割术 (2022)/season01-poster.jpg",
"season_poster:人生切割术 (2022)/Season 1/poster.jpg",
"episode_nfo:人生切割术 (2022)/Season 1/a.S01E01.nfo",
"episode_thumb:人生切割术 (2022)/Season 1/a.S01E01-thumb.jpg",
]);
const ep = planScrapeTargets({
kind: "tv",
workDir: null,
videos,
images,
options: { images: true, episodeThumbs: true },
parts: { work: false, season: false },
});
expect(ep.map((x) => x.role)).toEqual(["episode_nfo", "episode_thumb"]);
});
});