Hotstar with DRM

I forgot to test Hotstar with DRM. Made changes to support DRM correctly.
This commit is contained in:
Aswin 2025-04-07 00:45:16 +05:30
parent 1c15375821
commit 09e03b47f7
5 changed files with 33 additions and 16 deletions

View File

@ -27,6 +27,7 @@ from vinetrimmer.utils.click import (AliasedGroup, ContextData, acodec_param, la
from vinetrimmer.utils.collections import as_list, merge_dict
from vinetrimmer.utils.io import load_yaml
from pywidevine import Device, Cdm, RemoteCdm
from pywidevine import PSSH as PSSHWV
from vinetrimmer.vendor.pymp4.parser import Box
@ -476,7 +477,7 @@ def result(ctx, service, quality, range_, wanted, alang, slang, audio_only, subs
skip_title = True
break
if "common_privacy_cert" in dir(ctx.obj.cdm):
session_id = ctx.obj.cdm.open(track.pssh)
session_id = ctx.obj.cdm.open()
log.info(f"CDM Session ID - {session_id.hex()}")
ctx.obj.cdm.set_service_certificate(
session_id,
@ -490,7 +491,7 @@ def result(ctx, service, quality, range_, wanted, alang, slang, audio_only, subs
ctx.obj.cdm.parse_license(
session_id,
service.license(
challenge=ctx.obj.cdm.get_license_challenge(session_id),
challenge=ctx.obj.cdm.get_license_challenge(session_id=session_id, pssh=PSSHWV(track.pssh)),
title=title,
track=track,
session_id=session_id
@ -515,7 +516,7 @@ def result(ctx, service, quality, range_, wanted, alang, slang, audio_only, subs
content_keys = [
(x.kid, x.key) for x in ctx.obj.cdm.get_keys(session_id, content_only=True)
(str(x.kid).replace("-", ""), x.key.hex()) for x in ctx.obj.cdm.get_keys(session_id) if x.type == "CONTENT"
] if "common_privacy_cert" in dir(ctx.obj.cdm) else [
(str(x.key_id).replace("-", ""), x.key.hex()) for x in ctx.obj.cdm.get_keys(session_id)
]

Binary file not shown.

View File

@ -419,6 +419,8 @@ class Track:
headers,
proxy if self.needs_proxy else None
))
if self.__class__.__name__ == "AudioTrack":
save_path = save_path.replace(".mp4", f".{str(self.language)[:2]}.m4a")
else:
asyncio.run(aria2c(
self.url,

View File

@ -139,17 +139,33 @@ def parse(*, url=None, data=None, source, session=None, downloader=None):
for protection in protections:
# For HMAX, the PSSH has multiple keys but the PlayReady ContentProtection tag
# contains the correct KID
kid = protection.get("default_KID")
if kid:
kid = uuid.UUID(kid).hex
else:
kid = protection.get("kid")
if kid:
kid = uuid.UUID(bytes_le=base64.b64decode(kid)).hex
if (protection.get("schemeIdUri") or "").lower() != "urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95":
if not kid:
kid = protection.get("default_KID")
if not kid:
kid = protection.get("kid")
if kid:
kid = uuid.UUID(bytes_le=base64.b64decode(kid)).hex
else:
kid = uuid.UUID(kid).hex
if (protection.get("schemeIdUri") or "").lower() not in ["urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95", "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"]:
continue
pssh = protection.findtext("pssh")
"""
if pssh and ((protection.get("schemeIdUri") or "").lower() == "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed3"):
pssh = base64.b64decode(pssh)
# noinspection PyBroadException
try:
pssh = Box.parse(pssh)
except Exception:
pssh = Box.parse(Box.build(dict(
type=b"pssh",
version=0, # can only assume version & flag are 0
flags=0,
system_ID=Cdm.uuid,
init_data=pssh
)))
"""
rep_base_url = rep.findtext("BaseURL")
if rep_base_url and source not in ["DSCP", "DSNY"]: # TODO: Don't hardcode services

View File

@ -294,8 +294,9 @@ async def m3u8dl(uri, out, track, headers=None, proxy=None):
arguments.extend([
"-dv", "all",
"-ds", "all",
"-M", "format=mp4:muxer=ffmpeg",
])
if track.source != "HS":
arguments.extend(["-M", "format=mp4"])
else:
raise ValueError(f"{track.__class__.__name__} not supported yet!")
@ -303,8 +304,5 @@ async def m3u8dl(uri, out, track, headers=None, proxy=None):
arg_str = " ".join(arguments)
#print(arg_str)
p = subprocess.run(arg_str, check=True)
#os.system(arg_str)
except subprocess.CalledProcessError:
raise ValueError("N_m3u8DL-RE failed too many times, aborting")
# Removed above call using subprocess due to it failing to correctly pass --header value. The problem might be with spaces within the header string, I think? I know it's not recommended to use os.system but didn't have a choice
raise ValueError("N_m3u8DL-RE failed too many times, aborting")