Skip to content

Commit 527e126

Browse files
committed
New test download DASH
1 parent 27d8695 commit 527e126

File tree

7 files changed

+60
-353
lines changed

7 files changed

+60
-353
lines changed

StreamingCommunity/Api/Site/mediasetinfinity/film.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def download_film(select_title: MediaItem) -> Tuple[str, bool]:
5252
playback_json = get_playback_url(select_title.id)
5353
tracking_info = get_tracking_info(playback_json)['videos'][0]
5454

55-
license_url = generate_license_url(tracking_info)
55+
license_url, license_params = generate_license_url(tracking_info)
5656
mpd_url = get_manifest(tracking_info['url'])
5757

5858
# Download the episode
@@ -63,7 +63,7 @@ def download_film(select_title: MediaItem) -> Tuple[str, bool]:
6363
)
6464
dash_process.parse_manifest(custom_headers=get_headers())
6565

66-
if dash_process.download_and_decrypt():
66+
if dash_process.download_and_decrypt(query_params=license_params):
6767
dash_process.finalize_output()
6868

6969
# Get final output path and status

StreamingCommunity/Api/Site/mediasetinfinity/series.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
6868
# Generate mpd and license URLs
6969
playback_json = get_playback_url(obj_episode.id)
7070
tracking_info = get_tracking_info(playback_json)
71-
license_url = generate_license_url(tracking_info['videos'][0])
71+
license_url, license_params = generate_license_url(tracking_info['videos'][0])
7272
mpd_url = get_manifest(tracking_info['videos'][0]['url'])
7373

7474
# Download the episode
@@ -80,7 +80,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
8080
)
8181
dash_process.parse_manifest(custom_headers=get_headers())
8282

83-
if dash_process.download_and_decrypt():
83+
if dash_process.download_and_decrypt(query_params=license_params):
8484
dash_process.finalize_output()
8585

8686
# Get final output path and status

StreamingCommunity/Api/Site/mediasetinfinity/util/get_license.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import re
44
import uuid
5-
from urllib.parse import urlencode
65
import xml.etree.ElementTree as ET
76

87

@@ -271,4 +270,4 @@ def generate_license_url(tracking_info):
271270
'token': class_mediaset_api.getBearerToken(),
272271
}
273272

274-
return f"{'https://widevine.entitlement.theplatform.eu/wv/web/ModularDrm/getRawWidevineLicense'}?{urlencode(params)}"
273+
return f"{'https://widevine.entitlement.theplatform.eu/wv/web/ModularDrm/getRawWidevineLicense'}", params

StreamingCommunity/Lib/Downloader/DASH/cdm_helpher.py

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# 25.07.25
22

33
import base64
4-
import logging
4+
from urllib.parse import urlencode
55

66

77
# External libraries
@@ -16,15 +16,16 @@
1616
console = Console()
1717

1818

