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
This commit is contained in:
chu23465 2025-04-19 22:57:19 +05:30
parent 62972b20cf
commit 5f98f329af
8 changed files with 53 additions and 33 deletions

1
.gitignore vendored
View File

@ -5,6 +5,7 @@
/vinetrimmer/Cookies/ /vinetrimmer/Cookies/
/vinetrimmer/Logs/ /vinetrimmer/Logs/
.DS_Store .DS_Store
key_store.db
# Created by https://www.toptal.com/developers/gitignore/api/python # Created by https://www.toptal.com/developers/gitignore/api/python
# Edit at https://www.toptal.com/developers/gitignore?templates=python # Edit at https://www.toptal.com/developers/gitignore?templates=python

View File

@ -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. 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. 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.

View File

@ -218,6 +218,8 @@ def get_credentials(service, profile="default"):
@click.option("-ab", "--abitrate", "abitrate", type=int, @click.option("-ab", "--abitrate", "abitrate", type=int,
default=None, 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, @click.option("-aa", "--atmos", is_flag=True, default=False,
help="Prefer Atmos Audio") help="Prefer Atmos Audio")
@click.option("-r", "--range", "range_", callback=range_param, default="SDR", @click.option("-r", "--range", "range_", callback=range_param, default="SDR",
@ -320,7 +322,7 @@ def dl(ctx, profile, cdm, *_, **__):
@dl.result_callback() @dl.result_callback()
@click.pass_context @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, *_, **__): 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(): def ccextractor():
log.info("Extracting EIA-608 captions from stream with 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 quality = closest_res
title.tracks.select_videos(by_quality=quality, by_vbitrate=vbitrate, by_range=range_, one_only=True) 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) title.tracks.select_subtitles(by_language=slang, with_forced=True)
except ValueError as e: except ValueError as e:
log.error(f" - {e}") log.error(f" - {e}")

Binary file not shown.

View File

@ -13,7 +13,7 @@ from collections import defaultdict
from enum import Enum from enum import Enum
from io import BytesIO, TextIOWrapper from io import BytesIO, TextIOWrapper
from pathlib import Path from pathlib import Path
from typing import List
from subby import CommonIssuesFixer, SDHStripper from subby import CommonIssuesFixer, SDHStripper
import humanfriendly import humanfriendly
@ -903,7 +903,7 @@ class MenuTrack:
class Tracks: 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. Tracks.
Stores video, audio, and subtitle tracks. It also stores chapter/menu entries. Stores video, audio, and subtitle tracks. It also stores chapter/menu entries.
@ -1202,13 +1202,21 @@ class Tracks:
if not with_descriptive: if not with_descriptive:
self.audios = [x for x in self.audios if not x.descriptive] self.audios = [x for x in self.audios if not x.descriptive]
if by_codec: 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: if not codec_audio and not should_fallback:
raise ValueError(f"There's no {by_codec} audio tracks. Aborting.") raise ValueError(f"There's no {by_codec} audio tracks. Aborting.")
else: else:
self.audios = (codec_audio if codec_audio else self.audios) self.audios = (codec_audio if codec_audio else self.audios)
if by_channels: 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: if not channels_audio and not should_fallback:
raise ValueError(f"There's no {by_channels} {by_codec} audio tracks. Aborting.") raise ValueError(f"There's no {by_channels} {by_codec} audio tracks. Aborting.")
else: else:
@ -1219,8 +1227,15 @@ class Tracks:
if by_bitrate: if by_bitrate:
self.audios = [x for x in self.audios if int(x.bitrate) <= int(by_bitrate * 1000)] self.audios = [x for x in self.audios if int(x.bitrate) <= int(by_bitrate * 1000)]
if by_language: 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 # 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)) 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): def select_subtitles(self, by_language=None, with_cc=True, with_sdh=True, with_forced=True):

View File

@ -138,7 +138,7 @@ class Amazon(BaseService):
self.vquality = "SD" self.vquality = "SD"
if self.quality > 1080: 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.vquality = "UHD"
self.vcodec = "H265" self.vcodec = "H265"
@ -443,18 +443,16 @@ class Amazon(BaseService):
else: else:
tracks.add(audio_mpd.audios, warn_only=True) # expecting possible dupes, ignore 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): if not self.amanifest and ((self.aquality == "UHD" and self.vquality != "UHD") or not self.aquality):
audios = defaultdict(list) audios = defaultdict(list)
for audio in tracks.audios: for audio in tracks.audios:
audios[audio.language].append(audio) audios[audio.language].append(audio)
for lang in audios: for lang in audios:
if not any((x.bitrate or 0) >= 640000 for x in audios[lang]): if not any((x.bitrate or 0) >= 640000 for x in audios[lang]):
need_uhd_audio = True self.atmos = True
break 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") self.log.info("Getting audio from UHD manifest for potential higher bitrate or better codec")
temp_device = self.device temp_device = self.device
temp_device_token = self.device_token temp_device_token = self.device_token
@ -473,7 +471,7 @@ class Amazon(BaseService):
bitrate_mode="CVBR+CBR", bitrate_mode="CVBR+CBR",
quality="UHD", quality="UHD",
hdr="DV", # Needed for 576kbps Atmos sometimes hdr="DV", # Needed for 576kbps Atmos sometimes
manifest_type="DASH", manifest_type="SmoothStreaming", # "DASH"
ignore_errors=True ignore_errors=True
) )
@ -513,7 +511,6 @@ class Amazon(BaseService):
# replace the audio tracks with DV manifest version if atmos is present # 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): if any(x for x in uhd_audio_mpd.audios if x.atmos):
print("Hello")
tracks.audios = uhd_audio_mpd.audios tracks.audios = uhd_audio_mpd.audios
for video in tracks.videos: for video in tracks.videos:
@ -722,7 +719,7 @@ class Amazon(BaseService):
}) })
self.device = (self.config.get("device") or {}).get(self.profile, {}) 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.log.info(f"Using device to get UHD manifests")
self.register_device() 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): 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):

View File

@ -81,7 +81,10 @@ def _choice(ctx, param, value, value_map):
def acodec_param(ctx, param, value): def acodec_param(ctx, param, value):
return _choice(ctx, param, value, { values = value.split(",")
acodecs = []
for x in values:
acodecs.append(_choice(ctx, param, x, {
"aac": "AAC", "aac": "AAC",
"ac3": "AC3", "ac3": "AC3",
"ac-3": "AC3", "ac-3": "AC3",
@ -96,7 +99,9 @@ def acodec_param(ctx, param, value):
"vorb": "VORB", "vorb": "VORB",
"vorbis": "VORB", "vorbis": "VORB",
"opus": "OPUS", "opus": "OPUS",
}) }))
return ",".join(acodecs)
def language_param(ctx, param, value): def language_param(ctx, param, value):