StreamingCommunity 3.3.6__py3-none-any.whl → 3.3.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of StreamingCommunity might be problematic. Click here for more details.

Files changed (48) hide show
  1. StreamingCommunity/Api/Site/altadefinizione/film.py +1 -1
  2. StreamingCommunity/Api/Site/altadefinizione/series.py +1 -1
  3. StreamingCommunity/Api/Site/animeunity/serie.py +2 -2
  4. StreamingCommunity/Api/Site/animeworld/film.py +1 -1
  5. StreamingCommunity/Api/Site/animeworld/serie.py +2 -2
  6. StreamingCommunity/Api/Site/crunchyroll/film.py +3 -2
  7. StreamingCommunity/Api/Site/crunchyroll/series.py +3 -2
  8. StreamingCommunity/Api/Site/crunchyroll/site.py +0 -8
  9. StreamingCommunity/Api/Site/crunchyroll/util/get_license.py +11 -105
  10. StreamingCommunity/Api/Site/guardaserie/series.py +1 -1
  11. StreamingCommunity/Api/Site/mediasetinfinity/film.py +1 -1
  12. StreamingCommunity/Api/Site/mediasetinfinity/series.py +7 -9
  13. StreamingCommunity/Api/Site/mediasetinfinity/site.py +29 -66
  14. StreamingCommunity/Api/Site/mediasetinfinity/util/ScrapeSerie.py +5 -1
  15. StreamingCommunity/Api/Site/mediasetinfinity/util/get_license.py +151 -233
  16. StreamingCommunity/Api/Site/raiplay/film.py +2 -10
  17. StreamingCommunity/Api/Site/raiplay/series.py +2 -10
  18. StreamingCommunity/Api/Site/raiplay/site.py +1 -0
  19. StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py +7 -1
  20. StreamingCommunity/Api/Site/streamingcommunity/film.py +1 -1
  21. StreamingCommunity/Api/Site/streamingcommunity/series.py +1 -1
  22. StreamingCommunity/Api/Site/streamingwatch/film.py +1 -1
  23. StreamingCommunity/Api/Site/streamingwatch/series.py +1 -1
  24. StreamingCommunity/Api/Template/loader.py +158 -0
  25. StreamingCommunity/Lib/Downloader/DASH/downloader.py +267 -51
  26. StreamingCommunity/Lib/Downloader/DASH/segments.py +46 -15
  27. StreamingCommunity/Lib/Downloader/HLS/downloader.py +51 -36
  28. StreamingCommunity/Lib/Downloader/HLS/segments.py +105 -25
  29. StreamingCommunity/Lib/Downloader/MP4/downloader.py +12 -13
  30. StreamingCommunity/Lib/FFmpeg/command.py +18 -81
  31. StreamingCommunity/Lib/FFmpeg/util.py +14 -10
  32. StreamingCommunity/Lib/M3U8/estimator.py +13 -12
  33. StreamingCommunity/Lib/M3U8/parser.py +16 -16
  34. StreamingCommunity/Upload/update.py +2 -4
  35. StreamingCommunity/Upload/version.py +2 -2
  36. StreamingCommunity/Util/config_json.py +3 -132
  37. StreamingCommunity/Util/installer/bento4_install.py +21 -31
  38. StreamingCommunity/Util/installer/device_install.py +0 -1
  39. StreamingCommunity/Util/installer/ffmpeg_install.py +0 -1
  40. StreamingCommunity/Util/message.py +8 -9
  41. StreamingCommunity/Util/os.py +0 -8
  42. StreamingCommunity/run.py +4 -44
  43. {streamingcommunity-3.3.6.dist-info → streamingcommunity-3.3.8.dist-info}/METADATA +1 -3
  44. {streamingcommunity-3.3.6.dist-info → streamingcommunity-3.3.8.dist-info}/RECORD +48 -47
  45. {streamingcommunity-3.3.6.dist-info → streamingcommunity-3.3.8.dist-info}/WHEEL +0 -0
  46. {streamingcommunity-3.3.6.dist-info → streamingcommunity-3.3.8.dist-info}/entry_points.txt +0 -0
  47. {streamingcommunity-3.3.6.dist-info → streamingcommunity-3.3.8.dist-info}/licenses/LICENSE +0 -0
  48. {streamingcommunity-3.3.6.dist-info → streamingcommunity-3.3.8.dist-info}/top_level.txt +0 -0
