apply codex code review: fix useEffect refetch loops, dead routes, subtitle job_type leftovers
All checks were successful
Build and Push Docker Image / build (push) Successful in 36s
All checks were successful
Build and Push Docker Image / build (push) Successful in 36s
All ack'd as real bugs: frontend - AudioDetailPage / SubtitleDetailPage / PathsPage / ScanPage / SubtitleListPage / ExecutePage: load() was a fresh function reference every render, so 'useEffect(() => load(), [load])' refetched on every render. Wrap each in useCallback with the right deps ([id], [filter], or []). - SetupPage: langsLoaded was useState; setting it inside load() retriggered the same effect → infinite loop. Switch to useRef. Also wrap saveJellyfin/ Radarr/Sonarr in async fns so they return Promise<void> (matches the consumer signatures, fixes the latent TS error). - DashboardPage: redirect target /setup doesn't exist; the route is /settings. - ExecutePage: <>...</> fragment with two <tr> children had keys on the rows but not on the fragment → React reconciliation warning. Use <Fragment key>. jobTypeLabel + badge variant still branched on the removed 'subtitle' job_type — relabel to 'Audio Transcode' / 'Audio Remux' and use 'manual'/'noop' variants. server - review.ts + scan.ts: parseLanguageList helper catches JSON errors and enforces array-of-strings shape with a fallback. A corrupted config row would otherwise throw mid-scan.
This commit is contained in:
@@ -11,7 +11,21 @@ const app = new Hono();
|
||||
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
function getSubtitleLanguages(): string[] {
|
||||
return JSON.parse(getConfig("subtitle_languages") ?? '["eng","deu","spa"]');
|
||||
return parseLanguageList(getConfig("subtitle_languages"), ["eng", "deu", "spa"]);
|
||||
}
|
||||
|
||||
function getAudioLanguages(): string[] {
|
||||
return parseLanguageList(getConfig("audio_languages"), []);
|
||||
}
|
||||
|
||||
function parseLanguageList(raw: string | null, fallback: string[]): string[] {
|
||||
if (!raw) return fallback;
|
||||
try {
|
||||
const parsed = JSON.parse(raw);
|
||||
return Array.isArray(parsed) ? parsed.filter((v): v is string => typeof v === "string") : fallback;
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
function countsByFilter(db: ReturnType<typeof getDb>): Record<string, number> {
|
||||
@@ -114,7 +128,7 @@ function reanalyze(db: ReturnType<typeof getDb>, itemId: number, preservedTitles
|
||||
.prepare("SELECT * FROM media_streams WHERE item_id = ? ORDER BY stream_index")
|
||||
.all(itemId) as MediaStream[];
|
||||
const subtitleLanguages = getSubtitleLanguages();
|
||||
const audioLanguages: string[] = JSON.parse(getConfig("audio_languages") ?? "[]");
|
||||
const audioLanguages = getAudioLanguages();
|
||||
const analysis = analyzeItem(
|
||||
{ original_language: item.original_language, needs_review: item.needs_review, container: item.container },
|
||||
streams,
|
||||
@@ -186,7 +200,7 @@ function recomputePlanAfterToggle(db: ReturnType<typeof getDb>, itemId: number):
|
||||
}[];
|
||||
|
||||
const origLang = item.original_language ? normalizeLanguage(item.original_language) : null;
|
||||
const audioLanguages: string[] = JSON.parse(getConfig("audio_languages") ?? "[]");
|
||||
const audioLanguages = getAudioLanguages();
|
||||
|
||||
// Re-assign target_index based on current actions
|
||||
const decWithIdx = decisions.map((d) => ({
|
||||
|
||||
Reference in New Issue
Block a user