fix(audio): accept immersive channel layouts like 5.1.4

parse_channels rejected a three-figure layout, so a remote download crashed rebuilding the track from JSON. Add channel_total so --channels can compare either form.
This commit is contained in:
imSp4rky
2026-09-04 19:27:39 -06:00
parent 55bed4876c
commit dffb72e41d
4 changed files with 74 additions and 7 deletions
+3 -1
View File
@@ -2703,7 +2703,9 @@ class dl:
sys.exit(1)
if channels:
title.tracks.select_audio(
lambda x: bool(x.channels and math.ceil(x.channels) == math.ceil(channels))
lambda x: bool(
x.channels and math.ceil(Audio.channel_total(x.channels)) == math.ceil(channels)
)
)
if not title.tracks.audio:
self.log.error(f"There's no {channels} Audio Track...")
+23 -5
View File
@@ -191,14 +191,17 @@ class Audio(Track):
)
@staticmethod
def parse_channels(channels: Union[str, int, float]) -> float:
def parse_channels(channels: Union[str, int, float]) -> Union[float, str]:
"""
Converts a Channel string to a float representing the audio channel layout.
E.g. "3" -> "3.0", "2.1" -> "2.1", ".1" -> "0.1".
An immersive layout names its height channels in a third figure, e.g. "5.1.4",
which no float can hold, so it stays the string it came as. Use channel_total for
arithmetic on either form.
This does not validate channel strings as genuine channel counts or valid layouts.
It does not convert the value to assume a sub speaker channel layout, e.g. 5.1->6.0.
It also cannot read expanded surround sound channel layout strings like 7.1.2.
"""
if isinstance(channels, str):
# TODO: Support all possible DASH channel configurations (https://datatracker.ietf.org/doc/html/rfc8216)
@@ -206,13 +209,28 @@ class Audio(Track):
return 2.0
elif channels.upper() == "F801":
return 5.1
elif channels.replace("ch", "").replace(".", "", 1).isdigit():
# e.g., '2ch', '2', '2.0', '5.1ch', '5.1'
return float(channels.replace("ch", ""))
layout = channels.replace("ch", "")
if layout.count(".") == 2 and all(part.isdigit() for part in layout.split(".")):
return layout
elif layout.replace(".", "", 1).isdigit():
return float(layout)
raise NotImplementedError(f"Unsupported Channels string value, '{channels}'")
return float(channels)
@staticmethod
def channel_total(channels: Union[str, int, float]) -> float:
"""
Total number of channels in either channel form, so a caller can compare both.
A float layout keeps its own value, because a caller compares those by rounding
the sub channel up, e.g. 5.1 and 6.0 both match. An immersive layout has no such
float, so this adds its figures: "5.1.4" -> 10.0.
"""
if isinstance(channels, str) and channels.count(".") == 2:
return float(sum(int(part) for part in channels.split(".")))
return float(channels)
def to_music_container(self) -> bool:
"""Remux the track into the container of its codec, as a standalone audio file.