@@ -1,20 +1,15 @@
1
1
  # 16.03.25
2
2
 
3
- import json
4
- import time
3
+ import re
4
+ import uuid
5
5
  from urllib.parse import urlencode
6
6
  import xml.etree.ElementTree as ET
7
7
 
8
8
 
9
9
  # External library
10
10
  import httpx
11
+ from bs4 import BeautifulSoup
11
12
  from rich.console import Console
12
- try:
13
- from seleniumbase import Driver
14
- SELENIUMBASE_AVAILABLE = True
15
- except ImportError:
16
- SELENIUMBASE_AVAILABLE = False
17
- Driver = None
18
13
 
19
14
 
20
15
  # Internal utilities
@@ -25,192 +20,91 @@ from StreamingCommunity.Util.headers import get_headers, get_userAgent
25
20
  # Variable
26
21
  console = Console()
27
22
  MAX_TIMEOUT = config_manager.get_int("REQUESTS", "timeout")
28
- beToken = None
29
23
  network_data = []
24
+ class_mediaset_api = None
30
25
 
31
26
 
32
- def save_network_data(data):
33
- """Save network data and check for beToken"""
34
- global network_data
35
-
36
- # Filter only for login API responses
37
- if data.get('method') == 'Network.responseReceived':
38
- params = data.get('params', {})
39
- response = params.get('response', {})
40
- url = response.get('url', '')
27
+ class MediasetAPI:
28
+ def __init__(self):
29
+ self.client_id = str(uuid.uuid4())
30
+ self.headers = get_headers()
31
+ self.app_name = self.get_app_name()
32
+ self.beToken = self.generate_betoken()
33
+ self.sha256Hash = self.getHash2c()
41
34
 
42
- if "persona/login/v2.0" in url and params.get('type') != 'Preflight':
43
- network_data.append(data)
44
- console.print(f"[green]Found login API request to: {url}")
45
-
46
-
47
- def generate_betoken(username: str, password: str, sleep_action: float = 1.0) -> str:
48
- """Generate beToken using browser automation"""
49
-
50
- if not SELENIUMBASE_AVAILABLE:
51
- console.print("[red]Error: seleniumbase is not installed. Cannot perform browser login.")
52
- console.print("[yellow]Install seleniumbase with: pip install seleniumbase")
53
- return None
54
-
55
- if not Driver:
56
- console.print("[red]Error: seleniumbase Driver is not available.")
57
- return None
58
-
59
- driver = Driver(uc=True, uc_cdp_events=True, incognito=True, headless=True)
60
-
61
- try:
62
- console.print("[cyan]Launching browser...")
63
- be_token_holder = {"token": None}
64
- global network_data
65
- network_data = [] # Reset network data
66
-
67
- # Load home page
68
- console.print("[cyan]Navigating to Mediaset Play...")
69
- driver.uc_open_with_reconnect("https://www.mediasetplay.mediaset.it", sleep_action)
35
+ def get_app_name(self):
36
+ html = self.fetch_html()
37
+ soup = BeautifulSoup(html, "html.parser")
38
+ meta_tag = soup.find('meta', attrs={'name': 'app-name'})
70
39
 
