diff --git a/vinetrimmer/commands/dl.py b/vinetrimmer/commands/dl.py index b85e84a..10fc6f1 100644 --- a/vinetrimmer/commands/dl.py +++ b/vinetrimmer/commands/dl.py @@ -115,12 +115,14 @@ def get_cdm(log, service, profile=None, cdm_name=None): cdm_api = next(iter(x for x in config.cdm_api if x["name"] == cdm_name), None) if cdm_api: + device = None try: - return RemoteCdm(**cdm_api) + device = RemoteCdm(**cdm_api) except: from vinetrimmer.utils.widevine.device import RemoteDevice - return RemoteDevice(**cdm_api) # seller distributed some wack version of serve in pywidevine - + device = RemoteDevice(**cdm_api) # seller distributed some wack version of serve in pywidevine + finally: + return device raise ValueError(f"Device {cdm_name!r} not found") @@ -219,7 +221,9 @@ def get_credentials(service, profile="default"): default=None, help="Audio Bitrate, defaults to Max.") @click.option("-ac", "--audio-channels", type=str, default=None, - help="Select Audio by Channels Configuration, e.g `2.0`, `5.1`, `2.0,5.1`") + help="Select Audio by Channels Configuration, e.g `2.0`, `5.1`, `2.0,5.1`") +@click.option("-mac", "--max-audio-compatability", is_flag=True, default=False, + help="Select multiple audios for maximum compatibility with all devices") @click.option("-aa", "--atmos", is_flag=True, default=False, help="Prefer Atmos Audio") @click.option("-r", "--range", "range_", callback=range_param, default="SDR", @@ -322,7 +326,7 @@ def dl(ctx, profile, cdm, *_, **__): @dl.result_callback() @click.pass_context -def result(ctx, service, quality, closest_resolution, range_, wanted, alang, slang, acodec, audio_only, subs_only, chapters_only, audio_description, audio_channels, +def result(ctx, service, quality, closest_resolution, range_, wanted, alang, slang, acodec, audio_only, subs_only, chapters_only, audio_description, audio_channels, max_audio_compatability, list_, keys, cache, no_cache, no_subs, no_audio, no_video, no_chapters, atmos, vbitrate, abitrate: int, no_mux, mux, selected, latest_episode, strip_sdh, *_, **__): def ccextractor(): log.info("Extracting EIA-608 captions from stream with CCExtractor") @@ -422,7 +426,7 @@ def result(ctx, service, quality, closest_resolution, range_, wanted, alang, sla quality = closest_res title.tracks.select_videos(by_quality=quality, by_vbitrate=vbitrate, by_range=range_, one_only=True) - title.tracks.select_audios(by_language=alang, by_bitrate=abitrate, with_descriptive=audio_description, by_codec=acodec, by_channels=audio_channels) + title.tracks.select_audios(by_language=alang, by_bitrate=abitrate, with_descriptive=audio_description, by_codec=acodec, by_channels=audio_channels, max_audio_compatability=max_audio_compatability) title.tracks.select_subtitles(by_language=slang, with_forced=True) except ValueError as e: log.error(f" - {e}") diff --git a/vinetrimmer/objects/tracks.py b/vinetrimmer/objects/tracks.py index a83dbe4..62f40ad 100644 --- a/vinetrimmer/objects/tracks.py +++ b/vinetrimmer/objects/tracks.py @@ -1196,31 +1196,62 @@ class Tracks: by_bitrate=None, by_channels=None, by_codec=None, + max_audio_compatability: bool = False, should_fallback: bool = False ) -> None: """Filter audio tracks by language and other criteria.""" if not with_descriptive: self.audios = [x for x in self.audios if not x.descriptive] - if by_codec: + if max_audio_compatability and by_channels and by_codec: by_codec = by_codec.split(",") - codec_audio = [] - for codec in by_codec: - codec_audio.append(list(filter(lambda x: any(y for y in self.AUDIO_CODEC_MAP[codec] if y in x.codec), self.audios))[0]) - if not codec_audio and not should_fallback: - raise ValueError(f"There's no {by_codec} audio tracks. Aborting.") - else: - self.audios = (codec_audio if codec_audio else self.audios) - if by_channels: by_channels = by_channels.split(",") - channels_audio = [] - for channel in by_channels: - channels_audio.append(list(filter(lambda x: x.channels == channel, self.audios))[0]) + audios = [] + inner_audios = [] - if not channels_audio and not should_fallback: - raise ValueError(f"There's no {by_channels} {by_codec} audio tracks. Aborting.") - else: - self.audios = (channels_audio if channels_audio else self.audios) + for codec in by_codec: + for channels in by_channels: + + try: + inner_audios.extend( + list( + filter( + lambda x: ( + any(y for y in self.AUDIO_CODEC_MAP[codec] if y in x.codec) + and x.channels == channels + ), + self.audios + ) + ) + ) + + except: pass + + else: + if by_codec: + by_codec = by_codec.split(",") + codec_audio = [] + for codec in by_codec: + codec_audio.append(list(filter(lambda x: any(y for y in self.AUDIO_CODEC_MAP[codec] if y in x.codec), self.audios))[0]) + if not codec_audio and not should_fallback: + raise ValueError(f"There's no {by_codec} audio tracks. Aborting.") + else: + self.audios = (codec_audio if codec_audio else self.audios) + + if by_channels: + by_channels = by_channels.split(",") + channels_audio = [] + + for channel in by_channels: + channels_audio.append(list(filter(lambda x: x.channels == channel, self.audios))[0]) + + if not channels_audio and not should_fallback: + raise ValueError(f"There's no {by_channels} {by_codec} audio tracks. Aborting.") + else: + self.audios = (channels_audio if channels_audio else self.audios) + + if by_codec and by_channels: + self.audios = self.audios[::-1] if with_atmos: atmos_audio = list(filter(lambda x: x.atmos, self.audios)) self.audios = (atmos_audio if atmos_audio else self.audios) # Fallback if no atmos @@ -1228,7 +1259,9 @@ class Tracks: self.audios = [x for x in self.audios if int(x.bitrate) <= int(by_bitrate * 1000)] if by_language: one_per_lang = (False if - ( + ( + max_audio_compatability + or (isinstance(by_codec, List) and len(by_codec) > 1) or (isinstance(by_channels, List) and len(by_channels) > 1) diff --git a/vinetrimmer/utils/click.py b/vinetrimmer/utils/click.py index 0433e2d..4916e01 100644 --- a/vinetrimmer/utils/click.py +++ b/vinetrimmer/utils/click.py @@ -81,26 +81,30 @@ def _choice(ctx, param, value, value_map): def acodec_param(ctx, param, value): - values = value.split(",") - acodecs = [] - for x in values: - acodecs.append(_choice(ctx, param, x, { - "aac": "AAC", - "ac3": "AC3", - "ac-3": "AC3", - "dd": "AC3", - "ec3": "EC3", - "ec-3": "EC3", - "eac3": "EC3", - "e-ac3": "EC3", - "e-ac-3": "EC3", - "dd+": "EC3", - "ddp": "EC3", - "vorb": "VORB", - "vorbis": "VORB", - "opus": "OPUS", - })) - return ",".join(acodecs) + selectable_codecs = { + "aac": "AAC", + "ac3": "AC3", + "ac-3": "AC3", + "dd": "AC3", + "ec3": "EC3", + "ec-3": "EC3", + "eac3": "EC3", + "e-ac3": "EC3", + "e-ac-3": "EC3", + "dd+": "EC3", + "ddp": "EC3", + "vorb": "VORB", + "vorbis": "VORB", + "opus": "OPUS", + } + if value is None: + return _choice(ctx, param, value, selectable_codecs) + else: + values = value.split(",") + acodecs = [] + for x in values: + acodecs.append(_choice(ctx, param, x, selectable_codecs)) + return ",".join(acodecs)