Fixed channel override not triggering multi-audio track MKV downloads.
Also adds proper `ValueError` handling in the channel overwrites API view to return a structured 400 error response.
This commit is contained in:
parent
6fc2bf36b6
commit
d5fc906641
|
|
@ -19,6 +19,7 @@ class ChannelOverwriteSerializer(
|
|||
audio_multistream = serializers.BooleanField(
|
||||
required=False, allow_null=True
|
||||
)
|
||||
audio_languages = serializers.CharField(required=False, allow_null=True)
|
||||
autodelete_days = serializers.IntegerField(required=False, allow_null=True)
|
||||
index_playlists = serializers.BooleanField(required=False, allow_null=True)
|
||||
integrate_sponsorblock = serializers.BooleanField(
|
||||
|
|
|
|||
|
|
@ -268,6 +268,7 @@ class YoutubeChannel(YouTubeItem):
|
|||
"download_format",
|
||||
"download_container",
|
||||
"audio_multistream",
|
||||
"audio_languages",
|
||||
"autodelete_days",
|
||||
"index_playlists",
|
||||
"integrate_sponsorblock",
|
||||
|
|
|
|||
|
|
@ -175,7 +175,11 @@ class ChannelApiView(ApiBaseView):
|
|||
)
|
||||
|
||||
if overwrites := validated_data.get("channel_overwrites"):
|
||||
channel_overwrites(channel_id, overwrites)
|
||||
try:
|
||||
channel_overwrites(channel_id, overwrites)
|
||||
except ValueError as err:
|
||||
error = ErrorResponseSerializer({"error": str(err)})
|
||||
return Response(error.data, status=400)
|
||||
if overwrites.get("index_playlists"):
|
||||
index_channel_playlists.delay(channel_id)
|
||||
|
||||
|
|
|
|||
|
|
@ -391,6 +391,27 @@ class VideoDownloader(DownloaderBase):
|
|||
if lang.strip()
|
||||
]
|
||||
|
||||
def _is_audio_multistream_enabled(self, channel_id: str) -> bool:
|
||||
"""return True if audio_multistream is enabled globally or via channel overwrite"""
|
||||
overwrites = self.channel_overwrites.get(channel_id, {})
|
||||
if "audio_multistream" in overwrites and overwrites["audio_multistream"] is not None:
|
||||
return bool(overwrites["audio_multistream"])
|
||||
return bool(self.config["downloads"].get("audio_multistream"))
|
||||
|
||||
@staticmethod
|
||||
def _discover_audio_languages(formats: list[dict]) -> list[str]:
|
||||
"""collect all unique language codes from available audio formats"""
|
||||
seen: set[str] = set()
|
||||
langs: list[str] = []
|
||||
for f in formats:
|
||||
if (f.get("acodec") or "none") == "none":
|
||||
continue
|
||||
lang = (f.get("language") or "").strip()
|
||||
if lang and lang not in seen:
|
||||
seen.add(lang)
|
||||
langs.append(lang)
|
||||
return langs
|
||||
|
||||
def _dl_single_vid(self, youtube_id: str, channel_id: str) -> bool:
|
||||
"""download single video, with optional multi-language audio merging"""
|
||||
obs = self.obs.copy()
|
||||
|
|
@ -400,9 +421,20 @@ class VideoDownloader(DownloaderBase):
|
|||
languages = self._get_audio_languages(channel_id)
|
||||
hls_formats: dict[str, str] = {} # lang -> format_id for HLS post-process
|
||||
|
||||
if not languages and self._is_audio_multistream_enabled(channel_id):
|
||||
# audio_multistream is on but no explicit language list – auto-discover
|
||||
print(f"{youtube_id}: audio_multistream enabled, auto-discovering languages")
|
||||
formats = self._get_formats(youtube_id)
|
||||
if formats:
|
||||
languages = self._discover_audio_languages(formats)
|
||||
print(f"{youtube_id}: discovered audio languages: {languages}")
|
||||
else:
|
||||
formats = None # will be fetched below if needed
|
||||
|
||||
if languages:
|
||||
print(f"{youtube_id}: applying audio languages {languages}")
|
||||
formats = self._get_formats(youtube_id)
|
||||
if formats is None:
|
||||
formats = self._get_formats(youtube_id)
|
||||
if formats:
|
||||
dash_fmt, hls_formats = self._resolve_audio_formats(
|
||||
formats, languages
|
||||
|
|
|
|||
|
|
@ -342,12 +342,17 @@ class YoutubeVideo(YouTubeItem, YoutubeSubtitle):
|
|||
if channel_overwrites.get("download_container"):
|
||||
container = channel_overwrites.get("download_container")
|
||||
|
||||
# audio_languages forces mkv for multi-stream support
|
||||
# audio_languages or audio_multistream forces mkv for multi-stream support
|
||||
audio_languages = (
|
||||
channel_overwrites.get("audio_languages")
|
||||
or self.config["downloads"].get("audio_languages")
|
||||
)
|
||||
if audio_languages:
|
||||
audio_multistream = (
|
||||
channel_overwrites.get("audio_multistream")
|
||||
if channel_overwrites.get("audio_multistream") is not None
|
||||
else self.config["downloads"].get("audio_multistream")
|
||||
)
|
||||
if audio_languages or audio_multistream:
|
||||
container = "mkv"
|
||||
|
||||
return container
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ const ChannelAbout = () => {
|
|||
const [downloadContainer, setDownloadContainer] = useState<'mp4' | 'mkv' | null>(null);
|
||||
const [audioMultistream, setAudioMultistream] = useState<boolean | null>(null);
|
||||
const [audioMultistreamWarning, setAudioMultistreamWarning] = useState<string | null>(null);
|
||||
const [audioLanguages, setAudioLanguages] = useState<string | null>(null);
|
||||
const [autoDeleteAfter, setAutoDeleteAfter] = useState<number | null>(null);
|
||||
const [indexPlaylists, setIndexPlaylists] = useState(false);
|
||||
const [enableSponsorblock, setEnableSponsorblock] = useState<boolean | null>(null);
|
||||
|
|
@ -76,6 +77,7 @@ const ChannelAbout = () => {
|
|||
channelResponseData?.channel_overwrites?.audio_multistream ?? null,
|
||||
);
|
||||
setAudioMultistreamWarning(null);
|
||||
setAudioLanguages(channelResponseData?.channel_overwrites?.audio_languages ?? null);
|
||||
setAutoDeleteAfter(channelResponseData?.channel_overwrites?.autodelete_days ?? null);
|
||||
setIndexPlaylists(channelResponseData?.channel_overwrites?.index_playlists ?? false);
|
||||
setEnableSponsorblock(
|
||||
|
|
@ -356,6 +358,19 @@ const ChannelAbout = () => {
|
|||
<p className="settings-error">{audioMultistreamWarning}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="settings-box-wrapper">
|
||||
<div>
|
||||
<p>Audio Languages</p>
|
||||
</div>
|
||||
<InputConfig
|
||||
type="text"
|
||||
name="audio_languages"
|
||||
value={audioLanguages}
|
||||
setValue={setAudioLanguages}
|
||||
oldValue={channel.channel_overwrites?.audio_languages ?? null}
|
||||
updateCallback={handleUpdateConfig}
|
||||
/>
|
||||
</div>
|
||||
<div className="settings-box-wrapper">
|
||||
<div>
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ type ChannelOverwritesType = {
|
|||
download_format: string | null;
|
||||
download_container: 'mp4' | 'mkv' | null;
|
||||
audio_multistream: boolean | null;
|
||||
audio_languages: string | null;
|
||||
autodelete_days: number | null;
|
||||
index_playlists: boolean | null;
|
||||
integrate_sponsorblock: boolean | null;
|
||||
|
|
|
|||
Loading…
Reference in New Issue