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):