Max audio compatibility added. Changes for Hybrid DV/HDR.
This commit is contained in:
chu23465 2025-04-24 17:24:28 +05:30
parent 4fcd0748c0
commit 4a773d1db0
5 changed files with 121 additions and 11 deletions

View File

@ -4,6 +4,7 @@ python -m pip install poetry==1.8.5
poetry config virtualenvs.in-project true
poetry lock --no-update
poetry install
sudo add-apt-repository ppa:ubuntuhandbook1/apps
sudo apt update
sudo apt-get install ffmpeg aria2 mkvtoolnix libmediainfo0v5
ffmpeg --version

View File

@ -302,8 +302,9 @@ def dl(ctx, profile, cdm, *_, **__):
except ValueError as e:
raise log.exit(f" - {e}")
device_name = device.system_id if "vmp" in dir(device) else device.get_name()
log.info(f" + Loaded {device.__class__.__name__}: {device_name} (L{device.security_level})")
device_name = device.system_id if "vmp" in dir(device) else device.get_name().replace("_", " ").upper()
s = "" if "vmp" in dir(device) else "S"
log.info(f" + Loaded {device.__class__.__name__}: {device_name} ({s}L{device.security_level})")
cdm = Cdm.from_device(device) if "vmp" in dir(device) else CdmPr.from_device(device)
if profile:
@ -425,7 +426,10 @@ 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")
quality = closest_res
title.tracks.select_videos(by_quality=quality, by_vbitrate=vbitrate, by_range=range_, one_only=True)
if range_ == "DV+HDR":
title.tracks.select_videos_multi(["HDR10", "DV"], by_quality=quality, by_vbitrate=vbitrate)
else:
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, max_audio_compatability=max_audio_compatability)
title.tracks.select_subtitles(by_language=slang, with_forced=True)
except ValueError as e:
@ -714,12 +718,22 @@ def result(ctx, service, quality, closest_resolution, range_, wanted, alang, sla
if isinstance(track, TextTrack) and strip_sdh:
track.strip_sdh()
log.info("Stripped SDH subtitles to CC with subby")
if skip_title:
for track in title.tracks:
track.delete()
continue
if keys:
continue
if range_ == "DV+HDR":
try:
hybrid_path = title.tracks.make_hybrid()
log.info(f" + Hybrid DV+HDR created: {hybrid_path}")
except Exception as e:
log.warning(f" - Skipped Hybrid DV+HDR: {e}")
if not list(title.tracks) and not title.tracks.chapters:
continue
# mux all final tracks to a single mkv file

View File

@ -4,6 +4,7 @@ import logging
import math
import os
import re
import time
import shutil
import subprocess
import sys
@ -1188,6 +1189,20 @@ class Tracks:
if one_only and self.videos:
self.videos = [self.videos[0]]
def select_videos_multi(self, ranges: list[str], by_quality=None, by_vbitrate=None) -> None:
selected = []
for r in ranges:
temp = Tracks()
temp.videos = self.videos.copy()
temp.select_videos(by_range=r, by_quality=by_quality, one_only=False)
if by_vbitrate:
temp.videos = [x for x in temp.videos if int(x.bitrate) <= int(by_vbitrate * 1001)]
if temp.videos:
best = max(temp.videos, key=lambda x: x.bitrate)
selected.append(best)
unique = {(v.width, v.height, v.codec): v for v in selected}
self.videos = list(unique.values())
def select_audios(
self,
with_descriptive: bool = True,
@ -1214,18 +1229,20 @@ class Tracks:
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
),
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
)
)
))
)
audios.append(max(inner_audios, key=lambda x: x.bitrate))
except: pass
unique = {(v.bitrate, v.codec, v.channels): v for v in audios}
self.audios = list(unique.values())
else:
if by_codec:
@ -1314,6 +1331,82 @@ class Tracks:
from vinetrimmer import parsers
return parsers.ism.parse(**kwargs)
def make_hybrid(self) -> str:
start_time = time.time()
logsi = logging.getLogger("Hybrid")
logsi.info(" + Processing to Hybrid")
hdr = next((t for t in self.videos if t.hdr10 and not t.dv), None)
dv = next((t for t in self.videos if t.dv and not t.hdr10), None)
if not hdr or not dv:
raise ValueError("Hybrid failed: track HDR10 and DV not correct.")
hdr_path = Path(hdr.locate())
dv_path = Path(dv.locate())
hybrid_path = hdr_path.with_name(hdr_path.stem + "_hybrid.hevc")
hybrid_path = Path(self.make_hybrid_dv_hdr(str(dv_path), str(hdr_path), str(hybrid_path)))
timeout = 10
waited = 0
while not hybrid_path.exists() or os.path.getsize(hybrid_path) < 10000:
time.sleep(0.25)
waited += 0.25
if waited >= timeout:
raise FileNotFoundError(f"Hybrid file never appeared or too small: {hybrid_path}")
hdr.swap(str(hybrid_path))
# Hapus DV-only
self.videos = [v for v in self.videos if not (v.dv and not v.hdr10)]
#self._cleanup_paths = [dv_path, hybrid_path]
try:
if hdr_path.exists():
hdr_path.unlink()
if dv_path.exists():
dv_path.unlink()
except Exception as e:
logsi.warning(f" - Failed to delete the temp file: {e}")
end_time = time.time()
duration = format_duration(end_time - start_time)
logsi.info(gradient_text(f" + Finish processing Hybrid in {duration}!", (173, 255, 47), (0, 191, 255)))
return str(hybrid_path)
def make_hybrid_dv_hdr(dv_file: str, hdr_file: str, output_file: str = None) -> str:
dovi_tool = shutil.which("dovi_tool") or "./binaries/dovi_tool"
if not os.path.isfile(dovi_tool):
raise FileNotFoundError("dovi_tool not found.")
def extract_hevc(input_file, output_file):
subprocess.run(
["ffmpeg", "-y", "-i", input_file, "-c", "copy", "-bsf:v", "hevc_mp4toannexb", "-f", "hevc", output_file],
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
)
dv_file = Path(dv_file)
hdr_file = Path(hdr_file)
# Convert .mp4/.mkv to .hevc if needed
if not dv_file.suffix == ".hevc":
raw_dv = dv_file.with_suffix(".hevc")
extract_hevc(str(dv_file), str(raw_dv))
dv_file = raw_dv
if not hdr_file.suffix == ".hevc":
raw_hdr = hdr_file.with_suffix(".hevc")
extract_hevc(str(hdr_file), str(raw_hdr))
hdr_file = raw_hdr
output_file = Path(output_file or hdr_file.with_name(hdr_file.stem + "_hybrid.hevc")).resolve()
rpu_file = Path("RPU.bin")
temp_output = Path("temp_hybrid.hevc")
subprocess.run([dovi_tool, "extract-rpu", "-i", str(dv_file), "-o", str(rpu_file)], check=True)
subprocess.run([dovi_tool, "inject-rpu", "-i", str(hdr_file), "-r", str(rpu_file), "-o", str(temp_output)], check=True)
if temp_output.exists():
shutil.move(str(temp_output), str(output_file))
if rpu_file.exists():
rpu_file.unlink()
if not output_file.exists():
raise FileNotFoundError(f"Hybrid failed: {output_file} is not found.")
return str(output_file)
def mux(self, prefix):
"""
Takes the Video, Audio and Subtitle Tracks, and muxes them into an MKV file.

View File

@ -146,6 +146,8 @@ def range_param(ctx, param, value):
"hlg": "HLG",
"dv": "DV",
"dovi": "DV",
"dv+hdr": "DV+HDR",
"hdr+dv": "DV+HDR",
})