6022ed09b2
remove resolveSeriesTvdb from LanguageResolverConfig, rename jellyfinFallback to probeFallback, replace Jellyfin-based TVDB resolution with series_name title search over Sonarr library, update tests accordingly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
192 lines
5.6 KiB
TypeScript
192 lines
5.6 KiB
TypeScript
import { Database } from "bun:sqlite";
|
|
import { describe, expect, test, beforeEach } from "bun:test";
|
|
import { SCHEMA } from "../../db/schema";
|
|
import { resolveLanguage, type LanguageResolverConfig } from "../language-resolver";
|
|
import type { RadarrLibrary } from "../radarr";
|
|
import type { SonarrLibrary } from "../sonarr";
|
|
|
|
let db: Database;
|
|
|
|
function initDb(): Database {
|
|
const d = new Database(":memory:");
|
|
for (const stmt of SCHEMA.split(";").filter((s) => s.trim())) {
|
|
d.run(`${stmt};`);
|
|
}
|
|
return d;
|
|
}
|
|
|
|
function insertItem(db: Database, overrides: Record<string, unknown> = {}): number {
|
|
const defaults = {
|
|
type: "Movie",
|
|
name: "Test Movie",
|
|
file_path: "/movies/test.mkv",
|
|
original_language: "deu",
|
|
orig_lang_source: "probe",
|
|
needs_review: 0,
|
|
tmdb_id: "12345",
|
|
imdb_id: null,
|
|
tvdb_id: null,
|
|
series_name: null,
|
|
series_key: null,
|
|
};
|
|
const row = { ...defaults, ...overrides };
|
|
db.prepare(
|
|
`INSERT INTO media_items (type, name, file_path, original_language, orig_lang_source, needs_review, tmdb_id, imdb_id, tvdb_id, series_name, series_key) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
).run(
|
|
row.type,
|
|
row.name,
|
|
row.file_path,
|
|
row.original_language,
|
|
row.orig_lang_source,
|
|
row.needs_review,
|
|
row.tmdb_id,
|
|
row.imdb_id,
|
|
row.tvdb_id,
|
|
row.series_name,
|
|
row.series_key,
|
|
);
|
|
return (db.prepare("SELECT id FROM media_items WHERE file_path = ?").get(row.file_path) as { id: number }).id;
|
|
}
|
|
|
|
function makeRadarrLibrary(entries: Array<{ tmdbId?: string; imdbId?: string; lang: string }>): RadarrLibrary {
|
|
const byTmdbId = new Map<string, { originalLanguage?: { name: string } }>();
|
|
const byImdbId = new Map<string, { originalLanguage?: { name: string } }>();
|
|
for (const e of entries) {
|
|
const movie = { originalLanguage: { name: e.lang } };
|
|
if (e.tmdbId) byTmdbId.set(e.tmdbId, movie);
|
|
if (e.imdbId) byImdbId.set(e.imdbId, movie);
|
|
}
|
|
return { byTmdbId, byImdbId };
|
|
}
|
|
|
|
function makeSonarrLibrary(entries: Array<{ tvdbId: string; lang: string; title?: string }>): SonarrLibrary {
|
|
const byTvdbId = new Map<string, { originalLanguage?: { name: string }; title?: string }>();
|
|
for (const e of entries) {
|
|
byTvdbId.set(e.tvdbId, { originalLanguage: { name: e.lang }, title: e.title });
|
|
}
|
|
return { byTvdbId };
|
|
}
|
|
|
|
function baseCfg(overrides: Partial<LanguageResolverConfig> = {}): LanguageResolverConfig {
|
|
return {
|
|
radarr: null,
|
|
sonarr: null,
|
|
radarrLibrary: null,
|
|
sonarrLibrary: null,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
beforeEach(() => {
|
|
db = initDb();
|
|
});
|
|
|
|
describe("resolveLanguage", () => {
|
|
test("radarr hit overrides probe guess for movies", async () => {
|
|
const id = insertItem(db, {
|
|
type: "Movie",
|
|
tmdb_id: "12345",
|
|
original_language: "deu",
|
|
orig_lang_source: "probe",
|
|
});
|
|
const cfg = baseCfg({
|
|
radarr: { url: "http://radarr:7878", apiKey: "key" },
|
|
radarrLibrary: makeRadarrLibrary([{ tmdbId: "12345", lang: "English" }]),
|
|
});
|
|
|
|
const result = await resolveLanguage(db, id, cfg);
|
|
|
|
expect(result.origLang).toBe("eng");
|
|
expect(result.origLangSource).toBe("radarr");
|
|
expect(result.needsReview).toBe(0);
|
|
expect(result.externalRaw).toBeDefined();
|
|
});
|
|
|
|
test("sonarr hit overrides probe guess for episodes via tvdb_id", async () => {
|
|
const id = insertItem(db, {
|
|
type: "Episode",
|
|
file_path: "/series/ep1.mkv",
|
|
name: "Test Episode",
|
|
tmdb_id: null,
|
|
tvdb_id: "99999",
|
|
original_language: "deu",
|
|
orig_lang_source: "probe",
|
|
});
|
|
const cfg = baseCfg({
|
|
sonarr: { url: "http://sonarr:8989", apiKey: "key" },
|
|
sonarrLibrary: makeSonarrLibrary([{ tvdbId: "99999", lang: "Japanese" }]),
|
|
});
|
|
|
|
const result = await resolveLanguage(db, id, cfg);
|
|
|
|
expect(result.origLang).toBe("jpn");
|
|
expect(result.origLangSource).toBe("sonarr");
|
|
expect(result.needsReview).toBe(0);
|
|
});
|
|
|
|
test("sonarr hit via series_name when tvdb_id not in library", async () => {
|
|
const id = insertItem(db, {
|
|
type: "Episode",
|
|
file_path: "/series/arrow-ep1.mkv",
|
|
name: "Arrow S01E01",
|
|
tmdb_id: null,
|
|
tvdb_id: null,
|
|
series_name: "Arrow",
|
|
original_language: "deu",
|
|
orig_lang_source: "probe",
|
|
});
|
|
// Sonarr library has the series under a title match
|
|
const cfg = baseCfg({
|
|
sonarr: { url: "http://sonarr:8989", apiKey: "key" },
|
|
sonarrLibrary: makeSonarrLibrary([{ tvdbId: "series-tvdb-456", lang: "English", title: "Arrow" }]),
|
|
});
|
|
|
|
const result = await resolveLanguage(db, id, cfg);
|
|
|
|
expect(result.origLang).toBe("eng");
|
|
expect(result.origLangSource).toBe("sonarr");
|
|
expect(result.needsReview).toBe(0);
|
|
});
|
|
|
|
test("skips resolution when orig_lang_source is manual", async () => {
|
|
const id = insertItem(db, {
|
|
type: "Movie",
|
|
tmdb_id: "12345",
|
|
original_language: "fra",
|
|
orig_lang_source: "manual",
|
|
});
|
|
const cfg = baseCfg({
|
|
radarr: { url: "http://radarr:7878", apiKey: "key" },
|
|
radarrLibrary: makeRadarrLibrary([{ tmdbId: "12345", lang: "English" }]),
|
|
});
|
|
|
|
const result = await resolveLanguage(db, id, cfg);
|
|
|
|
expect(result.origLang).toBe("fra");
|
|
expect(result.origLangSource).toBe("manual");
|
|
expect(result.needsReview).toBe(0);
|
|
});
|
|
|
|
test("returns probe guess unchanged when no external source matches", async () => {
|
|
const id = insertItem(db, {
|
|
type: "Movie",
|
|
tmdb_id: "99999",
|
|
original_language: "deu",
|
|
orig_lang_source: "probe",
|
|
needs_review: 0,
|
|
});
|
|
// Radarr library has no entry for tmdb 99999
|
|
const cfg = baseCfg({
|
|
radarr: { url: "http://radarr:7878", apiKey: "key" },
|
|
radarrLibrary: makeRadarrLibrary([{ tmdbId: "11111", lang: "English" }]),
|
|
});
|
|
|
|
const result = await resolveLanguage(db, id, cfg);
|
|
|
|
expect(result.origLang).toBe("deu");
|
|
expect(result.origLangSource).toBe("probe");
|
|
expect(result.needsReview).toBe(1);
|
|
expect(result.externalRaw).toBeNull();
|
|
});
|
|
});
|