feat(scrape): 任意目录就地刮削,按路径识别作品,每次刮削一条记录(SCR-2)

- POST /files/scrape 改为对目录 / 视频文件刮削:已登记用作品,否则按路径识别(本地 + TMDB),
  认不准返回候选,选定后再提交;未登记直接用 TMDB 字段,addToLibrary 顺手建作品
- 作品详情重新刮削、入库后自动刮削改为对作品目录刮削,同一实现
- 迁移 0058_scrape_record 替代 media_scrape;GET /files/scrape-records
- 与 MP ScrapingChain 的文件布局差分(7 个场景,3 处登记偏离)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-01 00:08:17 +08:00
1 parent f60254e265
commit 87bf33b42b
29 files changed
+2272 -653

No files matched your search

+49 -1
View File
@@ -1,5 +1,14 @@
import { describe, expect, it } from "vitest";
import { episodeNfo, inferWorkDir, planScrapeTargets, seasonNfo, seasonPosterName, workNfo } from "../src/index.ts";
import {
episodeNfo,
inferWorkDir,
isSeasonDirName,
planScrapeTargets,
seasonNfo,
seasonOfDirName,
seasonPosterName,
workNfo,
} from "../src/index.ts";
describe("NFO 生成", () => {
it("电影:MP 元素顺序,缺值不写,IMDb 为 default", () => {
@@ -198,4 +207,43 @@ describe("刮削目标布局", () => {
});
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"]);
});
});