Files
netfelix-audio-fix/server/services/radarr.ts
Felix Förtsch 874f04b7a5 wire scheduler into queue, add retry, dev-reset cleanup, biome 2.4 migrate
- execute: actually call isInScheduleWindow/waitForWindow/sleepBetweenJobs in runSequential (they were dead code); emit queue_status SSE events (running/paused/sleeping/idle) so the pipeline's existing QueueStatus listener lights up
- review: POST /:id/retry resets an errored plan to approved, wipes old done/error jobs, rebuilds command from current decisions, queues fresh job
- scan: dev-mode DELETE now also wipes jobs + subtitle_files (previously orphaned after every dev reset)
- biome: migrate config to 2.4 schema, autoformat 68 files (strings + indentation), relax opinionated a11y/hooks-deps/index-key rules that don't fit this codebase
- routeTree.gen.ts regenerated after /nodes removal
2026-04-13 07:41:19 +02:00

109 lines
2.5 KiB
TypeScript

import { normalizeLanguage } from "./jellyfin";
export interface RadarrConfig {
url: string;
apiKey: string;
}
function headers(apiKey: string): Record<string, string> {
return { "X-Api-Key": apiKey };
}
export async function testConnection(cfg: RadarrConfig): Promise<{ ok: boolean; error?: string }> {
try {
const res = await fetch(`${cfg.url}/api/v3/system/status`, {
headers: headers(cfg.apiKey),
});
if (!res.ok) return { ok: false, error: `HTTP ${res.status}` };
return { ok: true };
} catch (e) {
return { ok: false, error: String(e) };
}
}
interface RadarrMovie {
tmdbId?: number;
imdbId?: string;
originalLanguage?: { name: string; nameTranslated?: string };
}
/** Returns ISO 639-2 original language or null. */
export async function getOriginalLanguage(
cfg: RadarrConfig,
ids: { tmdbId?: string; imdbId?: string },
): Promise<string | null> {
try {
let movie: RadarrMovie | null = null;
if (ids.tmdbId) {
const res = await fetch(`${cfg.url}/api/v3/movie?tmdbId=${ids.tmdbId}`, {
headers: headers(cfg.apiKey),
});
if (res.ok) {
const list = (await res.json()) as RadarrMovie[];
movie = list[0] ?? null;
}
}
if (!movie && ids.imdbId) {
const res = await fetch(`${cfg.url}/api/v3/movie`, {
headers: headers(cfg.apiKey),
});
if (res.ok) {
const list = (await res.json()) as (RadarrMovie & { imdbId?: string })[];
movie = list.find((m) => m.imdbId === ids.imdbId) ?? null;
}
}
if (!movie?.originalLanguage) return null;
return iso6391To6392(movie.originalLanguage.name) ?? null;
} catch {
return null;
}
}
// Radarr returns language names like "English", "French", "German", etc.
// Map them to ISO 639-2 codes.
const NAME_TO_639_2: Record<string, string> = {
english: "eng",
french: "fra",
german: "deu",
spanish: "spa",
italian: "ita",
portuguese: "por",
japanese: "jpn",
korean: "kor",
chinese: "zho",
arabic: "ara",
russian: "rus",
dutch: "nld",
swedish: "swe",
norwegian: "nor",
danish: "dan",
finnish: "fin",
polish: "pol",
turkish: "tur",
thai: "tha",
hindi: "hin",
hungarian: "hun",
czech: "ces",
romanian: "ron",
greek: "ell",
hebrew: "heb",
persian: "fas",
ukrainian: "ukr",
indonesian: "ind",
malay: "msa",
vietnamese: "vie",
catalan: "cat",
tamil: "tam",
telugu: "tel",
"brazilian portuguese": "por",
"portuguese (brazil)": "por",
};
function iso6391To6392(name: string): string | null {
const key = name.toLowerCase().trim();
return NAME_TO_639_2[key] ?? normalizeLanguage(key.slice(0, 3)) ?? null;
}