feat(adapters): qBittorrent, filesystem, TMDB and Emby adapters with testkit mocks (W1-3a)
- qbittorrent: WebUI API v2 client (cookie/API-key auth, add/info/files/filePrio, pause/resume with 5.x stop/start fallback, tags, delete with explicit deleteFiles, maindata), error classes, idempotent submit with local infohash (own bencode) and post-timeout hash/probe-tag lookup; state mapping per MoviePilot - fs: copyWithVerify (tmp + fsync + size/sha256/sample + no-clobber commit), moveSafe (EXDEV surfaced), resumable crossDeviceMove, statSafe/checkRoot with root sentinel, budgeted incremental scan with cursor, ffprobe, statfs usage - tmdb: multi-language search/details/seasons, LRU+TTL cache with coalescing, rate-limit backoff, MoviePilot match chain (name normalization, year +-1) - emby: Emby/Jellyfin items/seasons/episodes/MediaSources, failures -> unknown - testkit: mock qB / TMDB / Emby servers, torrent builder, temp-tree fixtures - docs: W1-3a porting table appended to downloader-and-sites ledger Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
9c881e737f
commit
ef2e504a94
45 files changed
+5766
-1
No files matched your search
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* mock TMDB 固定数据。电影/剧集 ID 取自公开 TMDB 条目以便对照,但标题/集数/日期只保证"合理",
|
||||
* 不作为真实数据的权威来源;国产综艺条目 ID(900001)为虚构。
|
||||
*/
|
||||
export interface TmdbFixtureEpisode {
|
||||
episode_number: number;
|
||||
air_date: string | null;
|
||||
names: Record<string, string>;
|
||||
}
|
||||
export interface TmdbFixtureSeason {
|
||||
season_number: number;
|
||||
air_date: string | null;
|
||||
names: Record<string, string>;
|
||||
episodes: TmdbFixtureEpisode[];
|
||||
}
|
||||
export interface TmdbFixture {
|
||||
type: "movie" | "tv";
|
||||
id: number;
|
||||
original_title: string;
|
||||
original_language: string;
|
||||
origin_country: string[];
|
||||
date: string;
|
||||
titles: Record<string, string>;
|
||||
overviews?: Record<string, string>;
|
||||
popularity: number;
|
||||
alternative_titles: { iso_3166_1: string; title: string; type: string }[];
|
||||
external_ids: { imdb_id?: string | null; tvdb_id?: number | null };
|
||||
/** 额外译名(titles 中的语言会自动生成 translations) */
|
||||
extra_translations?: { iso_639_1: string; iso_3166_1: string; title: string }[];
|
||||
seasons?: TmdbFixtureSeason[];
|
||||
}
|
||||
|
||||
function eps(n: number, start: string, zh: (i: number) => string, en: (i: number) => string): TmdbFixtureEpisode[] {
|
||||
const base = Date.parse(start);
|
||||
return Array.from({ length: n }, (_, k) => ({
|
||||
episode_number: k + 1,
|
||||
air_date: new Date(base + k * 7 * 86400_000).toISOString().slice(0, 10),
|
||||
names: { "zh-CN": zh(k + 1), "en-US": en(k + 1) },
|
||||
}));
|
||||
}
|
||||
|
||||
export const TMDB_FIXTURES: TmdbFixture[] = [
|
||||
{
|
||||
type: "movie",
|
||||
id: 872585,
|
||||
original_title: "Oppenheimer",
|
||||
original_language: "en",
|
||||
origin_country: ["US"],
|
||||
date: "2023-07-19",
|
||||
titles: { "zh-CN": "奥本海默", "en-US": "Oppenheimer" },
|
||||
overviews: { "zh-CN": "原子弹之父的故事。", "en-US": "The story of J. Robert Oppenheimer." },
|
||||
popularity: 120.5,
|
||||
alternative_titles: [
|
||||
{ iso_3166_1: "TW", title: "奧本海默", type: "" },
|
||||
{ iso_3166_1: "CN", title: "奥本海默传", type: "" },
|
||||
],
|
||||
external_ids: { imdb_id: "tt15398776" },
|
||||
extra_translations: [{ iso_639_1: "ja", iso_3166_1: "JP", title: "オッペンハイマー" }],
|
||||
},
|
||||
{
|
||||
type: "movie",
|
||||
id: 693134,
|
||||
original_title: "Dune: Part Two",
|
||||
original_language: "en",
|
||||
origin_country: ["US"],
|
||||
date: "2024-02-27",
|
||||
titles: { "zh-CN": "沙丘2", "en-US": "Dune: Part Two" },
|
||||
popularity: 210.1,
|
||||
alternative_titles: [
|
||||
{ iso_3166_1: "US", title: "Dune 2", type: "" },
|
||||
{ iso_3166_1: "CN", title: "沙丘:第二部", type: "" },
|
||||
{ iso_3166_1: "TW", title: "沙丘:第二部", type: "" },
|
||||
],
|
||||
external_ids: { imdb_id: "tt15239678" },
|
||||
},
|
||||
{
|
||||
type: "movie",
|
||||
id: 842675,
|
||||
original_title: "流浪地球2",
|
||||
original_language: "zh",
|
||||
origin_country: ["CN"],
|
||||
date: "2023-01-22",
|
||||
titles: { "zh-CN": "流浪地球2", "en-US": "The Wandering Earth II" },
|
||||
popularity: 45.3,
|
||||
alternative_titles: [
|
||||
{ iso_3166_1: "US", title: "The Wandering Earth 2", type: "" },
|
||||
{ iso_3166_1: "CN", title: "流浪地球 2", type: "" },
|
||||
],
|
||||
external_ids: { imdb_id: "tt13539646" },
|
||||
},
|
||||
{
|
||||
type: "tv",
|
||||
id: 136315,
|
||||
original_title: "The Bear",
|
||||
original_language: "en",
|
||||
origin_country: ["US"],
|
||||
date: "2022-06-23",
|
||||
titles: { "zh-CN": "熊家餐馆", "en-US": "The Bear" },
|
||||
popularity: 88.2,
|
||||
alternative_titles: [
|
||||
{ iso_3166_1: "TW", title: "大熊餐廳", type: "" },
|
||||
{ iso_3166_1: "CN", title: "熊家小馆", type: "" },
|
||||
],
|
||||
external_ids: { imdb_id: "tt14452776", tvdb_id: 401654 },
|
||||
seasons: [
|
||||
{
|
||||
season_number: 1,
|
||||
air_date: "2022-06-23",
|
||||
names: { "zh-CN": "第 1 季", "en-US": "Season 1" },
|
||||
episodes: eps(
|
||||
8,
|
||||
"2022-06-23",
|
||||
(i) => `第 ${i} 集`,
|
||||
(i) => `Episode ${i}`,
|
||||
),
|
||||
},
|
||||
{
|
||||
season_number: 2,
|
||||
air_date: "2023-06-22",
|
||||
names: { "zh-CN": "第 2 季", "en-US": "Season 2" },
|
||||
episodes: eps(
|
||||
10,
|
||||
"2023-06-22",
|
||||
(i) => `第 ${i} 集`,
|
||||
(i) => `Episode ${i}`,
|
||||
),
|
||||
},
|
||||
{
|
||||
season_number: 3,
|
||||
air_date: "2024-06-26",
|
||||
names: { "zh-CN": "第 3 季", "en-US": "Season 3" },
|
||||
episodes: eps(
|
||||
10,
|
||||
"2024-06-26",
|
||||
(i) => `第 ${i} 集`,
|
||||
(i) => `Episode ${i}`,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "tv",
|
||||
id: 100088,
|
||||
original_title: "The Last of Us",
|
||||
original_language: "en",
|
||||
origin_country: ["US"],
|
||||
date: "2023-01-15",
|
||||
titles: { "zh-CN": "最后生还者", "en-US": "The Last of Us" },
|
||||
popularity: 150.7,
|
||||
alternative_titles: [
|
||||
{ iso_3166_1: "CN", title: "最后的生还者", type: "" },
|
||||
{ iso_3166_1: "TW", title: "最後生還者", type: "" },
|
||||
],
|
||||
external_ids: { imdb_id: "tt3581920", tvdb_id: 392256 },
|
||||
seasons: [
|
||||
{
|
||||
season_number: 1,
|
||||
air_date: "2023-01-15",
|
||||
names: { "zh-CN": "第 1 季", "en-US": "Season 1" },
|
||||
episodes: eps(
|
||||
9,
|
||||
"2023-01-15",
|
||||
(i) => `第 ${i} 集`,
|
||||
(i) => `Episode ${i}`,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "tv",
|
||||
id: 900001,
|
||||
original_title: "向往的生活",
|
||||
original_language: "zh",
|
||||
origin_country: ["CN"],
|
||||
date: "2017-01-15",
|
||||
titles: { "zh-CN": "向往的生活", "en-US": "Back to Field" },
|
||||
popularity: 12.4,
|
||||
alternative_titles: [
|
||||
{ iso_3166_1: "CN", title: "向往的生活 第一季", type: "" },
|
||||
{ iso_3166_1: "CN", title: "向往", type: "简称" },
|
||||
],
|
||||
external_ids: { imdb_id: null, tvdb_id: null },
|
||||
seasons: [
|
||||
{
|
||||
season_number: 0,
|
||||
air_date: "2017-01-10",
|
||||
names: { "zh-CN": "特别篇", "en-US": "Specials" },
|
||||
episodes: eps(
|
||||
2,
|
||||
"2017-01-10",
|
||||
(i) => `先导片 ${i}`,
|
||||
(i) => `Special ${i}`,
|
||||
),
|
||||
},
|
||||
{
|
||||
season_number: 1,
|
||||
air_date: "2017-01-15",
|
||||
names: { "zh-CN": "第 1 季", "en-US": "Season 1" },
|
||||
episodes: eps(
|
||||
12,
|
||||
"2017-01-15",
|
||||
(i) => `第 ${i} 期`,
|
||||
(i) => `Episode ${i}`,
|
||||
),
|
||||
},
|
||||
{
|
||||
season_number: 2,
|
||||
air_date: "2018-04-20",
|
||||
names: { "zh-CN": "第 2 季", "en-US": "Season 2" },
|
||||
episodes: eps(
|
||||
3,
|
||||
"2018-04-20",
|
||||
(i) => `第 ${i} 期`,
|
||||
(i) => `Episode ${i}`,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
Reference in new issue
Block a user