StreamingCommunity 3.2.8__py3-none-any.whl → 3.3.0__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 (86) hide show
  1. StreamingCommunity/Api/Player/Helper/Vixcloud/util.py +2 -1
  2. StreamingCommunity/Api/Player/hdplayer.py +2 -2
  3. StreamingCommunity/Api/Player/sweetpixel.py +5 -8
  4. StreamingCommunity/Api/Site/altadefinizione/__init__.py +32 -15
  5. StreamingCommunity/Api/Site/altadefinizione/film.py +10 -8
  6. StreamingCommunity/Api/Site/altadefinizione/series.py +9 -7
  7. StreamingCommunity/Api/Site/altadefinizione/site.py +1 -1
  8. StreamingCommunity/Api/Site/animeunity/__init__.py +31 -15
  9. StreamingCommunity/Api/Site/animeunity/serie.py +2 -2
  10. StreamingCommunity/Api/Site/animeworld/__init__.py +33 -7
  11. StreamingCommunity/Api/Site/animeworld/site.py +3 -5
  12. StreamingCommunity/Api/Site/animeworld/util/ScrapeSerie.py +8 -10
  13. StreamingCommunity/Api/Site/crunchyroll/__init__.py +44 -12
  14. StreamingCommunity/Api/Site/crunchyroll/film.py +9 -7
  15. StreamingCommunity/Api/Site/crunchyroll/series.py +9 -7
  16. StreamingCommunity/Api/Site/crunchyroll/site.py +16 -1
  17. StreamingCommunity/Api/Site/guardaserie/__init__.py +36 -10
  18. StreamingCommunity/Api/Site/guardaserie/series.py +8 -6
  19. StreamingCommunity/Api/Site/guardaserie/site.py +0 -3
  20. StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +1 -2
  21. StreamingCommunity/Api/Site/mediasetinfinity/__init__.py +37 -12
  22. StreamingCommunity/Api/Site/mediasetinfinity/film.py +10 -16
  23. StreamingCommunity/Api/Site/mediasetinfinity/series.py +12 -18
  24. StreamingCommunity/Api/Site/mediasetinfinity/site.py +18 -3
  25. StreamingCommunity/Api/Site/mediasetinfinity/util/ScrapeSerie.py +214 -180
  26. StreamingCommunity/Api/Site/mediasetinfinity/util/get_license.py +2 -31
  27. StreamingCommunity/Api/Site/raiplay/__init__.py +47 -12
  28. StreamingCommunity/Api/Site/raiplay/film.py +42 -10
  29. StreamingCommunity/Api/Site/raiplay/series.py +53 -11
  30. StreamingCommunity/Api/Site/raiplay/site.py +4 -1
  31. StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py +2 -1
  32. StreamingCommunity/Api/Site/raiplay/util/get_license.py +40 -0
  33. StreamingCommunity/Api/Site/streamingcommunity/__init__.py +5 -8
  34. StreamingCommunity/Api/Site/streamingcommunity/film.py +7 -5
  35. StreamingCommunity/Api/Site/streamingcommunity/series.py +9 -7
  36. StreamingCommunity/Api/Site/streamingcommunity/site.py +8 -3
  37. StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +5 -2
  38. StreamingCommunity/Api/Site/streamingwatch/__init__.py +43 -9
  39. StreamingCommunity/Api/Site/streamingwatch/film.py +7 -5
  40. StreamingCommunity/Api/Site/streamingwatch/series.py +8 -6
  41. StreamingCommunity/Api/Site/streamingwatch/site.py +3 -1
  42. StreamingCommunity/Api/Site/streamingwatch/util/ScrapeSerie.py +3 -3
  43. StreamingCommunity/Api/Template/Util/__init__.py +10 -1
  44. StreamingCommunity/Api/Template/Util/manage_ep.py +4 -4
  45. StreamingCommunity/Api/Template/__init__.py +5 -1
  46. StreamingCommunity/Api/Template/site.py +10 -6
  47. StreamingCommunity/Lib/Downloader/DASH/cdm_helpher.py +13 -12
  48. StreamingCommunity/Lib/Downloader/DASH/decrypt.py +1 -1
  49. StreamingCommunity/Lib/Downloader/DASH/downloader.py +24 -22
  50. StreamingCommunity/Lib/Downloader/DASH/parser.py +1 -1
  51. StreamingCommunity/Lib/Downloader/DASH/segments.py +4 -3
  52. StreamingCommunity/Lib/Downloader/HLS/downloader.py +11 -9
  53. StreamingCommunity/Lib/Downloader/HLS/segments.py +4 -9
  54. StreamingCommunity/Lib/Downloader/MP4/downloader.py +25 -6
  55. StreamingCommunity/Lib/Downloader/TOR/downloader.py +3 -5
  56. StreamingCommunity/Lib/Downloader/__init__.py +9 -1
  57. StreamingCommunity/Lib/FFmpeg/__init__.py +10 -1
  58. StreamingCommunity/Lib/FFmpeg/command.py +4 -6
  59. StreamingCommunity/Lib/FFmpeg/util.py +1 -1
  60. StreamingCommunity/Lib/M3U8/__init__.py +9 -1
  61. StreamingCommunity/Lib/M3U8/decryptor.py +8 -4
  62. StreamingCommunity/Lib/M3U8/estimator.py +0 -6
  63. StreamingCommunity/Lib/M3U8/parser.py +1 -1
  64. StreamingCommunity/Lib/M3U8/url_fixer.py +1 -1
  65. StreamingCommunity/Lib/TMBD/__init__.py +6 -1
  66. StreamingCommunity/TelegramHelp/config.json +1 -5
  67. StreamingCommunity/TelegramHelp/telegram_bot.py +9 -10
  68. StreamingCommunity/Upload/update.py +2 -2
  69. StreamingCommunity/Upload/version.py +1 -1
  70. StreamingCommunity/Util/config_json.py +139 -59
  71. StreamingCommunity/Util/http_client.py +201 -0
  72. StreamingCommunity/Util/message.py +1 -1
  73. StreamingCommunity/Util/os.py +8 -5
  74. StreamingCommunity/Util/table.py +3 -3
  75. StreamingCommunity/__init__.py +9 -1
  76. StreamingCommunity/run.py +394 -258
  77. {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/METADATA +147 -45
  78. streamingcommunity-3.3.0.dist-info/RECORD +110 -0
  79. StreamingCommunity/Api/Site/cb01new/__init__.py +0 -72
  80. StreamingCommunity/Api/Site/cb01new/film.py +0 -62
  81. StreamingCommunity/Api/Site/cb01new/site.py +0 -78
  82. streamingcommunity-3.2.8.dist-info/RECORD +0 -111
  83. {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/WHEEL +0 -0
  84. {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/entry_points.txt +0 -0
  85. {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/licenses/LICENSE +0 -0
  86. {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/top_level.txt +0 -0
@@ -54,13 +54,13 @@ def download_film(select_title: MediaItem) -> str:
54
54
  query_params = parse_qs(parsed_url.query)
55
55
 
56
56
  # Download the episode
57
- r_proc = DASH_Downloader(
57
+ dash_process = DASH_Downloader(
58
58
  cdm_device=get_wvd_path(),
59
59
  license_url='https://www.crunchyroll.com/license/v1/license/widevine',
60
60
  mpd_url=mpd_url,
61
61
  output_path=os.path.join(mp4_path, mp4_name),
62
62
  )
63
- r_proc.parse_manifest(custom_headers=mpd_headers)
63
+ dash_process.parse_manifest(custom_headers=mpd_headers)
64
64
 
65
65
  # Create headers for license request
66
66
  license_headers = mpd_headers.copy()
@@ -69,14 +69,16 @@ def download_film(select_title: MediaItem) -> str:
69
69
  "x-cr-video-token": query_params['playbackGuid'][0],
70
70
  })
71
71
 
72
- if r_proc.download_and_decrypt(custom_headers=license_headers):
73
- r_proc.finalize_output()
72
+ if dash_process.download_and_decrypt(custom_headers=license_headers):
73
+ dash_process.finalize_output()
74
74
 
75
75
  # Get final output path and status
76
- status = r_proc.get_status()
76
+ status = dash_process.get_status()
77
77
 
78
78
  if status['error'] is not None and status['path']:
79
- try: os.remove(status['path'])
80
- except Exception: pass
79
+ try:
80
+ os.remove(status['path'])
81
+ except Exception:
82
+ pass
81
83
 
82
84
  return status['path'], status['stopped']
@@ -71,13 +71,13 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
71
71
  query_params = parse_qs(parsed_url.query)
72
72
 
73
73
  # Download the episode
74
- r_proc = DASH_Downloader(
74
+ dash_process = DASH_Downloader(
75
75
  cdm_device=get_wvd_path(),
76
76
  license_url='https://www.crunchyroll.com/license/v1/license/widevine',
77
77
  mpd_url=mpd_url,
78
78
  output_path=os.path.join(mp4_path, mp4_name),
79
79
  )
80
- r_proc.parse_manifest(custom_headers=mpd_headers)
80
+ dash_process.parse_manifest(custom_headers=mpd_headers)
81
81
 
82
82
  # Create headers for license request
83
83
  license_headers = mpd_headers.copy()
@@ -86,15 +86,17 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
86
86
  "x-cr-video-token": query_params['playbackGuid'][0],
87
87
  })
88
88
 
89
- if r_proc.download_and_decrypt(custom_headers=license_headers):
90
- r_proc.finalize_output()
89
+ if dash_process.download_and_decrypt(custom_headers=license_headers):
90
+ dash_process.finalize_output()
91
91
 
92
92
  # Get final output path and status
93
- status = r_proc.get_status()
93
+ status = dash_process.get_status()
94
94
 
95
95
  if status['error'] is not None and status['path']:
96
- try: os.remove(status['path'])
97
- except Exception: pass
96
+ try:
97
+ os.remove(status['path'])
98
+ except Exception:
99
+ pass
98
100
 
99
101
  # Delete episode stream
100
102
  delete_stream_episode(url_id, query_params['playbackGuid'][0], mpd_headers)
@@ -1,5 +1,8 @@
1
1
  # 16.03.25
2
2
 
3
+ import os
4
+ import sys
5
+
3
6
 
4
7
  # External libraries
5
8
  from curl_cffi import requests
@@ -8,6 +11,7 @@ from rich.console import Console
8
11
 
9
12
  # Internal utilities
10
13
  from StreamingCommunity.Util.config_json import config_manager
14
+ from StreamingCommunity.Util.os import get_wvd_path
11
15
  from StreamingCommunity.Util.headers import get_headers
12
16
  from StreamingCommunity.Util.table import TVShowManager
13
17
 
@@ -38,8 +42,19 @@ def title_search(query: str) -> int:
38
42
  media_search_manager.clear()
39
43
  table_show_manager.clear()
40
44
 
45
+ # Check CDM file before usage
46
+ cdm_device_path = get_wvd_path()
47
+ if not cdm_device_path or not isinstance(cdm_device_path, (str, bytes, os.PathLike)) or not os.path.isfile(cdm_device_path):
48
+ console.print(f"[bold red] CDM file not found or invalid path: {cdm_device_path}[/bold red]")
49
+ sys.exit(0)
50
+
51
+ # Check if x_cr_tab_id or etp_rt is present
52
+ if config_manager.get_dict("SITE_LOGIN", "crunchyroll")['x_cr_tab_id'] is None or config_manager.get_dict("SITE_LOGIN", "crunchyroll")['x_cr_tab_id'] == "" or config_manager.get_dict("SITE_LOGIN", "crunchyroll")['etp_rt'] is None or config_manager.get_dict("SITE_LOGIN", "crunchyroll")['etp_rt'] == "":
53
+ console.print(f"[bold red] x_cr_tab_id or etp_rt is missing or empty.[/bold red]")
54
+ sys.exit(0)
55
+
41
56
  # Build new Crunchyroll API search URL
42
- api_url = f"https://www.crunchyroll.com/content/v2/discover/search"
57
+ api_url = "https://www.crunchyroll.com/content/v2/discover/search"
43
58
 
44
59
  params = {
45
60
  "q": query,
@@ -12,6 +12,7 @@ from rich.prompt import Prompt
12
12
  from StreamingCommunity.Api.Template import get_select_title
13
13
  from StreamingCommunity.Api.Template.config_loader import site_constant
14
14
  from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
15
+ from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
15
16
 
16
17
 
17
18
  # Logic class
@@ -39,6 +40,14 @@ def process_search_result(select_title, selections=None):
39
40
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
40
41
  {'season': season_selection, 'episode': episode_selection}
41
42
  """
43
+ if not select_title:
44
+ if site_constant.TELEGRAM_BOT:
45
+ bot = get_bot_instance()
46
+ bot.send_message("No title selected or selection cancelled.", None)
47
+ else:
48
+ console.print("[yellow]No title selected or selection cancelled.")
49
+ return
50
+
42
51
  season_selection = None
43
52
  episode_selection = None
44
53
 
@@ -59,27 +68,44 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
59
68
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
60
69
  {'season': season_selection, 'episode': episode_selection}
61
70
  """
71
+ bot = None
72
+ if site_constant.TELEGRAM_BOT:
73
+ try:
74
+ from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
75
+ bot = get_bot_instance()
76
+ except Exception:
77
+ bot = None
78
+
62
79
  if direct_item:
63
80
  select_title = MediaItem(**direct_item)
64
81
  process_search_result(select_title, selections)
65
82
  return
66
83
 
67
84
  if string_to_search is None:
68
- string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{site_constant.SITE_NAME}").strip()
69
-
70
- # Search on database
71
- len_database = title_search(quote_plus(string_to_search))
85
+ actual_search_query = msg.ask(f"\n[purple]Insert word to search in [green]{site_constant.SITE_NAME}").strip()
86
+ else:
87
+ actual_search_query = string_to_search
88
+
89
+ # Handle empty input
90
+ if not actual_search_query:
91
+ if bot:
92
+ if actual_search_query is None:
93
+ bot.send_message("Search term not provided or operation cancelled. Returning.", None)
94
+ return
95
+
96
+ # Search on database (preserve quote_plus usage)
97
+ len_database = title_search(quote_plus(actual_search_query))
72
98
 
73
99
  # If only the database is needed, return the manager
74
100
  if get_onlyDatabase:
75
101
  return media_search_manager
76
102
 
77
103
  if len_database > 0:
78
- select_title = get_select_title(table_show_manager, media_search_manager,len_database)
104
+ select_title = get_select_title(table_show_manager, media_search_manager, len_database)
79
105
  process_search_result(select_title, selections)
80
-
81
106
  else:
82
-
83
- # If no results are found, ask again
84
- console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
85
- search()
107
+ if bot:
108
+ bot.send_message(f"No results found for: '{actual_search_query}'", None)
109
+ else:
110
+ console.print(f"\n[red]Nothing matching was found for[white]: [purple]{actual_search_query}")
111
+ return
@@ -55,7 +55,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scap
55
55
  # Get episode information
56
56
  obj_episode = scape_info_serie.selectEpisode(index_season_selected, index_episode_selected-1)
57
57
  index_season_selected = dynamic_format_number(str(index_season_selected))
58
- console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [bold magenta]{obj_episode.get('name')}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
58
+ console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{scape_info_serie.tv_name}[/cyan] \\ [bold magenta]{obj_episode.get('name')}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
59
59
 
60
60
  # Define filename and path for the downloaded video
61
61
  mp4_name = f"{map_episode_title(scape_info_serie.tv_name, index_season_selected, index_episode_selected, obj_episode.get('name'))}.mp4"
@@ -68,16 +68,18 @@ def download_video(index_season_selected: int, index_episode_selected: int, scap
68
68
  master_playlist = video_source.get_playlist()
69
69
 
70
70
  # Download the film using the m3u8 playlist, and output filename
71
- r_proc = HLS_Downloader(
71
+ hls_process = HLS_Downloader(
72
72
  m3u8_url=master_playlist,
73
73
  output_path=os.path.join(mp4_path, mp4_name)
74
74
  ).start()
75
75
 
76
- if r_proc['error'] is not None:
77
- try: os.remove(r_proc['path'])
78
- except: pass
76
+ if hls_process['error'] is not None:
77
+ try:
78
+ os.remove(hls_process['path'])
79
+ except Exception:
80
+ pass
79
81
 
80
- return r_proc['path'], r_proc['stopped']
82
+ return hls_process['path'], hls_process['stopped']
81
83
 
82
84
 
83
85
  def download_episode(scape_info_serie: GetSerieInfo, index_season_selected: int, download_all: bool = False, episode_selection: str = None) -> None:
@@ -1,8 +1,5 @@
1
1
  # 09.06.24
2
2
 
3
- import sys
4
-
5
-
6
3
  # External libraries
7
4
  import httpx
8
5
  from bs4 import BeautifulSoup
@@ -53,8 +53,7 @@ class GetSerieInfo:
53
53
  seasons_number = len(table_content.find_all("li"))
54
54
 
55
55
  # Try to get the title, with fallback
56
- title_element = soup.find("h1", class_="entry-title")
57
- self.tv_name = title_element.get_text(strip=True) if title_element else "Unknown Title"
56
+ self.tv_name = soup.find('h1', class_="front_title").get_text(strip=True) if soup.find('h1', class_="front_title") else "Unknown Series"
58
57
 
59
58
  return seasons_number
60
59
 
@@ -10,6 +10,7 @@ from rich.prompt import Prompt
10
10
  from StreamingCommunity.Api.Template import get_select_title
11
11
  from StreamingCommunity.Api.Template.config_loader import site_constant
12
12
  from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
13
+ from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
13
14
 
14
15
 
15
16
  # Logic class
@@ -22,7 +23,7 @@ from .film import download_film
22
23
  indice = 3
23
24
  _useFor = "Film_&_Serie"
24
25
  _priority = 0
25
- _engineDownload = "hls"
26
+ _engineDownload = "dash"
26
27
  _deprecate = False
27
28
 
28
29
  msg = Prompt()
@@ -47,6 +48,14 @@ def process_search_result(select_title, selections=None):
47
48
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
48
49
  {'season': season_selection, 'episode': episode_selection}
49
50
  """
51
+ if not select_title:
52
+ if site_constant.TELEGRAM_BOT:
53
+ bot = get_bot_instance()
54
+ bot.send_message("No title selected or selection cancelled.", None)
55
+ else:
56
+ console.print("[yellow]No title selected or selection cancelled.")
57
+ return
58
+
50
59
  if select_title.type == 'tv':
51
60
  season_selection = None
52
61
  episode_selection = None
@@ -71,26 +80,42 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
71
80
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
72
81
  {'season': season_selection, 'episode': episode_selection}
73
82
  """
83
+ bot = None
84
+ if site_constant.TELEGRAM_BOT:
85
+ bot = get_bot_instance()
86
+
74
87
  if direct_item:
75
88
  select_title = MediaItem(**direct_item)
76
89
  process_search_result(select_title, selections)
77
90
  return
78
-
79
- if string_to_search is None:
80
- string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
81
-
82
- # Search on database
83
- len_database = title_search(string_to_search)
91
+
92
+ # Get the user input for the search term
93
+ actual_search_query = get_user_input(string_to_search)
94
+
95
+ # Handle cases where user input is empty, or 'back' was handled (sys.exit or None return)
96
+ if not actual_search_query:
97
+ if bot:
98
+ if actual_search_query is None: # Specifically for timeout from bot.ask or failed restart
99
+ bot.send_message("Search term not provided or operation cancelled. Returning.", None)
100
+ return
101
+
102
+ # Perform the database search
103
+ len_database = title_search(actual_search_query)
84
104
 
85
105
  # If only the database is needed, return the manager
86
106
  if get_onlyDatabase:
87
107
  return media_search_manager
88
-
108
+
89
109
  if len_database > 0:
90
- select_title = get_select_title(table_show_manager, media_search_manager,len_database)
110
+ select_title = get_select_title(table_show_manager, media_search_manager, len_database)
91
111
  process_search_result(select_title, selections)
92
112
 
93
113
  else:
94
- # If no results are found, ask again
95
- console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
96
- search()
114
+ if bot:
115
+ bot.send_message(f"No results found for: '{actual_search_query}'", None)
116
+ else:
117
+ console.print(f"\n[red]Nothing matching was found for[white]: [purple]{actual_search_query}")
118
+
119
+ # Do not call search() recursively here to avoid infinite loops on no results.
120
+ # The flow should return to the caller (e.g., main menu in run.py).
121
+ return
@@ -49,37 +49,31 @@ def download_film(select_title: MediaItem) -> Tuple[str, bool]:
49
49
  # Generate mpd and license URLs
50
50
  bearer = get_bearer_token()
51
51
 
52
- # Extract ID from the episode URL
53
- episode_id = select_title.url.split('_')[-1]
54
- if "http" in episode_id:
55
- try: episode_id = select_title.url.split('/')[-1]
56
- except Exception:
57
- console.print(f"[red]Error:[/red] Failed to parse episode ID from URL: {select_title.url}")
58
- return None, True
59
-
60
- playback_json = get_playback_url(bearer, episode_id)
52
+ playback_json = get_playback_url(bearer, select_title.id)
61
53
  tracking_info = get_tracking_info(bearer, playback_json)[0]
62
54
 
63
55
  license_url = generate_license_url(bearer, tracking_info)
64
56
  mpd_url = get_manifest(tracking_info['video_src'])
65
57
 
66
58
  # Download the episode
67
- r_proc = DASH_Downloader(
59
+ dash_process = DASH_Downloader(
68
60
  cdm_device=get_wvd_path(),
69
61
  license_url=license_url,
70
62
  mpd_url=mpd_url,
71
63
  output_path=mp4_path,
72
64
  )
73
- r_proc.parse_manifest(custom_headers=get_headers())
65
+ dash_process.parse_manifest(custom_headers=get_headers())
74
66
 
75
- if r_proc.download_and_decrypt():
76
- r_proc.finalize_output()
67
+ if dash_process.download_and_decrypt():
68
+ dash_process.finalize_output()
77
69
 
78
70
  # Get final output path and status
79
- status = r_proc.get_status()
71
+ status = dash_process.get_status()
80
72
 
81
73
  if status['error'] is not None and status['path']:
82
- try: os.remove(status['path'])
83
- except Exception: pass
74
+ try:
75
+ os.remove(status['path'])
76
+ except Exception:
77
+ pass
84
78
 
85
79
  return status['path'], status['stopped']
@@ -56,7 +56,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
56
56
 
57
57
  # Get episode information
58
58
  obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
59
- console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [bold magenta]{obj_episode.name}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
59
+ 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
60
 
61
61
  # Define filename and path for the downloaded video
62
62
  mp4_name = f"{map_episode_title(scrape_serie.series_name, index_season_selected, index_episode_selected, obj_episode.name)}.mp4"
@@ -64,39 +64,33 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
64
64
 
65
65
  # Generate mpd and license URLs
66
66
  bearer = get_bearer_token()
67
-
68
- # Extract ID from the episode URL
69
- episode_id = obj_episode.url.split('_')[-1]
70
- if "http" in episode_id:
71
- try: episode_id = obj_episode.url.split('/')[-1]
72
- except Exception:
73
- console.print(f"[red]Error:[/red] Failed to parse episode ID from URL: {obj_episode.url}")
74
- return None, True
75
-
76
- playback_json = get_playback_url(bearer, episode_id)
67
+
68
+ playback_json = get_playback_url(bearer, obj_episode.id)
77
69
  tracking_info = get_tracking_info(bearer, playback_json)[0]
78
70
 
79
71
  license_url = generate_license_url(bearer, tracking_info)
80
72
  mpd_url = get_manifest(tracking_info['video_src'])
81
73
 
82
74
  # Download the episode
83
- r_proc = DASH_Downloader(
75
+ dash_process = DASH_Downloader(
84
76
  cdm_device=get_wvd_path(),
85
77
  license_url=license_url,
86
78
  mpd_url=mpd_url,
87
79
  output_path=os.path.join(mp4_path, mp4_name),
88
80
  )
89
- r_proc.parse_manifest(custom_headers=get_headers())
81
+ dash_process.parse_manifest(custom_headers=get_headers())
90
82
 
91
- if r_proc.download_and_decrypt():
92
- r_proc.finalize_output()
83
+ if dash_process.download_and_decrypt():
84
+ dash_process.finalize_output()
93
85
 
94
86
  # Get final output path and status
95
- status = r_proc.get_status()
87
+ status = dash_process.get_status()
96
88
 
97
89
  if status['error'] is not None and status['path']:
98
- try: os.remove(status['path'])
99
- except Exception: pass
90
+ try:
91
+ os.remove(status['path'])
92
+ except Exception:
93
+ pass
100
94
 
101
95
  return status['path'], status['stopped']
102
96
 
@@ -1,5 +1,9 @@
1
1
  # 25.07.25
2
2
 
3
+ import os
4
+ import sys
5
+
6
+
3
7
  # External libraries
4
8
  import httpx
5
9
  from rich.console import Console
@@ -7,6 +11,7 @@ from rich.console import Console
7
11
 
8
12
  # Internal utilities
9
13
  from StreamingCommunity.Util.config_json import config_manager
14
+ from StreamingCommunity.Util.os import get_wvd_path
10
15
  from StreamingCommunity.Util.headers import get_headers
11
16
  from StreamingCommunity.Util.table import TVShowManager
12
17
  from StreamingCommunity.Api.Template.config_loader import site_constant
@@ -37,7 +42,18 @@ def title_search(query: str) -> int:
37
42
  media_search_manager.clear()
38
43
  table_show_manager.clear()
39
44
 
40
- search_url = f'https://api-ott-prod-fe.mediaset.net/PROD/play/reco/anonymous/v2.0'
45
+ # Check CDM file before usage
46
+ cdm_device_path = get_wvd_path()
47
+ if not cdm_device_path or not isinstance(cdm_device_path, (str, bytes, os.PathLike)) or not os.path.isfile(cdm_device_path):
48
+ console.print(f"[bold red] CDM file not found or invalid path: {cdm_device_path}[/bold red]")
49
+ sys.exit(0)
50
+
51
+ # Check if beToken is present
52
+ if config_manager.get_dict("SITE_LOGIN", "mediasetinfinity")["beToken"] is None or config_manager.get_dict("SITE_LOGIN", "mediasetinfinity")["beToken"] == "":
53
+ console.print(f"[bold red] beToken is missing or empty.[/bold red]")
54
+ sys.exit(0)
55
+
56
+ search_url = 'https://api-ott-prod-fe.mediaset.net/PROD/play/reco/account/v2.0'
41
57
  console.print(f"[cyan]Search url: [yellow]{search_url}")
42
58
 
43
59
  params = {
@@ -48,11 +64,10 @@ def title_search(query: str) -> int:
48
64
  'contentId': '',
49
65
  'property': 'search',
50
66
  'tenant': 'play-prod-v2',
51
- 'userContext': 'iwiAeyJwbGF0Zm9ybSI6IndlYiJ9Aw==',
52
67
  'aresContext': '',
68
+ 'clientId': 'client_id',
53
69
  'page': '1',
54
70
  'hitsPerPage': '8',
55
- 'clientId': 'client_id'
56
71
  }
57
72
 
58
73
  headers = get_headers()