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>
86 lines
1.9 KiB
TypeScript
86 lines
1.9 KiB
TypeScript
import { normalizeLanguage } from './jellyfin';
|
|
|
|
export interface SonarrConfig {
|
|
url: string;
|
|
apiKey: string;
|
|
}
|
|
|
|
function headers(apiKey: string): Record<string, string> {
|
|
return { 'X-Api-Key': apiKey };
|
|
}
|
|
|
|
export async function testConnection(cfg: SonarrConfig): 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 SonarrSeries {
|
|
tvdbId?: number;
|
|
originalLanguage?: { name: string };
|
|
}
|
|
|
|
/** Returns ISO 639-2 original language for a series or null. */
|
|
export async function getOriginalLanguage(
|
|
cfg: SonarrConfig,
|
|
tvdbId: string
|
|
): Promise<string | null> {
|
|
try {
|
|
const res = await fetch(`${cfg.url}/api/v3/series?tvdbId=${tvdbId}`, {
|
|
headers: headers(cfg.apiKey),
|
|
});
|
|
if (!res.ok) return null;
|
|
|
|
const list = (await res.json()) as SonarrSeries[];
|
|
const series = list[0];
|
|
if (!series?.originalLanguage) return null;
|
|
return languageNameToCode(series.originalLanguage.name) ?? null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
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',
|
|
};
|
|
|
|
function languageNameToCode(name: string): string | null {
|
|
const key = name.toLowerCase().trim();
|
|
return NAME_TO_639_2[key] ?? normalizeLanguage(key.slice(0, 3)) ?? null;
|
|
}
|