Files
netfelix-audio-fix/server/services/radarr.ts
Felix Förtsch 5ac44b7551 restructure to react spa + hono api, fix missing server/ and lib/
rewrite from monolithic hono jsx to react 19 spa with tanstack router
+ hono json api backend. add scan, review, execute, nodes, and setup
pages. multi-stage dockerfile (node for vite build, bun for runtime).

previously, server/ and src/shared/lib/ were silently excluded by
global gitignore patterns (/server/ from emacs, lib/ from python).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 22:57:40 +01: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;
}