mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-05-17 14:29:27 +00:00
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:
@@ -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,10 +542,13 @@ 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)
|
||||||
|
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)
|
default = bool(self.audio and is_close_match(st.language, [self.audio[0].language]) and st.forced)
|
||||||
cl.extend(
|
cl.extend(
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user