mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-06-22 08:57:25 +00:00
feat(hls): handle FairPlay skd keys in segment decrypt path
The FairPlay->PlayReady bridge synthesized headers and routed licensing but the HLS download loop still rejected the skd EXT-X-KEY. Teach get_supported_key and get_drm the com.apple.streamingkeydelivery keyformat, and reuse a service-provided FairPlay session DRM for skd segments (its KID encoding is service-specific, e.g. base64).
This commit is contained in:
@@ -30,7 +30,8 @@ from requests import Session
|
|||||||
from unshackle.core import binaries
|
from unshackle.core import binaries
|
||||||
from unshackle.core.cdm.detect import is_playready_cdm, is_widevine_cdm
|
from unshackle.core.cdm.detect import is_playready_cdm, is_widevine_cdm
|
||||||
from unshackle.core.constants import DOWNLOAD_CANCELLED, DOWNLOAD_LICENCE_ONLY, AnyTrack
|
from unshackle.core.constants import DOWNLOAD_CANCELLED, DOWNLOAD_LICENCE_ONLY, AnyTrack
|
||||||
from unshackle.core.drm import DRM_T, ClearKey, MonaLisa, PlayReady, Widevine
|
from unshackle.core.drm import DRM_T, ClearKey, FairPlay, MonaLisa, PlayReady, Widevine
|
||||||
|
from unshackle.core.drm.fairplay import fairplay_kid_from_skd
|
||||||
from unshackle.core.events import events
|
from unshackle.core.events import events
|
||||||
from unshackle.core.session import RnetResponse, RnetSession
|
from unshackle.core.session import RnetResponse, RnetSession
|
||||||
from unshackle.core.tracks import Audio, Subtitle, Tracks, Video
|
from unshackle.core.tracks import Audio, Subtitle, Tracks, Video
|
||||||
@@ -38,6 +39,9 @@ from unshackle.core.utilities import get_extension, is_close_match, log_event, t
|
|||||||
from unshackle.core.utils.redact import safe_display_url
|
from unshackle.core.utils.redact import safe_display_url
|
||||||
from unshackle.core.utils.subprocess import log_tool_run
|
from unshackle.core.utils.subprocess import log_tool_run
|
||||||
|
|
||||||
|
# FairPlay HLS (cbcs) EXT-X-KEY keyformat; bridged to PlayReady (see drm/fairplay.py).
|
||||||
|
FAIRPLAY_KEYFORMAT = "com.apple.streamingkeydelivery"
|
||||||
|
|
||||||
|
|
||||||
class HLS:
|
class HLS:
|
||||||
SUPP_CODECS_RE = re.compile(r'SUPPLEMENTAL-CODECS="([^"]+)"', re.IGNORECASE)
|
SUPP_CODECS_RE = re.compile(r'SUPPLEMENTAL-CODECS="([^"]+)"', re.IGNORECASE)
|
||||||
@@ -902,8 +906,16 @@ class HLS:
|
|||||||
if key is None:
|
if key is None:
|
||||||
encryption_data = None
|
encryption_data = None
|
||||||
elif not encryption_data or encryption_data[0] != key:
|
elif not encryption_data or encryption_data[0] != key:
|
||||||
drm = HLS.get_drm(key, session)
|
if (
|
||||||
if isinstance(drm, (Widevine, PlayReady)):
|
key.keyformat
|
||||||
|
and key.keyformat.lower() == FAIRPLAY_KEYFORMAT
|
||||||
|
and isinstance(session_drm, FairPlay)
|
||||||
|
):
|
||||||
|
# Reuse already-licensed service FairPlay; skd KID encoding is service-specific.
|
||||||
|
drm = session_drm
|
||||||
|
else:
|
||||||
|
drm = HLS.get_drm(key, session)
|
||||||
|
if isinstance(drm, (Widevine, PlayReady)) and not getattr(drm, "content_keys", None):
|
||||||
try:
|
try:
|
||||||
if map_data:
|
if map_data:
|
||||||
track_kid = track.get_key_id(map_data[1])
|
track_kid = track.get_key_id(map_data[1])
|
||||||
@@ -1247,6 +1259,9 @@ class HLS:
|
|||||||
"com.microsoft.playready",
|
"com.microsoft.playready",
|
||||||
}:
|
}:
|
||||||
return key
|
return key
|
||||||
|
elif key.keyformat and key.keyformat.lower() == FAIRPLAY_KEYFORMAT:
|
||||||
|
# FairPlay (cbcs) bridged to PlayReady; key licensed via FairPlay DRM.
|
||||||
|
return key
|
||||||
else:
|
else:
|
||||||
unsupported_systems.append(key.method + (f" ({key.keyformat})" if key.keyformat else ""))
|
unsupported_systems.append(key.method + (f" ({key.keyformat})" if key.keyformat else ""))
|
||||||
else:
|
else:
|
||||||
@@ -1287,6 +1302,13 @@ class HLS:
|
|||||||
pssh=PR_PSSH(key.uri.split(",")[-1]),
|
pssh=PR_PSSH(key.uri.split(",")[-1]),
|
||||||
pssh_b64=key.uri.split(",")[-1],
|
pssh_b64=key.uri.split(",")[-1],
|
||||||
)
|
)
|
||||||
|
elif key.keyformat and key.keyformat.lower() == FAIRPLAY_KEYFORMAT:
|
||||||
|
# FairPlay -> PlayReady: synthesize FairPlay DRM from the skd KID. Services whose skd
|
||||||
|
# KID isn't a plain GUID/hex should supply the DRM via get_track_drm instead.
|
||||||
|
kid = fairplay_kid_from_skd(key.uri)
|
||||||
|
if not kid:
|
||||||
|
raise NotImplementedError(f"Could not derive a FairPlay content KID from key: {key}")
|
||||||
|
drm = FairPlay.from_kid(kid)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError(f"The key system is not supported: {key}")
|
raise NotImplementedError(f"The key system is not supported: {key}")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user