refactor(DSCP): Prioritize clearkey HLS over Widevine DASH

- The program will now look for AES HLS instead of Widevine DASH.
- Using '-v H.265' will request DASH manifest even if no H.265 tracks are available.
This can be useful if HLS is not available for some reason.
This commit is contained in:
stabbedbybrick 2024-08-02 14:12:31 +02:00
parent a3ec1f86bc
commit 5a286926ec

View File

@ -12,11 +12,11 @@ from urllib.parse import urljoin
import click import click
from click import Context from click import Context
from devine.core.credential import Credential from devine.core.credential import Credential
from devine.core.manifests.dash import DASH from devine.core.manifests import DASH, HLS
from devine.core.search_result import SearchResult from devine.core.search_result import SearchResult
from devine.core.service import Service from devine.core.service import Service
from devine.core.titles import Episode, Movie, Movies, Series from devine.core.titles import Episode, Movie, Movies, Series
from devine.core.tracks import Chapter, Tracks from devine.core.tracks import Chapters, Tracks
from requests import Request from requests import Request
@ -29,7 +29,10 @@ class DSCP(Service):
Author: stabbedbybrick Author: stabbedbybrick
Authorization: Cookies Authorization: Cookies
Robustness: Robustness:
L3: 2160p, AAC2.0 Widevine:
L3: 2160p, AAC2.0
ClearKey:
AES-128: 1080p, AAC2.0
\b \b
Tips: Tips:
@ -38,12 +41,12 @@ class DSCP(Service):
EPISODE: /video/richard-hammonds-workshop/new-beginnings EPISODE: /video/richard-hammonds-workshop/new-beginnings
SPORT: /video/sport/tnt-sports-1/uefa-champions-league SPORT: /video/sport/tnt-sports-1/uefa-champions-league
- Use the --lang LANG_RANGE option to request non-english tracks - Use the --lang LANG_RANGE option to request non-english tracks
- use -v H.265 to request H.265 tracks - use -v H.265 to request H.265 UHD tracks (if available)
\b \b
Known issues: Notes:
- Devine can't properly parse certain manifests, causing an error in download workers. - Using '-v H.265' will request DASH manifest even if no H.265 tracks are available.
- Sport streams specifically seem to be the most affected by this. This can be useful if HLS is not available for some reason.
""" """
@ -118,43 +121,53 @@ class DSCP(Service):
return Series(episodes) return Series(episodes)
def get_tracks(self, title: Union[Movie, Episode]) -> Tracks: def get_tracks(self, title: Union[Movie, Episode]) -> Tracks:
platform = "firetv" if self.vcodec == "H.265" else "desktop" payload = {
res = self._request( "videoId": title.id,
"POST", "deviceInfo": {
"/playback/v3/videoPlaybackInfo", "adBlocker": "false",
payload={ "drmSupported": "false",
"videoId": title.id, "hwDecodingCapabilities": ["H264", "H265"],
"deviceInfo": { "screen": {"width": 3840, "height": 2160},
"adBlocker": "false", "player": {"width": 3840, "height": 2160},
"drmSupported": "true",
"hwDecodingCapabilities": ["H264", "H265"],
"screen": {"width": 3840, "height": 2160},
"player": {"width": 3840, "height": 2160},
},
"wisteriaProperties": {
"advertiser": {"firstPlay": 0, "fwIsLat": 0},
"device": {"browser": {"name": "chrome", "version": "96.0.4664.55"}, "type": platform},
"platform": platform,
"product": "dplus_emea",
"sessionId": str(uuid.uuid1()),
"streamProvider": {"suspendBeaconing": 0, "hlsVersion": 7, "pingConfig": 1},
},
}, },
) "wisteriaProperties": {
"product": "dplus_emea",
"sessionId": str(uuid.uuid1()),
},
}
if self.vcodec == "H.265":
payload["wisteriaProperties"]["device"] = {
"browser": {"name": "chrome", "version": "96.0.4664.55"},
"type": "firetv",
}
payload["wisteriaProperties"]["platform"] = "firetv"
res = self._request("POST", "/playback/v3/videoPlaybackInfo", payload=payload)
self.license = None
streaming = res["data"]["attributes"]["streaming"][0] streaming = res["data"]["attributes"]["streaming"][0]
streaming_type = streaming["type"].strip().lower()
manifest = streaming["url"] manifest = streaming["url"]
self.token = None
self.license = None
if streaming["protection"]["drmEnabled"]: if streaming["protection"]["drmEnabled"]:
self.token = streaming["protection"]["drmToken"] self.token = streaming["protection"]["drmToken"]
self.license = streaming["protection"]["schemes"]["widevine"]["licenseUrl"] self.license = streaming["protection"]["schemes"]["widevine"]["licenseUrl"]
tracks = DASH.from_url(url=manifest, session=self.session).to_tracks(language=title.language) if streaming_type == "hls":
tracks = HLS.from_url(url=manifest, session=self.session).to_tracks(language=title.language)
elif streaming_type == "dash":
tracks = DASH.from_url(url=manifest, session=self.session).to_tracks(language=title.language)
else:
raise ValueError(f"Unknown streaming type: {streaming_type}")
return tracks return tracks
def get_chapters(self, title: Union[Movie, Episode]) -> list[Chapter]: def get_chapters(self, title: Union[Movie, Episode]) -> Chapters:
return [] return Chapters()
def get_widevine_service_certificate(self, **_: Any) -> str: def get_widevine_service_certificate(self, **_: Any) -> str:
return None return None
@ -186,12 +199,8 @@ class DSCP(Service):
seasons = [ seasons = [
self._request( self._request(
"GET", "GET", "/cms/collections/{}?{}&{}".format(content_id, season, show_id),
"/cms/collections/{}?{}&{}".format(content_id, season, show_id), params={"include": "default", "decorators": "playbackAllowed,contentAction,badges"},
params={
"include": "default",
"decorators": "playbackAllowed,contentAction,badges",
},
) )
for season in season_params for season in season_params
] ]
@ -231,15 +240,12 @@ class DSCP(Service):
if not video_id: if not video_id:
raise IndexError("Episode id not found") raise IndexError("Episode id not found")
params = { params = {"decorators": "isFavorite", "include": "primaryChannel"}
"decorators": "isFavorite",
"include": "primaryChannel",
}
content = self._request("GET", "/content/videos/{}".format(video_id), params=params) content = self._request("GET", "/content/videos/{}".format(video_id), params=params)
episode = content["data"]["attributes"] episode = content["data"]["attributes"]
name = episode.get("name") name = episode.get("name")
if episode.get("secondaryTitle"): if episode.get("secondaryTitle"):
name += " - " + episode.get("secondaryTitle") name += f" {episode.get('secondaryTitle')}"
return [ return [
Episode( Episode(