19-
def get_widevine_keys(pssh, license_url, cdm_device_path, headers=None, payload=None):
19+
def get_widevine_keys(pssh, license_url, cdm_device_path, headers=None, query_params=None):
2020
"""
2121
Extract Widevine CONTENT keys (KID/KEY) from a license using pywidevine.
2222
2323
Args:
2424
pssh (str): PSSH base64.
2525
license_url (str): Widevine license URL.
2626
cdm_device_path (str): Path to CDM file (device.wvd).
27-
headers (dict): Optional HTTP headers.
27+
headers (dict): Optional HTTP headers for the license request (from fetch).
28+
query_params (dict): Optional query parameters to append to the URL.
2829
2930
Returns:
3031
list: List of dicts {'kid': ..., 'key': ...} (only CONTENT keys) or None if error.
@@ -40,13 +41,26 @@ def get_widevine_keys(pssh, license_url, cdm_device_path, headers=None, payload=
4041

4142
try:
4243
challenge = cdm.get_license_challenge(session_id, PSSH(pssh))
43-
req_headers = headers or {}
44-
req_headers['Content-Type'] = 'application/octet-stream'
45-
46-
# Send license request using curl_cffi
44+
45+
# Build request URL with query params
46+
request_url = license_url
47+
if query_params:
48+
request_url = f"{license_url}?{urlencode(query_params)}"
49+
50+
# Prepare headers (use original headers from fetch)
51+
req_headers = headers.copy() if headers else {}
52+
request_kwargs = {}
53+
request_kwargs['data'] = challenge
54+
55+
# Keep original Content-Type or default to octet-stream
56+
if 'Content-Type' not in req_headers:
57+
req_headers['Content-Type'] = 'application/octet-stream'
58+
59+
# Send license request
4760
try:
4861
# response = httpx.post(license_url, data=challenge, headers=req_headers, content=payload)
49-
response = requests.post(license_url, data=challenge, headers=req_headers, json=payload, impersonate="chrome124")
62+
response = requests.post(request_url, headers=req_headers, impersonate="chrome124", **request_kwargs)
63+
5064
except Exception as e:
5165
console.print(f"[bold red]Request error:[/bold red] {e}")
5266
return None
@@ -56,60 +70,51 @@ def get_widevine_keys(pssh, license_url, cdm_device_path, headers=None, payload=
5670
console.print({
5771
"url": license_url,
5872
"headers": req_headers,
59-
"content": payload,
6073
"session_id": session_id.hex(),
6174
"pssh": pssh
6275
})
63-
6476
return None
6577

66-
# Handle (JSON) or classic (binary) license response
67-
license_data = response.content
78+
# Parse license response
79+
license_bytes = response.content
6880
content_type = response.headers.get("Content-Type", "")
69-
logging.info(f"License data: {license_data}, Content-Type: {content_type}")
70-
71-
# Check if license_data is empty
72-
if not license_data:
73-
console.print("[bold red]License response is empty.[/bold red]")
74-
return None
7581

82+
# Handle JSON response
7683
if "application/json" in content_type:
7784
try:
78-
79-
# Try to decode as JSON only if plausible
80-
data = None
81-
try:
82-
data = response.json()
83-
except Exception:
84-
data = None
85-
86-
if data and "license" in data:
87-
license_data = base64.b64decode(data["license"])
88-
89-
elif data is not None:
90-
console.print("[bold red]'license' field not found in JSON response.[/bold red]")
85+
data = response.json()
86+
if "license" in data:
87+
license_bytes = base64.b64decode(data["license"])
88+
else:
89+
console.print(f"[bold red]'license' field not found in JSON response: {data}.[/bold red]")
9190
return None
92-
9391
except Exception as e:
9492
console.print(f"[bold red]Error parsing JSON license:[/bold red] {e}")
93+
return None
9594

96-
cdm.parse_license(session_id, license_data)
95+
if not license_bytes:
96+
console.print("[bold red]License data is empty.[/bold red]")
97+
return None
98+
99+
# Parse license
100+
try:
101+
cdm.parse_license(session_id, license_bytes)
102+
except Exception as e:
103+
console.print(f"[bold red]Error parsing license:[/bold red] {e}")
104+
return None
97105

98-
# Extract only CONTENT keys from the license
106+
# Extract CONTENT keys
99107
content_keys = []
100108
for key in cdm.get_keys(session_id):
101109
if key.type == "CONTENT":
102-
103110
kid = key.kid.hex() if isinstance(key.kid, bytes) else str(key.kid)
104111
key_val = key.key.hex() if isinstance(key.key, bytes) else str(key.key)
105112

106113
content_keys.append({
107114
'kid': kid.replace('-', '').strip(),
108115
'key': key_val.replace('-', '').strip()
109116
})
110-
logging.info(f"Use kid: {kid}, key: {key_val}")
111117

112-
# Check if content_keys list is empty
113118
if not content_keys:
114119
console.print("[bold yellow]⚠️ No CONTENT keys found in license.[/bold yellow]")
115120
return None

StreamingCommunity/Lib/Downloader/DASH/downloader.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,14 @@ def download_subtitles(self) -> bool:
227227

228228
return True
229229

230-
def download_and_decrypt(self, custom_headers=None, custom_payload=None):
230+
def download_and_decrypt(self, custom_headers=None, query_params=None):
231231
"""
232232
Download and decrypt video/audio streams. Skips download if file already exists.
233+
234+
Args:
235+
custom_headers (dict): Optional HTTP headers for the license request.
236+
query_params (dict): Optional query parameters to append to the license URL.
237+
license_data (str/bytes): Optional raw license data to bypass HTTP request.
233238
"""
234239
if self.file_already_exists:
235240
console.print(f"[red]File already exists: {self.original_output_path}[/red]")
@@ -250,7 +255,7 @@ def download_and_decrypt(self, custom_headers=None, custom_payload=None):
250255
license_url=self.license_url,
251256
cdm_device_path=self.cdm_device,
252257
headers=custom_headers,
253-
payload=custom_payload
258+
query_params=query_params,
254259
)
255260

256261
if not keys:

Test/Downloads/DASH.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,20 @@
1818
start_message()
1919
logger = Logger()
2020

21-
license_url = ""
21+
2222
mpd_url = ""
23+
mpd_headers = {}
24+
license_url = ""
25+
license_params = {}
2326

2427
dash_process = DASH_Downloader(
2528
license_url=license_url,
2629
mpd_url=mpd_url,
2730
output_path="out.mp4",
2831
)
29-
dash_process.parse_manifest()
32+
dash_process.parse_manifest(custom_headers=mpd_headers)
3033

31-
if dash_process.download_and_decrypt():
34+
if dash_process.download_and_decrypt(query_params=license_params):
3235
dash_process.finalize_output()
3336

3437
status = dash_process.get_status()

0 commit comments

Comments
 (0)