From 5f98f329af8ced5bec64fd6833a679a9a45f54e6 Mon Sep 17 00:00:00 2001 From: chu23465 <130033130+chu23465@users.noreply.github.com> Date: Sat, 19 Apr 2025 22:57:19 +0530 Subject: [PATCH] Feature and a fix Fix: --atmos works now with AMZN even if not -q 2160 or -r DV Feature: Selecting more than 1 track based on channels or codec. Not complete --- .gitignore | 1 + README.md | 2 +- vinetrimmer/commands/dl.py | 8 ++-- .../hisense_smarttv_he55a7000euwts_sl3000.prd | Bin 2108 -> 2108 bytes vinetrimmer/key_store.db | Bin 409600 -> 409600 bytes vinetrimmer/objects/tracks.py | 25 +++++++++--- vinetrimmer/services/amazon.py | 13 +++--- vinetrimmer/utils/click.py | 37 ++++++++++-------- 8 files changed, 53 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index 75e5899..d212dc7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /vinetrimmer/Cookies/ /vinetrimmer/Logs/ .DS_Store +key_store.db # Created by https://www.toptal.com/developers/gitignore/api/python # Edit at https://www.toptal.com/developers/gitignore?templates=python diff --git a/README.md b/README.md index 61ac503..059144f 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,7 @@ When caching cookies, use a profile without PIN. Otherwise it may cause errors. If you are facing 403 or 400 errors even after saving fresh cookies and clearing `Cache` folder, try logging out of your Amazon account in the browser and logging back in. Then save cookies. -Some titles say `UHD/2160p` is available and if VT is saying `no 2160p track available`, then `UHD/2160p` is only available via renting. +Some titles say `UHD/2160p` is available and if VT is saying `no 2160p track available`, then `UHD/2160p` is only available via renting. As in some titles advertise UHD but UHD will not be available to PrimeVideo customers. You will have to rent the title using the Rent button on the title page in UHD quality. If you are getting an `AssertionError` with Amazon, then try reprovisioning the device. I have included a batch script in the `vinetrimmer/devices/` directory to do this. Simply execute the script and try again. diff --git a/vinetrimmer/commands/dl.py b/vinetrimmer/commands/dl.py index 1165445..b85e84a 100644 --- a/vinetrimmer/commands/dl.py +++ b/vinetrimmer/commands/dl.py @@ -217,7 +217,9 @@ def get_credentials(service, profile="default"): help="Video Bitrate, defaults to Max.") @click.option("-ab", "--abitrate", "abitrate", type=int, default=None, - help="Audio Bitrate, defaults to Max.") + 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`") @click.option("-aa", "--atmos", is_flag=True, default=False, help="Prefer Atmos Audio") @click.option("-r", "--range", "range_", callback=range_param, default="SDR", @@ -320,7 +322,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, +def result(ctx, service, quality, closest_resolution, range_, wanted, alang, slang, acodec, audio_only, subs_only, chapters_only, audio_description, audio_channels, 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") @@ -420,7 +422,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) + title.tracks.select_audios(by_language=alang, by_bitrate=abitrate, with_descriptive=audio_description, by_codec=acodec, by_channels=audio_channels) title.tracks.select_subtitles(by_language=slang, with_forced=True) except ValueError as e: log.error(f" - {e}") diff --git a/vinetrimmer/devices/hisense_smarttv_he55a7000euwts_sl3000.prd b/vinetrimmer/devices/hisense_smarttv_he55a7000euwts_sl3000.prd index ee3ee0dbed9119d90d9c11be77dc4934b5a304a4..ead43c7633a3bc49c3f5a585779e7946e5b7db98 100644 GIT binary patch delta 516 zcmdlZut#7*O8p9nX`4S^?NLgdxGAuOtM@N!jOC@2ZuOE&-iB3Y6u*V3Dc3K%TIJ5N z%jQ{#iN(4XBJ*y%*X>-jowr0Q;O-R(KEsaB$tK;Y8b@wj{=IU;o4*VCq@Mm>#l!Z& zzu`>k1<9;(-7f{6GE+mQTtDBjy<>;@k@EPG3+n05R_yV)F;6wqu`|}YXrjQjSKc== zU3YlMe9N00H#=))_f1u9E)GG$e?5=C zV8~2NxH(yc(I8+$%i2%p?=G7CaF(+7(M<_U85p>CFhBs)DP5M+X%o6OcC7or>k&L_ zZts+WtFkv_m$}R~<=&7UVf!Blriv}Q;p;B9=Gx|Oe_fh&%O)>jbf~A)WkCBG!I+T= z>=Y7%QW7I9p$0NdHehjaNV|GzM@i4rw_*yb()VO7sl58_?88p>ikyXZA_sa>1X~nl o#s^q%U!GFhdiLNywzh38hHN?$s}l=^Y_GIQevdNQ#B9R~05N&-?EnA( delta 516 zcmdlZut#7*O8p$Zb*ZX;VGbPnj3>6vPv22~gyoiS{ex(cH(xJ)p0;wO>KcCMr5k62 z>3cp>O<4K&+2j=fK07)0 zyfVKB6TjTih$0EAX1AhC`i$IJzvXWp`0vinJLUP*+VBa?iQn}dHC#1jt=9f#v~GgP zl$4IIe_NWhGt{eB8>Ss{2tBfHgWkgbnh9T~ubG{5{bQ|4y+JP*JIAz5$)2hc5@#Nf zyZG?q?PmmIMkcUR zNDN9zjIe|n$TZo2#l@lYSc?AkSL_qT9$hIobhhBltjiOO`o3%2TG6Y)JBxMp;sd|D n&o6wj*GHXmmhr(4EB0LS+Y&J+ZI$a2S2^>bjy#r4%r>k5ssQD& diff --git a/vinetrimmer/key_store.db b/vinetrimmer/key_store.db index 51513072a00a60b810afebd5118c3c13aab1b4a3..33241ab2a0ad0e563ff9690e578e80093a3ff7a8 100644 GIT binary patch delta 729 zcmbu+y=xRf7{~FQm%TS6xjRJ+5hR5O24Qw*c6KMlxf1a#JQA@8X+maaXOJM|MbTKf zU9K?ra>$B^SV#m>h%8|{IXKdbjo4X zPlNrBi(f?gpaL#tt5cgO?cRnB2!3Xb#`_wmXZ@KAuc5u$IkmTtt?%8iFFB5ks8%xO zE{h3~JR(H#D3XzhO+>jTxnk5;hDt?^=E8VXc-$y27LplU&yM~6TZWZ;&q`ov=ME@U z(&Z`)A^4Dgs(|UU<&k;Ivl3W-la>g$nXeJxmOJOCnj3uq^1gs4w{YRP<)-#`I9IiM z)^6$Z=o&o0Z$~HbVt(uJYhSy9TP-Lxa~0D3*${i9hS33cVBgKg^ zLK{xCa5&}M_e>m19V@{dN3fU@NBK-r8vD3*lsLYH!u-W`d+8NAUbNF<2+m~_;n%{| oyY0GdwOY$$$l21D?~vPU*9O?I)&H^T02_9t0hX`+u^%=60T0L0m;e9( delta 298 zcmV+_0oDG1fEj>*8IT(R?vWfr0q(J2(m9us&jLLH0ui%uI*kLDaS#GN0s*bFK0d4w z0S~k5Pc5L68t`5M1;w*?@KXhszjFdV0s-i=4EFp9l?nlB0t$DvbO8tolP@|#x4sGi zLnQ|ZqyRJkfB}~ZFac-+4xhJjFaa_N1P)mMNS7aE0Yr!FIRUrqIRX`?1O>wYF_#74 z0#^_K#R$+3asvGXrw=OxU;{+AY~TV{1qB9}07n9s4R-@T2LkCv1Wio>m$38!K(|lk z0=3x#j{}Ie2fza#1Ct>@VwZ5k0|keT!vnXC!vu7U1OW~JDwhRN1y=$90GD1+1+WDJ wjQ~dim$1YFK$oo70SK2IrUO5>no$L6o&yjFaF+)_0U@&?Ku8F;aQFp~N3a55u>b%7 diff --git a/vinetrimmer/objects/tracks.py b/vinetrimmer/objects/tracks.py index 67d4346..a83dbe4 100644 --- a/vinetrimmer/objects/tracks.py +++ b/vinetrimmer/objects/tracks.py @@ -13,7 +13,7 @@ from collections import defaultdict from enum import Enum from io import BytesIO, TextIOWrapper from pathlib import Path - +from typing import List from subby import CommonIssuesFixer, SDHStripper import humanfriendly @@ -903,7 +903,7 @@ class MenuTrack: class Tracks: - AUDIO_CODEC_MAP = {"EC3": "ec-3", "AAC": "mp4a"} + AUDIO_CODEC_MAP = {"EC3": "ec-3", "AAC": "mp4a", "AC3": "ac-3", "VORB": "ogg", "OPUS": "ogg"} """ Tracks. Stores video, audio, and subtitle tracks. It also stores chapter/menu entries. @@ -1202,13 +1202,21 @@ class Tracks: if not with_descriptive: self.audios = [x for x in self.audios if not x.descriptive] if by_codec: - codec_audio = list(filter(lambda x: any(y for y in self.AUDIO_CODEC_MAP[by_codec] if y in x.codec), self.audios)) + 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: - channels_audio = list(filter(lambda x: x.channels == by_channels, self.audios)) + 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: @@ -1219,8 +1227,15 @@ class Tracks: if by_bitrate: self.audios = [x for x in self.audios if int(x.bitrate) <= int(by_bitrate * 1000)] if by_language: + one_per_lang = (False if + ( + (isinstance(by_codec, List) and len(by_codec) > 1) + or + (isinstance(by_channels, List) and len(by_channels) > 1) + ) + else True) # Todo: Optimize select_by_language - self.audios = list(self.select_by_language(by_language, self.audios, one_per_lang=True)) + \ + self.audios = list(self.select_by_language(by_language, self.audios, one_per_lang=one_per_lang)) + \ list(self.select_by_language(by_language, [x for x in self.audios if x.descriptive], one_per_lang=True)) def select_subtitles(self, by_language=None, with_cc=True, with_sdh=True, with_forced=True): diff --git a/vinetrimmer/services/amazon.py b/vinetrimmer/services/amazon.py index b7cee19..c503451 100644 --- a/vinetrimmer/services/amazon.py +++ b/vinetrimmer/services/amazon.py @@ -138,7 +138,7 @@ class Amazon(BaseService): self.vquality = "SD" if self.quality > 1080: - self.log.info(" + Setting manifest quality to UHD to be able to get 2160p video track") + self.log.info(" + Setting manifest quality to UHD and vcodec to H265 to be able to get 2160p video track") self.vquality = "UHD" self.vcodec = "H265" @@ -443,18 +443,16 @@ class Amazon(BaseService): else: tracks.add(audio_mpd.audios, warn_only=True) # expecting possible dupes, ignore - need_uhd_audio = self.atmos - if not self.amanifest and ((self.aquality == "UHD" and self.vquality != "UHD") or not self.aquality): audios = defaultdict(list) for audio in tracks.audios: audios[audio.language].append(audio) for lang in audios: if not any((x.bitrate or 0) >= 640000 for x in audios[lang]): - need_uhd_audio = True + self.atmos = True break - if need_uhd_audio and (self.config.get("device") or {}).get(self.profile, None): + if self.atmos and (self.config.get("device") or {}).get(self.profile, None): self.log.info("Getting audio from UHD manifest for potential higher bitrate or better codec") temp_device = self.device temp_device_token = self.device_token @@ -473,7 +471,7 @@ class Amazon(BaseService): bitrate_mode="CVBR+CBR", quality="UHD", hdr="DV", # Needed for 576kbps Atmos sometimes - manifest_type="DASH", + manifest_type="SmoothStreaming", # "DASH" ignore_errors=True ) @@ -513,7 +511,6 @@ class Amazon(BaseService): # replace the audio tracks with DV manifest version if atmos is present if any(x for x in uhd_audio_mpd.audios if x.atmos): - print("Hello") tracks.audios = uhd_audio_mpd.audios for video in tracks.videos: @@ -722,7 +719,7 @@ class Amazon(BaseService): }) self.device = (self.config.get("device") or {}).get(self.profile, {}) - if (self.quality > 1080 or self.range != "SDR") and self.vcodec == "H265" and (self.cdm.device.type == Device.Types.CHROME if "common_privacy_cert" in dir(self.cdm) else True): + if (self.quality > 1080 or self.range != "SDR" or self.atmos): #and self.vcodec == "H265" and (self.cdm.device.type == Device.Types.CHROME if "common_privacy_cert" in dir(self.cdm) else True): self.log.info(f"Using device to get UHD manifests") self.register_device() elif not self.device or self.vquality != "UHD" or (self.cdm.device.type == Device.Types.CHROME if "common_privacy_cert" in dir(self.cdm) else False): diff --git a/vinetrimmer/utils/click.py b/vinetrimmer/utils/click.py index eb11e12..0433e2d 100644 --- a/vinetrimmer/utils/click.py +++ b/vinetrimmer/utils/click.py @@ -81,22 +81,27 @@ def _choice(ctx, param, value, value_map): def acodec_param(ctx, param, value): - return _choice(ctx, param, value, { - "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", - }) + 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) + def language_param(ctx, param, value):