71
- # Add CDP listeners after opening the page
72
- driver.add_cdp_listener(
73
- "Network.responseReceived",
74
- lambda data: save_network_data(data)
75
- )
76
- driver.sleep(sleep_action)
77
-
78
- # Accept privacy policy if present
79
- try:
80
- driver.click("#rti-privacy-accept-btn-screen1-id", timeout=3)
81
- except Exception:
82
- pass
83
-
84
- # Click Login using the specific div structure
85
- console.print("[cyan]Clicking login button...")
86
- try:
87
- driver.click('div.dwv_v span:contains("Login")', timeout=5)
88
- except Exception as e:
89
- print(f"Error clicking login: {e}")
90
- try:
91
- driver.click('div.dwv_v', timeout=3)
92
- except Exception:
93
- pass
94
-
95
- driver.sleep(sleep_action)
96
-
97
- # Click "Accedi con email e password" using the specific input
98
- console.print("[cyan]Clicking email/password login...")
99
- try:
100
- driver.click('input.gigya-input-submit[value="Accedi con email e password"]', timeout=5)
101
- except Exception as e:
102
- print(f"Error clicking email login: {e}")
103
- return None
104
-
105
- driver.sleep(sleep_action)
106
-
107
- # Fill login credentials using specific IDs
108
- console.print("[cyan]Filling login credentials...")
109
- try:
110
- email_input = 'input[name="username"].gigya-input-text'
111
- driver.wait_for_element(email_input, timeout=5)
112
- driver.type(email_input, username)
113
-
114
- password_input = 'input[name="password"].gigya-input-password'
115
- driver.wait_for_element(password_input, timeout=5)
116
- driver.type(password_input, password)
117
-
118
- except Exception as e:
119
- print(f"Error filling credentials: {e}")
120
- return None
121
-
122
- driver.sleep(sleep_action)
123
-
124
- # Click Continue/Procedi using the submit button
125
- console.print("[cyan]Clicking continue button...")
126
- try:
127
- driver.click('input.gigya-input-submit[type="submit"][value="Continua"]', timeout=5)
128
- except Exception as e:
129
- print(f"Error clicking continue: {e}")
130
- return None
131
-
132
- # Wait for login response and parse network data
133
- console.print("[cyan]Waiting for login response...")
134
- for attempt in range(30):
135
- driver.sleep(0.3)
136
-
137
- # Check network data for beToken - skip preflight requests
138
- for data in network_data:
139
- if data.get('method') == 'Network.responseReceived':
140
- params = data.get('params', {})
141
- response = params.get('response', {})
142
- url = response.get('url', '')
143
- request_type = params.get('type', '')
144
-
145
- if "persona/login/v2.0" in url and request_type != 'Preflight':
146
- request_id = params.get('requestId')
147
-
148
- if request_id:
149
- try:
150
- response_body = driver.execute_cdp_cmd(
151
- 'Network.getResponseBody',
152
- {'requestId': request_id}
153
- )
154
- body = response_body.get('body', '')
155
-
156
- if body:
157
- response_data = json.loads(body)
158
- be_token = response_data.get("response", {}).get("beToken")
159
-
160
- if be_token:
161
- be_token_holder["token"] = be_token
162
- console.print("[green]Login successful! BeToken found!")
163
- return be_token
164
-
165
- except Exception:
166
- continue
167
-
168
- console.print(f"[yellow]Login completed. Total network events captured: {len(network_data)}")
169
- return be_token_holder["token"]
40
+ if meta_tag:
41
+ return meta_tag.get('content')
170
42
 
