per-track language override on audio detail page
Build and Push Docker Image / build (push) Successful in 3m3s
Build and Push Docker Image / build (push) Successful in 3m3s
adds stream_decisions.custom_language (ISO 639-2 code or null) so the user can correct a mislabeled audio track — e.g. a Spanish dub tagged "und" in the container — without going through Jellyfin. the override wins over stream.language everywhere it matters: the analyzer reads it for keep/remove decisions and track ordering, the ffmpeg command builder writes it as both the language metadata tag and the harmonized track title, and reanalyze preserves it across reruns and rescans. on the audio detail page, each pending audio row swaps its language cell for an inline <select> populated from LANG_NAMES. picking the raw file language clears the override; anything else sets it and triggers a server-side reanalyze so keep/remove + target_index update immediately. a small ✎ hint marks overridden tracks. rebuilt commands tag the output accordingly so Jellyfin reads the corrected language. PATCH /api/review/:id/stream/:streamId/language validates the code against LANG_NAMES (accepts ISO 639-1/2/2B aliases, rejects garbage) and runs reanalyze inside. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -150,7 +150,7 @@ function computeExtractionEntries(allStreams: MediaStream[], basePath: string):
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
const LANG_NAMES: Record<string, string> = {
|
||||
export const LANG_NAMES: Record<string, string> = {
|
||||
eng: "English",
|
||||
deu: "German",
|
||||
spa: "Spanish",
|
||||
@@ -207,7 +207,7 @@ function formatChannels(n: number | null): string | null {
|
||||
return `${n}ch`;
|
||||
}
|
||||
|
||||
function trackTitle(stream: MediaStream): string | null {
|
||||
function trackTitle(stream: MediaStream, customLanguage: string | null = null): string | null {
|
||||
if (stream.type === "Subtitle") {
|
||||
// Subtitles always get a clean language-based title so Jellyfin displays
|
||||
// "German", "English (Forced)", etc. regardless of the original file title.
|
||||
@@ -225,8 +225,11 @@ function trackTitle(stream: MediaStream): string | null {
|
||||
// the review UI to drop unwanted tracks before we get here, so by this
|
||||
// point every kept audio track is a primary track that deserves a clean
|
||||
// canonical label. If a user wants a different title, custom_title on
|
||||
// the decision still wins (see buildStreamFlags).
|
||||
const lang = stream.language ? normalizeLanguage(stream.language) : null;
|
||||
// the decision still wins (see buildStreamFlags). A per-stream language
|
||||
// override comes through as customLanguage so "UND → Spanish" renames
|
||||
// flow through to the harmonized title too.
|
||||
const rawLang = customLanguage ?? stream.language;
|
||||
const lang = rawLang ? normalizeLanguage(rawLang) : null;
|
||||
const langPart = lang ? lang.toUpperCase() : null;
|
||||
const codecPart = stream.codec ? stream.codec.toUpperCase() : null;
|
||||
const channelsPart = formatChannels(stream.channels);
|
||||
@@ -278,10 +281,13 @@ function buildStreamFlags(kept: { stream: MediaStream; dec: StreamDecision }[]):
|
||||
audioKept.forEach((k, i) => {
|
||||
args.push(`-disposition:a:${i}`, i === 0 ? "default" : "0");
|
||||
|
||||
const title = k.dec.custom_title ?? trackTitle(k.stream);
|
||||
const title = k.dec.custom_title ?? trackTitle(k.stream, k.dec.custom_language);
|
||||
if (title) args.push(`-metadata:s:a:${i}`, `title=${shellQuote(title)}`);
|
||||
|
||||
const lang = k.stream.language ? normalizeLanguage(k.stream.language) : "und";
|
||||
// Per-stream language override wins over the raw file tag so the
|
||||
// ffmpeg output carries the corrected language (e.g. "und" → "spa").
|
||||
const rawLang = k.dec.custom_language ?? k.stream.language;
|
||||
const lang = rawLang ? normalizeLanguage(rawLang) : "und";
|
||||
args.push(`-metadata:s:a:${i}`, `language=${lang}`);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user