From 43585a76cb4199dc2ba61022450d76ed3922a267 Mon Sep 17 00:00:00 2001 From: rlaphoenix Date: Sun, 14 Apr 2024 03:13:46 +0100 Subject: [PATCH] fix(Audio): Optionalise constructor args, add doc-string & checks Some HLS playlists can have extremely limited information so to accommodate this we need to make the Audio track support having almost no information. This isn't ideal but it's really the only solution. --- devine/core/tracks/audio.py | 62 ++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/devine/core/tracks/audio.py b/devine/core/tracks/audio.py index 19a4765..7a73189 100644 --- a/devine/core/tracks/audio.py +++ b/devine/core/tracks/audio.py @@ -64,15 +64,63 @@ class Audio(Track): return Audio.Codec.OGG raise ValueError(f"The Content Profile '{profile}' is not a supported Audio Codec") - def __init__(self, *args: Any, codec: Audio.Codec, bitrate: Union[str, int, float], - channels: Optional[Union[str, int, float]] = None, joc: int = 0, descriptive: bool = False, - **kwargs: Any): + def __init__( + self, + *args: Any, + codec: Optional[Audio.Codec] = None, + bitrate: Optional[Union[str, int, float]] = None, + channels: Optional[Union[str, int, float]] = None, + joc: Optional[int] = None, + descriptive: Union[bool, int] = False, + **kwargs: Any + ): + """ + Create a new Audio track object. + + Parameters: + codec: An Audio.Codec enum representing the audio codec. + If not specified, MediaInfo will be used to retrieve the codec + once the track has been downloaded. + bitrate: A number or float representing the average bandwidth in bytes/s. + Float values are rounded up to the nearest integer. + channels: A number, float, or string representing the number of audio channels. + Strings may represent numbers or floats. Expanded layouts like 7.1.1 is + not supported. All numbers and strings will be cast to float. + joc: The number of Joint-Object-Coding Channels/Objects in the audio stream. + descriptive: Mark this audio as being descriptive audio for the blind. + + Note: If codec, bitrate, channels, or joc is not specified some checks may be + skipped or assume a value. Specifying as much information as possible is highly + recommended. + """ super().__init__(*args, **kwargs) - # required + + if not isinstance(codec, (Audio.Codec, type(None))): + raise TypeError(f"Expected codec to be a {Audio.Codec}, not {codec!r}") + if not isinstance(bitrate, (str, int, float, type(None))): + raise TypeError(f"Expected bitrate to be a {str}, {int}, or {float}, not {bitrate!r}") + if not isinstance(channels, (str, int, float, type(None))): + raise TypeError(f"Expected channels to be a {str}, {int}, or {float}, not {channels!r}") + if not isinstance(joc, (int, type(None))): + raise TypeError(f"Expected joc to be a {int}, not {joc!r}") + if ( + not isinstance(descriptive, (bool, int)) or + (isinstance(descriptive, int) and descriptive not in (0, 1)) + ): + raise TypeError(f"Expected descriptive to be a {bool} or bool-like {int}, not {descriptive!r}") + self.codec = codec - self.bitrate = int(math.ceil(float(bitrate))) if bitrate else None - self.channels = self.parse_channels(channels) if channels else None - # optional + + try: + self.bitrate = int(math.ceil(float(bitrate))) if bitrate else None + except (ValueError, TypeError) as e: + raise ValueError(f"Expected bitrate to be a number or float, {e}") + + try: + self.channels = self.parse_channels(channels) if channels else None + except (ValueError, NotImplementedError) as e: + raise ValueError(f"Expected channels to be a number, float, or a string, {e}") + self.joc = joc self.descriptive = bool(descriptive)