171
- finally:
172
- driver.quit()
43
+ def getHash256(self):
44
+ return self.sha256Hash
45
+
46
+ def getBearerToken(self):
47
+ return self.beToken
48
+
49
+ def generate_betoken(self):
50
+ json_data = {
51
+ 'appName': self.app_name,
52
+ 'client_id': self.client_id,
53
+ }
54
+ response = httpx.post(
55
+ 'https://api-ott-prod-fe.mediaset.net/PROD/play/idm/anonymous/login/v2.0',
56
+ headers=self.headers,
57
+ json=json_data,
58
+ )
59
+ return response.json()['response']['beToken']
60
+
61
+ def fetch_html(self, timeout=10):
62
+ r = httpx.get("https://mediasetinfinity.mediaset.it/", timeout=timeout, headers=self.headers)
63
+ r.raise_for_status()
64
+ return r.text
65
+
66
+ def find_relevant_script(self, html):
67
+ soup = BeautifulSoup(html, "html.parser")
68
+ return [s.get_text() for s in soup.find_all("script") if "imageEngines" in s.get_text()]
69
+
70
+ def extract_pairs_from_scripts(self, scripts):
71
+ # Chi ha inventato questo metodo di offuscare le chiavi merita di essere fustigato in piazza.
72
+ relevant_part = scripts[0].replace('\\"', '').split('...Option')[1].split('imageEngines')[0]
73
+ pairs = {}
74
+ for match in re.finditer(r'([a-f0-9]{64}):\$(\w+)', relevant_part):
75
+ pairs[match.group(1)] = f"${match.group(2)}"
76
+ return pairs
77
+
78
+ def getHash2c(self):
79
+ html = self.fetch_html()
80
+ scripts = self.find_relevant_script(html)[0:1]
81
+ pairs = self.extract_pairs_from_scripts(scripts)
82
+ return next((h for h, k in pairs.items() if k == "$2a"), None)
83
+
84
+ def generate_request_headers(self):
85
+ return {
86
+ 'authorization': self.beToken,
87
+ 'user-agent': self.headers['user-agent'],
88
+ 'x-m-device-id': self.client_id,
89
+ 'x-m-platform': 'WEB',
90
+ 'x-m-property': 'MPLAY',
91
+ 'x-m-sid': self.client_id
92
+ }
173
93
 
174
94
 
175
95
  def get_bearer_token():
176
96
  """
177
97
  Gets the BEARER_TOKEN for authentication.
178
-
179
- Returns:
180
- str: The bearer token string.
98
+ Anche i manifestanti per strada dio bellissimo.
181
99
  """
182
- global beToken
183
-
184
- # Read beToken from config if already present
185
- beToken = config_manager.get_dict("SITE_LOGIN", "mediasetinfinity")["beToken"]
186
- if beToken is not None and len(beToken) != 0:
187
- return beToken
188
-
189
- username = config_manager.get_dict("SITE_LOGIN", "mediasetinfinity").get("username", "")
190
- password = config_manager.get_dict("SITE_LOGIN", "mediasetinfinity").get("password", "")
191
-
192
- if username and password:
193
- if not SELENIUMBASE_AVAILABLE:
194
- console.print("[yellow]Warning: seleniumbase not available. Cannot perform automatic login.")
195
- console.print("[yellow]Please manually obtain beToken and set it in config.")
196
- return config_manager.get_dict("SITE_LOGIN", "mediasetinfinity")["beToken"]
197
-
198
- beToken = generate_betoken(username, password)
199
-
200
- if beToken is not None:
201
-
202
- # Save current beToken
203
- current_value = config_manager.get("SITE_LOGIN", "mediasetinfinity", dict)
204
- current_value["beToken"] = beToken
205
- config_manager.set_key("SITE_LOGIN", "mediasetinfinity", current_value)
206
- config_manager.save_config()
207
-
208
- return beToken
100
+ global class_mediaset_api
101
+ if class_mediaset_api is None:
102
+ class_mediaset_api = MediasetAPI()
103
+ return class_mediaset_api
209
104
 
210
- return config_manager.get_dict("SITE_LOGIN", "mediasetinfinity")["beToken"]
211
105
 
212
106
 
213
- def get_playback_url(BEARER_TOKEN, CONTENT_ID):
107
+ def get_playback_url(CONTENT_ID):
214
108
  """
215
109
  Gets the playback URL for the specified content.
216
110
 
@@ -222,7 +116,7 @@ def get_playback_url(BEARER_TOKEN, CONTENT_ID):
222
116
  dict: The playback JSON object.
223
117
  """
