drop the subtitle-languages setting, it never influenced extraction
All checks were successful
Build and Push Docker Image / build (push) Successful in 53s
All checks were successful
Build and Push Docker Image / build (push) Successful in 53s
analyzer removes every subtitle unconditionally (see case 'Subtitle' in decideAction) and the pipeline extracts all of them to sidecars — the config was purely informational and only subtitles.ts echoed it back as 'keepLanguages' for a subtitle-manager ui that doesn't exist yet. we'll revive language preferences inside that manager when it ships. removes: the settings card + ui state, POST /api/settings/subtitle-languages, the config default, the SUBTITLE_LANGUAGES env mapping, AnalyzerConfig's subtitleLanguages field, RescanConfig's subtitleLanguages field, every caller site (scan.ts / execute.ts / review.ts), and the keepLanguages surface in subtitles.ts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -71,7 +71,6 @@ async function refreshItemFromJellyfin(itemId: number): Promise<void> {
|
||||
db,
|
||||
fresh,
|
||||
{
|
||||
subtitleLanguages: parseLanguageList(cfg.subtitle_languages, ["eng", "deu", "spa"]),
|
||||
audioLanguages: parseLanguageList(cfg.audio_languages, []),
|
||||
radarr: radarrEnabled ? radarrCfg : null,
|
||||
sonarr: sonarrEnabled ? sonarrCfg : null,
|
||||
|
||||
@@ -10,10 +10,6 @@ const app = new Hono();
|
||||
|
||||
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
function getSubtitleLanguages(): string[] {
|
||||
return parseLanguageList(getConfig("subtitle_languages"), ["eng", "deu", "spa"]);
|
||||
}
|
||||
|
||||
function getAudioLanguages(): string[] {
|
||||
return parseLanguageList(getConfig("audio_languages"), []);
|
||||
}
|
||||
@@ -127,12 +123,11 @@ function reanalyze(db: ReturnType<typeof getDb>, itemId: number, preservedTitles
|
||||
const streams = db
|
||||
.prepare("SELECT * FROM media_streams WHERE item_id = ? ORDER BY stream_index")
|
||||
.all(itemId) as MediaStream[];
|
||||
const subtitleLanguages = getSubtitleLanguages();
|
||||
const audioLanguages = getAudioLanguages();
|
||||
const analysis = analyzeItem(
|
||||
{ original_language: item.original_language, needs_review: item.needs_review, container: item.container },
|
||||
streams,
|
||||
{ subtitleLanguages, audioLanguages },
|
||||
{ audioLanguages },
|
||||
);
|
||||
|
||||
db
|
||||
|
||||
@@ -150,7 +150,6 @@ async function runScan(limit: number | null = null): Promise<void> {
|
||||
|
||||
const cfg = getAllConfig();
|
||||
const jellyfinCfg = { url: cfg.jellyfin_url, apiKey: cfg.jellyfin_api_key, userId: cfg.jellyfin_user_id };
|
||||
const subtitleLanguages = parseLanguageList(cfg.subtitle_languages ?? null, ["eng", "deu", "spa"]);
|
||||
const audioLanguages = parseLanguageList(cfg.audio_languages ?? null, []);
|
||||
const radarrCfg = { url: cfg.radarr_url, apiKey: cfg.radarr_api_key };
|
||||
const sonarrCfg = { url: cfg.sonarr_url, apiKey: cfg.sonarr_api_key };
|
||||
@@ -183,7 +182,6 @@ async function runScan(limit: number | null = null): Promise<void> {
|
||||
let total = 0;
|
||||
|
||||
const rescanCfg = {
|
||||
subtitleLanguages,
|
||||
audioLanguages,
|
||||
radarr: radarrEnabled ? radarrCfg : null,
|
||||
sonarr: sonarrEnabled ? sonarrCfg : null,
|
||||
|
||||
@@ -83,14 +83,6 @@ app.post("/sonarr", async (c) => {
|
||||
return c.json({ ok: result.ok, saved: true, testError: result.ok ? undefined : result.error });
|
||||
});
|
||||
|
||||
app.post("/subtitle-languages", async (c) => {
|
||||
const body = await c.req.json<{ langs: string[] }>();
|
||||
if (body.langs?.length > 0) {
|
||||
setConfig("subtitle_languages", JSON.stringify(body.langs));
|
||||
}
|
||||
return c.json({ ok: true });
|
||||
});
|
||||
|
||||
app.post("/audio-languages", async (c) => {
|
||||
const body = await c.req.json<{ langs: string[] }>();
|
||||
setConfig("audio_languages", JSON.stringify(body.langs ?? []));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { unlinkSync } from "node:fs";
|
||||
import { dirname, resolve as resolvePath, sep } from "node:path";
|
||||
import { Hono } from "hono";
|
||||
import { getAllConfig, getConfig, getDb } from "../db/index";
|
||||
import { getAllConfig, getDb } from "../db/index";
|
||||
import { error as logError } from "../lib/log";
|
||||
import { parseId } from "../lib/validate";
|
||||
import { getItem, mapStream, normalizeLanguage, refreshItem } from "../services/jellyfin";
|
||||
@@ -288,16 +288,7 @@ app.get("/summary", (c) => {
|
||||
isCanonical: canonicalByLang.get(r.language) === r.title,
|
||||
}));
|
||||
|
||||
// Keep languages from config
|
||||
const raw = getConfig("subtitle_languages");
|
||||
let keepLanguages: string[] = [];
|
||||
try {
|
||||
keepLanguages = JSON.parse(raw ?? "[]");
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
return c.json({ embeddedCount, categories, titles, keepLanguages });
|
||||
return c.json({ embeddedCount, categories, titles });
|
||||
});
|
||||
|
||||
// ─── Detail ──────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -21,7 +21,6 @@ const ENV_MAP: Record<string, string> = {
|
||||
sonarr_url: "SONARR_URL",
|
||||
sonarr_api_key: "SONARR_API_KEY",
|
||||
sonarr_enabled: "SONARR_ENABLED",
|
||||
subtitle_languages: "SUBTITLE_LANGUAGES",
|
||||
audio_languages: "AUDIO_LANGUAGES",
|
||||
};
|
||||
|
||||
@@ -32,8 +31,7 @@ function envValue(key: string): string | null {
|
||||
const val = process.env[envKey];
|
||||
if (!val) return null;
|
||||
if (key.endsWith("_enabled")) return val === "1" || val.toLowerCase() === "true" ? "1" : "0";
|
||||
if (key === "subtitle_languages" || key === "audio_languages")
|
||||
return JSON.stringify(val.split(",").map((s) => s.trim()));
|
||||
if (key === "audio_languages") return JSON.stringify(val.split(",").map((s) => s.trim()));
|
||||
if (key.endsWith("_url")) return val.replace(/\/$/, "");
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -133,7 +133,6 @@ export const DEFAULT_CONFIG: Record<string, string> = {
|
||||
sonarr_url: "",
|
||||
sonarr_api_key: "",
|
||||
sonarr_enabled: "0",
|
||||
subtitle_languages: JSON.stringify(["eng", "deu", "spa"]),
|
||||
audio_languages: "[]",
|
||||
|
||||
scan_running: "0",
|
||||
|
||||
@@ -35,7 +35,6 @@ describe("analyzeItem — audio keep rules", () => {
|
||||
stream({ id: 4, type: "Audio", stream_index: 3, codec: "aac", language: "fra" }),
|
||||
];
|
||||
const result = analyzeItem({ ...ITEM_DEFAULTS, original_language: "eng" }, streams, {
|
||||
subtitleLanguages: [],
|
||||
audioLanguages: ["deu"],
|
||||
});
|
||||
const actions = Object.fromEntries(result.decisions.map((d) => [d.stream_id, d.action]));
|
||||
@@ -49,7 +48,6 @@ describe("analyzeItem — audio keep rules", () => {
|
||||
stream({ id: 3, type: "Audio", stream_index: 2, language: "fra" }),
|
||||
];
|
||||
const result = analyzeItem({ ...ITEM_DEFAULTS, original_language: null, needs_review: 1 }, streams, {
|
||||
subtitleLanguages: [],
|
||||
audioLanguages: ["deu"],
|
||||
});
|
||||
expect(result.decisions.every((d) => d.action === "keep")).toBe(true);
|
||||
@@ -62,7 +60,6 @@ describe("analyzeItem — audio keep rules", () => {
|
||||
stream({ id: 2, type: "Audio", stream_index: 1, language: null }),
|
||||
];
|
||||
const result = analyzeItem({ ...ITEM_DEFAULTS, original_language: "eng" }, streams, {
|
||||
subtitleLanguages: [],
|
||||
audioLanguages: [],
|
||||
});
|
||||
const byId = Object.fromEntries(result.decisions.map((d) => [d.stream_id, d.action]));
|
||||
@@ -72,7 +69,6 @@ describe("analyzeItem — audio keep rules", () => {
|
||||
test("normalizes language codes (ger → deu)", () => {
|
||||
const streams = [stream({ id: 1, type: "Audio", stream_index: 0, language: "ger" })];
|
||||
const result = analyzeItem({ ...ITEM_DEFAULTS, original_language: "deu" }, streams, {
|
||||
subtitleLanguages: [],
|
||||
audioLanguages: [],
|
||||
});
|
||||
expect(result.decisions[0].action).toBe("keep");
|
||||
@@ -87,7 +83,6 @@ describe("analyzeItem — audio ordering", () => {
|
||||
stream({ id: 12, type: "Audio", stream_index: 2, codec: "aac", language: "spa" }),
|
||||
];
|
||||
const result = analyzeItem({ ...ITEM_DEFAULTS, original_language: "eng" }, streams, {
|
||||
subtitleLanguages: [],
|
||||
audioLanguages: ["deu", "spa"],
|
||||
});
|
||||
const byId = Object.fromEntries(result.decisions.map((d) => [d.stream_id, d.target_index]));
|
||||
@@ -103,7 +98,6 @@ describe("analyzeItem — audio ordering", () => {
|
||||
stream({ id: 3, type: "Audio", stream_index: 2, language: "eng" }),
|
||||
];
|
||||
const result = analyzeItem({ ...ITEM_DEFAULTS, original_language: "eng" }, streams, {
|
||||
subtitleLanguages: [],
|
||||
audioLanguages: ["deu"],
|
||||
});
|
||||
expect(result.is_noop).toBe(false);
|
||||
@@ -116,7 +110,6 @@ describe("analyzeItem — audio ordering", () => {
|
||||
stream({ id: 3, type: "Audio", stream_index: 2, codec: "aac", language: "deu" }),
|
||||
];
|
||||
const result = analyzeItem({ ...ITEM_DEFAULTS, original_language: "eng" }, streams, {
|
||||
subtitleLanguages: [],
|
||||
audioLanguages: ["deu"],
|
||||
});
|
||||
expect(result.is_noop).toBe(true);
|
||||
@@ -128,7 +121,6 @@ describe("analyzeItem — audio ordering", () => {
|
||||
stream({ id: 2, type: "Audio", stream_index: 1, codec: "aac", language: "fra" }),
|
||||
];
|
||||
const result = analyzeItem({ ...ITEM_DEFAULTS, original_language: "eng" }, streams, {
|
||||
subtitleLanguages: [],
|
||||
audioLanguages: [],
|
||||
});
|
||||
expect(result.is_noop).toBe(false);
|
||||
@@ -142,7 +134,6 @@ describe("analyzeItem — subtitles & is_noop", () => {
|
||||
stream({ id: 2, type: "Subtitle", stream_index: 1, codec: "subrip", language: "eng" }),
|
||||
];
|
||||
const result = analyzeItem({ ...ITEM_DEFAULTS, original_language: "eng" }, streams, {
|
||||
subtitleLanguages: ["eng"],
|
||||
audioLanguages: [],
|
||||
});
|
||||
const subDec = result.decisions.find((d) => d.stream_id === 2);
|
||||
@@ -156,7 +147,6 @@ describe("analyzeItem — subtitles & is_noop", () => {
|
||||
stream({ id: 2, type: "Audio", stream_index: 1, codec: "aac", language: "eng", is_default: 1 }),
|
||||
];
|
||||
const result = analyzeItem({ ...ITEM_DEFAULTS, original_language: "eng" }, streams, {
|
||||
subtitleLanguages: [],
|
||||
audioLanguages: [],
|
||||
});
|
||||
expect(result.is_noop).toBe(true);
|
||||
@@ -168,7 +158,6 @@ describe("analyzeItem — subtitles & is_noop", () => {
|
||||
stream({ id: 2, type: "Audio", stream_index: 1, codec: "aac", language: "eng", is_default: 0 }),
|
||||
];
|
||||
const result = analyzeItem({ ...ITEM_DEFAULTS, original_language: "eng" }, streams, {
|
||||
subtitleLanguages: [],
|
||||
audioLanguages: [],
|
||||
});
|
||||
expect(result.is_noop).toBe(false);
|
||||
@@ -180,7 +169,6 @@ describe("analyzeItem — subtitles & is_noop", () => {
|
||||
stream({ id: 2, type: "Audio", stream_index: 1, codec: "aac", language: "en", is_default: 1 }),
|
||||
];
|
||||
const result = analyzeItem({ ...ITEM_DEFAULTS, original_language: "eng" }, streams, {
|
||||
subtitleLanguages: [],
|
||||
audioLanguages: [],
|
||||
});
|
||||
expect(result.is_noop).toBe(false);
|
||||
@@ -191,7 +179,6 @@ describe("analyzeItem — transcode targets", () => {
|
||||
test("DTS on mp4 → transcode to eac3", () => {
|
||||
const streams = [stream({ id: 1, type: "Audio", stream_index: 0, codec: "dts", language: "eng" })];
|
||||
const result = analyzeItem({ original_language: "eng", needs_review: 0, container: "mp4" }, streams, {
|
||||
subtitleLanguages: [],
|
||||
audioLanguages: [],
|
||||
});
|
||||
expect(result.decisions[0].transcode_codec).toBe("eac3");
|
||||
@@ -202,7 +189,6 @@ describe("analyzeItem — transcode targets", () => {
|
||||
test("AAC passes through without transcode", () => {
|
||||
const streams = [stream({ id: 1, type: "Audio", stream_index: 0, codec: "aac", language: "eng" })];
|
||||
const result = analyzeItem({ original_language: "eng", needs_review: 0, container: "mp4" }, streams, {
|
||||
subtitleLanguages: [],
|
||||
audioLanguages: [],
|
||||
});
|
||||
expect(result.decisions[0].transcode_codec).toBe(null);
|
||||
|
||||
@@ -3,7 +3,6 @@ import { computeAppleCompat, transcodeTarget } from "./apple-compat";
|
||||
import { normalizeLanguage } from "./jellyfin";
|
||||
|
||||
export interface AnalyzerConfig {
|
||||
subtitleLanguages: string[];
|
||||
audioLanguages: string[]; // additional languages to keep (after OG)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import { type RadarrLibrary, getOriginalLanguage as radarrLang } from "./radarr"
|
||||
import { type SonarrLibrary, getOriginalLanguage as sonarrLang } from "./sonarr";
|
||||
|
||||
export interface RescanConfig {
|
||||
subtitleLanguages: string[];
|
||||
audioLanguages: string[];
|
||||
radarr: { url: string; apiKey: string } | null;
|
||||
sonarr: { url: string; apiKey: string } | null;
|
||||
@@ -217,7 +216,7 @@ export async function upsertJellyfinItem(
|
||||
const analysis = analyzeItem(
|
||||
{ original_language: origLang, needs_review: needsReview, container: jellyfinItem.Container ?? null },
|
||||
streams,
|
||||
{ subtitleLanguages: cfg.subtitleLanguages, audioLanguages: cfg.audioLanguages },
|
||||
{ audioLanguages: cfg.audioLanguages },
|
||||
);
|
||||
|
||||
db
|
||||
|
||||
Reference in New Issue
Block a user