ISM Atmos fix for Amazon

This commit is contained in:
chu23465 2025-04-15 19:07:41 +05:30
parent 999c73d1e6
commit 11108223bc
12 changed files with 67 additions and 51 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -405,17 +405,6 @@ def result(ctx, service, quality, closest_resolution, range_, wanted, alang, sla
log.warning(f" - No {quality}p resolution available, using closest available: {closest_res}p") log.warning(f" - No {quality}p resolution available, using closest available: {closest_res}p")
quality = closest_res quality = closest_res
# Modified video track selection to choose lowest bitrate if vbitrate == min
if isinstance(vbitrate, str) and vbitrate.lower() == "min":
if not quality:
quality = 1080
available_bitrate = [int(track.bitrate) for track in title.tracks.videos if track.height == quality]
if available_bitrate == []:
log.error(" - No video tracks available")
continue
vbitrate = min(available_bitrate) / 1000
log.warning(f" - Choosing minimum bitrate: {vbitrate}")
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) title.tracks.select_audios(by_language=alang, by_bitrate=abitrate, with_descriptive=audio_description)
title.tracks.select_subtitles(by_language=slang, with_forced=True) title.tracks.select_subtitles(by_language=slang, with_forced=True)
@ -639,7 +628,9 @@ def result(ctx, service, quality, closest_resolution, range_, wanted, alang, sla
if not executable: if not executable:
raise log.exit(" - Unable to find mp4decrypt binary") raise log.exit(" - Unable to find mp4decrypt binary")
dec = os.path.splitext(track.locate())[0] + ".dec.mp4" dec = os.path.splitext(track.locate())[0] + ".dec.mp4"
os.makedirs(directories.temp, exist_ok=True)
try: try:
os.makedirs(directories.temp, exist_ok=True)
subprocess.run([ subprocess.run([
executable, executable,
"--show-progress", "--show-progress",
@ -657,6 +648,24 @@ def result(ctx, service, quality, closest_resolution, range_, wanted, alang, sla
if keys: if keys:
continue continue
if isinstance(track, AudioTrack) and track.descriptor == Track.Descriptor.ISM and track.atmos:
#--enable-libmfx is the only difference between 6.1.1 and 7.X.
#FFMPEG 6.1.1 is necessary as that version correctly puts in place an init.mp4 for EAC-3-JOC ie Atmos
#https://github.com/GyanD/codexffmpeg/releases/tag/6.1.1
#https://github.com/BtbN/FFmpeg-Builds/releases/tag/latest
executable = shutil.which("ffmpeg")
if not executable:
raise log.exit(" - Unable to find ffmpeg binary")
eac3 = os.path.splitext(track.locate())[0] + ".eac3"
os.makedirs(directories.temp, exist_ok=True)
try:
os.makedirs(directories.temp, exist_ok=True)
exec_string = f"{executable} -i {track.locate()} -hide_banner -loglevel error -map 0 -c:a copy {eac3}"
subprocess.run(exec_string)
except subprocess.CalledProcessError:
raise log.exit(" - Failed!")
track.swap(eac3)
log.info(" + Fixed ISM Atmos")
if track.needs_repack or (config.decrypter == "mp4decrypt" and isinstance(track, (VideoTrack, AudioTrack))): if track.needs_repack or (config.decrypter == "mp4decrypt" and isinstance(track, (VideoTrack, AudioTrack))):
log.info("Repackaging stream with FFmpeg (to fix malformed streams)") log.info("Repackaging stream with FFmpeg (to fix malformed streams)")

Binary file not shown.

View File

@ -237,21 +237,17 @@ class Track:
self.psshPR = str(x).split("\"")[1].split(",")[-1] self.psshPR = str(x).split("\"")[1].split(",")[-1]
break break
try: xml_str = base64.b64decode(self.psshPR).decode("utf-16-le", "ignore")
xml_str = base64.b64decode(self.psshPR).decode("utf-16-le", "ignore") xml_str = xml_str[xml_str.index("<"):]
xml_str = xml_str[xml_str.index("<"):] xml = load_xml(xml_str).find("DATA") # root: WRMHEADER
xml = load_xml(xml_str).find("DATA") # root: WRMHEADER self.kid = xml.findtext("KID") # v4.0.0.0
if not self.kid: # v4.1.0.0
self.kid = next(iter(xml.xpath("PROTECTINFO/KID/@VALUE")), None)
if not self.kid: # v4.3.0.0
self.kid = next(iter(xml.xpath("PROTECTINFO/KIDS/KID/@VALUE")), None) # can be multiple?
self.kid = xml.findtext("KID") # v4.0.0.0 self.kid = uuid.UUID(base64.b64decode(self.kid).hex()).bytes_le.hex()
if not self.kid: # v4.1.0.0
self.kid = next(iter(xml.xpath("PROTECTINFO/KID/@VALUE")), None)
if not self.kid: # v4.3.0.0
self.kid = next(iter(xml.xpath("PROTECTINFO/KIDS/KID/@VALUE")), None) # can be multiple?
self.kid = uuid.UUID(base64.b64decode(self.kid).hex()).bytes_le.hex()
except: pass
if self.source == "NF": if self.source == "NF":
self.kid = "{}{}{}".format( self.kid = "{}{}{}".format(
@ -367,9 +363,11 @@ class Track:
if not Path(save_path).is_file(): if not Path(save_path).is_file():
save_path = save_path_orig.replace(".mp4", f".{str(self.language)[:2]}.m4a") save_path = save_path_orig.replace(".mp4", f".{str(self.language)[:2]}.m4a")
if not Path(save_path).is_file(): if not Path(save_path).is_file():
save_path = save_path_orig save_path = save_path_orig.replace(".mp4", f".eng.m4a") # This is a hack as only english atmos audio is available in Amazon
if not Path(save_path).is_file(): if not Path(save_path).is_file():
raise save_path = save_path_orig
if not Path(save_path).is_file():
raise
else: else:
asyncio.run(aria2c( asyncio.run(aria2c(
self.url, self.url,
@ -1134,8 +1132,17 @@ class Tracks:
if not videos_quality: if not videos_quality:
raise ValueError(f"There's no {by_quality}p resolution video track. Aborting.") raise ValueError(f"There's no {by_quality}p resolution video track. Aborting.")
self.videos = videos_quality self.videos = videos_quality
if by_vbitrate:
# Modified video track selection to choose lowest bitrate if by_vbitrate == min
if isinstance(by_vbitrate, str) and by_vbitrate.lower() == "min":
available_bitrate = [int(track.bitrate) for track in self.videos]
bitrate = min(available_bitrate) / 1001
#if bitrate < 99999:
# bitrate = bitrate / 1000
self.videos = [x for x in self.videos if int(x.bitrate) <= int(bitrate * 1001)]
elif by_vbitrate:
self.videos = [x for x in self.videos if int(x.bitrate) <= int(by_vbitrate * 1001)] self.videos = [x for x in self.videos if int(x.bitrate) <= int(by_vbitrate * 1001)]
if by_codec: if by_codec:
codec_videos = list(filter(lambda x: any(y for y in self.VIDEO_CODEC_MAP[by_codec] if y in x.codec), self.videos)) codec_videos = list(filter(lambda x: any(y for y in self.VIDEO_CODEC_MAP[by_codec] if y in x.codec), self.videos))
if not codec_videos and not should_fallback: if not codec_videos and not should_fallback:

View File

@ -217,28 +217,28 @@ def parse(*, url=None, data=None, source, session=None, downloader=None):
# extra # extra
extra=(list(quality_level), list(stream_info),) # Either set size as a attribute of VideoTrack or append to extra here. extra=(list(quality_level), list(stream_info),) # Either set size as a attribute of VideoTrack or append to extra here.
)) ))
"""
elif type_info == 'audio': elif type_info == 'audio':
atmos = ( str( quality_level.get('@HasAtmos', 'N/A') ).lower() == "true" ) or ( "ATM" in stream_info.get('@Name', 'N/A') ) atmos = ( str( quality_level.get('@HasAtmos', 'N/A') ).lower() == "true" ) or ( "ATM" in stream_info.get('@Name', 'N/A') )
tracks.append(AudioTrack( if atmos: # Only appending Atmos streams -> Other audios can be obtained from Amazon MPD
id_=track_id, tracks.append(AudioTrack(
source=source, id_=track_id,
url=url, source=source,
# metadata url=url,
codec="E-AC3" if codec == "EC-3" else (codec or "").split(".")[0], # metadata
language=lang, codec="E-AC3" if codec == "EC-3" else (codec or "").split(".")[0],
bitrate=bitrate, language=lang,
channels=quality_level.get('@Channels', 'N/A'), bitrate=bitrate,
atmos=atmos, channels=quality_level.get('@Channels', 'N/A'),
# switches/options atmos=atmos,
descriptor=Track.Descriptor.ISM, # switches/options
# decryption descriptor=Track.Descriptor.ISM,
needs_repack=True, # Necessary # decryption
encrypted=encrypted, needs_repack=True, # Necessary
pssh=pssh, encrypted=encrypted,
kid=kid, psshPR=pssh,
# extra kid=kid,
extra=(dict(quality_level), dict(stream_info),) # extra
)) extra=(dict(quality_level), dict(stream_info),)
""" ))
return tracks return tracks

View File

@ -296,8 +296,8 @@ async def m3u8dl(uri, out, track, headers=None, proxy=None):
"-dv", "all", "-dv", "all",
"-ds", "all", "-ds", "all",
]) ])
if track.source != "HS": #if track.source != "HS":
arguments.extend(["-M", "format=mp4"]) # arguments.extend(["-M", "format=mp4"])
else: else:
raise ValueError(f"{track.__class__.__name__} not supported yet!") raise ValueError(f"{track.__class__.__name__} not supported yet!")