224
118
  headers = get_headers()
225
- headers['authorization'] = f'Bearer {BEARER_TOKEN}'
119
+ headers['authorization'] = f'Bearer {class_mediaset_api.getBearerToken()}'
226
120
 
227
121
  json_data = {
228
122
  'contentId': CONTENT_ID,
@@ -254,75 +148,101 @@ def get_playback_url(BEARER_TOKEN, CONTENT_ID):
254
148
  except Exception as e:
255
149
  raise RuntimeError(f"Failed to get playback URL: {e}")
256
150
 
257
-
258
- def parse_tracking_data(tracking_value):
151
+ def parse_smil_for_media_info(smil_xml):
259
152
  """
260
- Parses the trackingData string into a dictionary.
261
-
262
- Args:
263
- tracking_value (str): The tracking data string.
264
-
265
- Returns:
266
- dict: Parsed tracking data.
267
- """
268
- return dict(item.split('=', 1) for item in tracking_value.split('|') if '=' in item)
269
-
270
-
271
- def parse_smil_for_tracking_and_video(smil_xml):
272
- """
273
- Extracts all video_src and trackingData pairs from the SMIL.
153
+ Extracts video streams with quality info and subtitle streams from SMIL.
274
154
 
275
155
  Args:
276
156
  smil_xml (str): The SMIL XML as a string.
277
157
 
278
158
  Returns:
279
- list: A list of dicts: {'video_src': ..., 'tracking_info': ...}
280
- """
281
- results = []
159
+ dict: {
160
+ 'videos': [{'url': str, 'quality': str, 'clipBegin': str, 'clipEnd': str, 'tracking_data': dict}, ...],
161
+ 'subtitles': [{'url': str, 'lang': str, 'type': str}, ...]
162
+ }
163
+ """
282
164
  root = ET.fromstring(smil_xml)
283
165
  ns = {'smil': root.tag.split('}')[0].strip('{')}
284
-
285
- # Search all <par>
166
+
167
+ videos = []
168
+ subtitles_raw = []
169
+
170
+ # Process all <par> elements
286
171
  for par in root.findall('.//smil:par', ns):
287
- video_src = None
288
- tracking_info = None
289
-
290
- # Search <video> inside <par>
291
- video_elem = par.find('.//smil:video', ns)
292
- if video_elem is not None:
293
- video_src = video_elem.attrib.get('src')
294
172
 
295
- # Search <ref> inside <par>
173
+ # Extract video information from <ref>
296
174
  ref_elem = par.find('.//smil:ref', ns)
297
175
  if ref_elem is not None:
298
- # Search <param name="trackingData">
176
+ url = ref_elem.attrib.get('src')
177
+ title = ref_elem.attrib.get('title', '')
178
+
179
+ # Parse tracking data inline
180
+ tracking_data = {}
299
181
  for param in ref_elem.findall('.//smil:param', ns):
300
182
  if param.attrib.get('name') == 'trackingData':
301
- tracking_value = param.attrib.get('value')
302
- if tracking_value:
303
- tracking_info = parse_tracking_data(tracking_value)
183
+ tracking_value = param.attrib.get('value', '')
184
+ tracking_data = dict(item.split('=', 1) for item in tracking_value.split('|') if '=' in item)
304
185
  break
186
+
187
+ if url and url.endswith('.mpd'):
188
+ video_info = {
189
+ 'url': url,
190
+ 'title': title,
191
+ 'tracking_data': tracking_data
192
+ }
193
+ videos.append(video_info)
194
+
195
+ # Extract subtitle information from <textstream>
196
+ for textstream in par.findall('.//smil:textstream', ns):
197
+ sub_url = textstream.attrib.get('src')
198
+ lang = textstream.attrib.get('lang', 'unknown')
199
+ sub_type = textstream.attrib.get('type', 'unknown')
200
+
201
+ if sub_url:
202
+ subtitle_info = {
203
+ 'url': sub_url,
204
+ 'language': lang,
205
+ 'type': sub_type
206
+ }
207
+ subtitles_raw.append(subtitle_info)
208
+
209
+ # Filter subtitles: prefer VTT, fallback to SRT
210
+ subtitles_by_lang = {}
211
+ for sub in subtitles_raw:
212
+ lang = sub['language']
213
+ if lang not in subtitles_by_lang:
214
+ subtitles_by_lang[lang] = []
215
+ subtitles_by_lang[lang].append(sub)
216
+
217
+ subtitles = []
218
+ for lang, subs in subtitles_by_lang.items():
219
+ vtt_subs = [s for s in subs if s['type'] == 'text/vtt']
220
+ if vtt_subs:
221
+ subtitles.append(vtt_subs[0]) # Take first VTT
222
+
223
+ else:
224
+ srt_subs = [s for s in subs if s['type'] == 'text/srt']
225
+ if srt_subs:
226
+ subtitles.append(srt_subs[0]) # Take first SRT
227
+
228
+ return {
229
+ 'videos': videos,
230
+ 'subtitles': subtitles
231
+ }
305
232
 
306
- if video_src and tracking_info:
307
- results.append({'video_src': video_src, 'tracking_info': tracking_info})
308
-
309
- return results
310
-
311
-
312
- def get_tracking_info(BEARER_TOKEN, PLAYBACK_JSON):
233
+ def get_tracking_info(PLAYBACK_JSON):
313
234
  """
314
- Retrieves tracking information from the playback JSON.
235
+ Retrieves media information including videos and subtitles from the playback JSON.
315
236
 
316
237
  Args:
317
- BEARER_TOKEN (str): The authentication token.
318
238
  PLAYBACK_JSON (dict): The playback JSON object.
319
239
 
320
240
  Returns:
321
- list or None: List of tracking info dicts, or None if request fails.
241
+ dict or None: {'videos': [...], 'subtitles': [...]}, or None if request fails.
322
242
  """
323
243
  params = {
324
244
  "format": "SMIL",
325
- "auth": BEARER_TOKEN,
245
+ "auth": class_mediaset_api.getBearerToken(),
326
246
  "formats": "MPEG-DASH",
327
247
  "assetTypes": "HR,browser,widevine,geoIT|geoNo:HR,browser,geoIT|geoNo:SD,browser,widevine,geoIT|geoNo:SD,browser,geoIT|geoNo:SS,browser,widevine,geoIT|geoNo:SS,browser,geoIT|geoNo",
328
248
  "balance": "true",
@@ -344,31 +264,29 @@ def get_tracking_info(BEARER_TOKEN, PLAYBACK_JSON):
344
264
  )
345
265
  response.raise_for_status()
346
266
 
347
- smil_xml = response.text
348
- time.sleep(0.2)
349
- results = parse_smil_for_tracking_and_video(smil_xml)
267
+ results = parse_smil_for_media_info(response.text)
350
268
  return results
351
269
 
352
- except Exception:
270
+ except Exception as e:
271
+ print(f"Error fetching tracking info: {e}")
353
272
  return None
354
273
 
355
274
 
356
- def generate_license_url(BEARER_TOKEN, tracking_info):
275
+ def generate_license_url(tracking_info):
357
276
  """
358
277
  Generates the URL to obtain the Widevine license.
359
278
 
360
279
  Args:
361
- BEARER_TOKEN (str): The authentication token.
362
280
  tracking_info (dict): The tracking info dictionary.
363
281
 
364
282
  Returns:
365
283
  str: The full license URL.
366
284
  """
367
285
  params = {
368
- 'releasePid': tracking_info['tracking_info'].get('pid'),
369
- 'account': f"http://access.auth.theplatform.com/data/Account/{tracking_info['tracking_info'].get('aid')}",
286
+ 'releasePid': tracking_info['tracking_data'].get('pid'),
287
+ 'account': f"http://access.auth.theplatform.com/data/Account/{tracking_info['tracking_data'].get('aid')}",
370
288
  'schema': '1.0',
371
- 'token': BEARER_TOKEN,
289
+ 'token': class_mediaset_api.getBearerToken(),
372
290
  }
373
291
 
374
292
  return f"{'https://widevine.entitlement.theplatform.eu/wv/web/ModularDrm/getRawWidevineLicense'}?{urlencode(params)}"
@@ -1,7 +1,6 @@
1
1
  # 21.05.24
2
2
 
3
3
  import os
4
- import sys
5
4
  from typing import Tuple
6
5
 
7
6
 
@@ -44,7 +43,7 @@ def download_film(select_title: MediaItem) -> Tuple[str, bool]:
44
43
  - bool: Whether download was stopped
45
44
  """
46
45
  start_message()
47
- console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{select_title.name}[/cyan] \n")
46
+ console.print(f"\n[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{select_title.name}[/cyan] \n")
48
47
 
49
48
  # Extract m3u8 URL from the film's URL
50
49
  response = httpx.get(select_title.url + ".json", headers=get_headers(), timeout=10)
@@ -64,17 +63,10 @@ def download_film(select_title: MediaItem) -> Tuple[str, bool]:
64
63
 
65
64
  # MPD
66
65
  else:
67
-
68
- # Check CDM file before usage
69
- cdm_device_path = get_wvd_path()
70
- if not cdm_device_path or not isinstance(cdm_device_path, (str, bytes, os.PathLike)) or not os.path.isfile(cdm_device_path):
71
- console.print(f"[bold red] CDM file not found or invalid path: {cdm_device_path}[/bold red]")
72
- sys.exit(0)
73
-
74
66
  license_url = generate_license_url(select_title.mpd_id)
75
67
 
76
68
  dash_process = DASH_Downloader(
77
- cdm_device=cdm_device_path,
69
+ cdm_device=get_wvd_path(),
78
70
  license_url=license_url,
79
71
  mpd_url=master_playlist,
80
72
  output_path=os.path.join(mp4_path, mp4_name),
@@ -1,7 +1,6 @@
1
1
  # 21.05.24
2
2
 
3
3
  import os
4
- import sys
5
4
  from typing import Tuple
6
5
 
7
6
 
@@ -58,7 +57,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
58
57
 
59
58
  # Get episode information
60
59
  obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
61
- console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{scrape_serie.series_name}[/cyan] \\ [bold magenta]{obj_episode.name}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
60
+ console.print(f"\n[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{scrape_serie.series_name}[/cyan] \\ [bold magenta]{obj_episode.name}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
62
61
 
63
62
  # Define filename and path
64
63
  mp4_name = f"{map_episode_title(scrape_serie.series_name, index_season_selected, index_episode_selected, obj_episode.name)}.mp4"
@@ -85,13 +84,6 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
85
84
 
86
85
  # MPD
87
86
  else:
88
-
89
- # Check CDM file before usage
90
- cdm_device_path = get_wvd_path()
91
- if not cdm_device_path or not isinstance(cdm_device_path, (str, bytes, os.PathLike)) or not os.path.isfile(cdm_device_path):
92
- console.print(f"[bold red] CDM file not found or invalid path: {cdm_device_path}[/bold red]")
93
- sys.exit(0)
94
-
95
87
  full_license_url = generate_license_url(obj_episode.mpd_id)
96
88
  license_headers = {
97
89
  'nv-authorizations': full_license_url.split("?")[1].split("=")[1],
@@ -99,7 +91,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
99
91
  }
100
92
 
101
93
  dash_process = DASH_Downloader(
102
- cdm_device=cdm_device_path,
94
+ cdm_device=get_wvd_path(),
103
95
  license_url=full_license_url.split("?")[0],
104
96
  mpd_url=master_playlist,
105
97
  output_path=os.path.join(mp4_path, mp4_name),
@@ -40,6 +40,7 @@ def determine_media_type(item):
40
40
  if not program_name:
41
41
  return "film"
42
42
 
43
+ # Dio stranamente guarda che giro bisogna fare per avere il tipo di media.
43
44
  scraper = GetSerieInfo(program_name)
44
45
  scraper.collect_info_title()
45
46
  return scraper.prog_tipology, scraper.prog_description, scraper.prog_year
@@ -41,7 +41,12 @@ class GetSerieInfo:
41
41
 
42
42
  response.raise_for_status()
43
43
  json_data = response.json()
44
- self.prog_tipology = "tv" if "tv" in json_data.get('track_info').get('typology') else "film"
44
+
45
+ # Dio santissimo ma chi ha fatto le cose cosi di merda.
46
+ type_check_1 = "tv" if json_data.get('program_info', {}).get('layout', 'single') == 'multi' else "film"
47
+ #type_check_2 = "tv" if "tv" in json_data.get('track_info', {}).get('typology', '') else "film"
48
+
49
+ self.prog_tipology = type_check_1
45
50
  self.prog_description = json_data.get('program_info', '').get('vanity', '')
46
51
  self.prog_year = json_data.get('program_info', '').get('year', '')
47
52
 
@@ -82,6 +87,7 @@ class GetSerieInfo:
82
87
  try:
83
88
  season = self.seasons_manager.get_season_by_number(number_season)
84
89
 
90
+ # Se stai leggendo questo codice spieami perche hai fatto cosi.
85
91
  url = f"{self.base_url}/programmi/{self.program_name}/{self.publishing_block_id}/{season.id}/episodes.json"
86
92
  response = httpx.get(url=url, headers=get_headers(), timeout=max_timeout)
87
93
  response.raise_for_status()
@@ -52,7 +52,7 @@ def download_film(select_title: MediaItem) -> str:
52
52
 
53
53
  # Start message and display film information
54
54
  start_message()
55
- console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{select_title.name}[/cyan] \n")
55
+ console.print(f"\n[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{select_title.name}[/cyan] \n")
56
56
 
57
57
  # Init class
58
58
  video_source = VideoSource(f"{site_constant.FULL_URL}/it", False, select_title.id)
@@ -55,7 +55,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
55
55
 
56
56
  # Get episode information
57
57
  obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
58
- console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{scrape_serie.series_name}[/cyan] \\ [bold magenta]{obj_episode.name}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
58
+ console.print(f"\n[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{scrape_serie.series_name}[/cyan] \\ [bold magenta]{obj_episode.name}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
59
59
 
60
60
  if site_constant.TELEGRAM_BOT:
61
61
  bot = get_bot_instance()
@@ -38,7 +38,7 @@ def download_film(select_title: MediaItem) -> str:
38
38
  - str: output path
39
39
  """
40
40
  start_message()
41
- console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{select_title.name}[/cyan] \n")
41
+ console.print(f"\n[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{select_title.name}[/cyan] \n")
42
42
 
43
43
  # Get master playlists
44
44
  video_source = VideoSource()
@@ -53,7 +53,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
53
53
 
54
54
  # Get episode information
55
55
  obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
56
- console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{scrape_serie.series_name}[/cyan] \\ [bold magenta]{obj_episode.name}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
56
+ console.print(f"\n[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{scrape_serie.series_name}[/cyan] \\ [bold magenta]{obj_episode.name}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
57
57
 
58
58
  # Define filename and path for the downloaded video
59
59
  mp4_name = f"{map_episode_title(scrape_serie.series_name, index_season_selected, index_episode_selected, obj_episode.name)}.mp4"