feat(mux): add muxing.default_language to override default track per type

Allows users to force a preferred audio/video/subtitle language as the MKV default track regardless of the title's original_language. Each track type falls back to its previous default rule when no match is found.
This commit is contained in:
imSp4rky
2026-05-14 14:24:23 -06:00
parent 0aca0e8888
commit ccf8bafaf7
2 changed files with 52 additions and 5 deletions

View File

@@ -428,13 +428,40 @@ class Tracks:
if config.muxing.get("set_title", True): if config.muxing.get("set_title", True):
cl.extend(["--title", title]) cl.extend(["--title", title])
default_language = config.muxing.get("default_language") or {}
preferred_video_lang = default_language.get("video")
preferred_audio_lang = default_language.get("audio")
preferred_subtitle_lang = default_language.get("subtitle")
preferred_video_idx: Optional[int] = None
if preferred_video_lang:
preferred_video_idx = next(
(idx for idx, v in enumerate(self.videos) if is_close_match(v.language, [preferred_video_lang])),
None,
)
preferred_audio_idx: Optional[int] = None
if preferred_audio_lang:
preferred_audio_idx = next(
(idx for idx, a in enumerate(self.audio) if is_close_match(a.language, [preferred_audio_lang])),
None,
)
preferred_subtitle_idx: Optional[int] = None
if preferred_subtitle_lang and not skip_subtitles:
preferred_subtitle_idx = next(
(idx for idx, s in enumerate(self.subtitles) if is_close_match(s.language, [preferred_subtitle_lang])),
None,
)
for i, vt in enumerate(self.videos): for i, vt in enumerate(self.videos):
if not vt.path or not vt.path.exists(): if not vt.path or not vt.path.exists():
raise ValueError("Video Track must be downloaded before muxing...") raise ValueError("Video Track must be downloaded before muxing...")
events.emit(events.Types.TRACK_MULTIPLEX, track=vt) events.emit(events.Types.TRACK_MULTIPLEX, track=vt)
is_default = False if preferred_video_idx is not None:
if title_language: is_default = i == preferred_video_idx
elif title_language:
is_default = vt.language == title_language is_default = vt.language == title_language
if not any(v.language == title_language for v in self.videos): if not any(v.language == title_language for v in self.videos):
is_default = vt.is_original_lang or i == 0 is_default = vt.is_original_lang or i == 0
@@ -490,6 +517,10 @@ class Tracks:
if not at.path or not at.path.exists(): if not at.path or not at.path.exists():
raise ValueError("Audio Track must be downloaded before muxing...") raise ValueError("Audio Track must be downloaded before muxing...")
events.emit(events.Types.TRACK_MULTIPLEX, track=at) events.emit(events.Types.TRACK_MULTIPLEX, track=at)
if preferred_audio_idx is not None:
audio_default = i == preferred_audio_idx
else:
audio_default = at.is_original_lang
cl.extend( cl.extend(
[ [
"--track-name", "--track-name",
@@ -497,7 +528,7 @@ class Tracks:
"--language", "--language",
f"0:{at.language}", f"0:{at.language}",
"--default-track", "--default-track",
f"0:{at.is_original_lang}", f"0:{audio_default}",
"--visual-impaired-flag", "--visual-impaired-flag",
f"0:{at.descriptive}", f"0:{at.descriptive}",
"--original-flag", "--original-flag",
@@ -511,11 +542,14 @@ class Tracks:
) )
if not skip_subtitles: if not skip_subtitles:
for st in self.subtitles: for i, st in enumerate(self.subtitles):
if not st.path or not st.path.exists(): if not st.path or not st.path.exists():
raise ValueError("Text Track must be downloaded before muxing...") raise ValueError("Text Track must be downloaded before muxing...")
events.emit(events.Types.TRACK_MULTIPLEX, track=st) events.emit(events.Types.TRACK_MULTIPLEX, track=st)
default = bool(self.audio and is_close_match(st.language, [self.audio[0].language]) and st.forced) if preferred_subtitle_idx is not None:
default = i == preferred_subtitle_idx
else:
default = bool(self.audio and is_close_match(st.language, [self.audio[0].language]) and st.forced)
cl.extend( cl.extend(
[ [
"--track-name", "--track-name",

View File

@@ -147,6 +147,19 @@ muxing:
# false: Separate MKV per (quality, audio_codec) combination # false: Separate MKV per (quality, audio_codec) combination
# Example: Title.1080p.AAC.mkv, Title.1080p.EC3.mkv # Example: Title.1080p.AAC.mkv, Title.1080p.EC3.mkv
merge_audio: true merge_audio: true
# default_language: Override which track is flagged as the default in the muxed MKV.
# audio: BCP-47 tag of the preferred default audio track (e.g. pl, en, pt-BR).
# Wins over the title's original_language. Falls back to is_original_lang
# if no matching track is present.
# video: BCP-47 tag of the preferred default video track. Falls back to the
# original-language / first-track rule if no match is found.
# subtitle: BCP-47 tag of the preferred default subtitle track. Falls back to
# the existing rule (forced sub matching the audio language) if no
# matching subtitle is present.
# default_language:
# audio: pl
# video: pl
# subtitle: pl
# Login credentials for each Service # Login credentials for each Service
credentials: credentials: