StreamingCommunity 3.2.9__py3-none-any.whl → 3.3.1__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 (38) hide show
  1. StreamingCommunity/Api/Site/altadefinizione/__init__.py +67 -30
  2. StreamingCommunity/Api/Site/animeunity/__init__.py +65 -29
  3. StreamingCommunity/Api/Site/animeworld/__init__.py +80 -10
  4. StreamingCommunity/Api/Site/crunchyroll/__init__.py +75 -15
  5. StreamingCommunity/Api/Site/crunchyroll/site.py +7 -1
  6. StreamingCommunity/Api/Site/guardaserie/__init__.py +80 -10
  7. StreamingCommunity/Api/Site/mediasetinfinity/__init__.py +78 -15
  8. StreamingCommunity/Api/Site/mediasetinfinity/film.py +1 -1
  9. StreamingCommunity/Api/Site/mediasetinfinity/site.py +12 -2
  10. StreamingCommunity/Api/Site/mediasetinfinity/util/ScrapeSerie.py +6 -7
  11. StreamingCommunity/Api/Site/mediasetinfinity/util/get_license.py +162 -0
  12. StreamingCommunity/Api/Site/raiplay/__init__.py +78 -12
  13. StreamingCommunity/Api/Site/raiplay/film.py +2 -1
  14. StreamingCommunity/Api/Site/raiplay/series.py +21 -7
  15. StreamingCommunity/Api/Site/streamingcommunity/__init__.py +12 -9
  16. StreamingCommunity/Api/Site/streamingcommunity/site.py +4 -1
  17. StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +5 -2
  18. StreamingCommunity/Api/Site/streamingwatch/__init__.py +76 -12
  19. StreamingCommunity/Lib/Downloader/DASH/cdm_helpher.py +8 -0
  20. StreamingCommunity/Lib/Downloader/DASH/downloader.py +109 -75
  21. StreamingCommunity/Lib/Downloader/HLS/downloader.py +18 -6
  22. StreamingCommunity/Lib/Downloader/HLS/segments.py +1 -1
  23. StreamingCommunity/Lib/Downloader/MP4/downloader.py +21 -3
  24. StreamingCommunity/Lib/FFmpeg/command.py +66 -7
  25. StreamingCommunity/Lib/FFmpeg/util.py +16 -13
  26. StreamingCommunity/Upload/update.py +2 -2
  27. StreamingCommunity/Upload/version.py +2 -2
  28. StreamingCommunity/Util/os.py +4 -1
  29. StreamingCommunity/run.py +4 -4
  30. {streamingcommunity-3.2.9.dist-info → streamingcommunity-3.3.1.dist-info}/METADATA +2 -7
  31. {streamingcommunity-3.2.9.dist-info → streamingcommunity-3.3.1.dist-info}/RECORD +35 -38
  32. StreamingCommunity/Api/Site/cb01new/__init__.py +0 -72
  33. StreamingCommunity/Api/Site/cb01new/film.py +0 -64
  34. StreamingCommunity/Api/Site/cb01new/site.py +0 -78
  35. {streamingcommunity-3.2.9.dist-info → streamingcommunity-3.3.1.dist-info}/WHEEL +0 -0
  36. {streamingcommunity-3.2.9.dist-info → streamingcommunity-3.3.1.dist-info}/entry_points.txt +0 -0
  37. {streamingcommunity-3.2.9.dist-info → streamingcommunity-3.3.1.dist-info}/licenses/LICENSE +0 -0
  38. {streamingcommunity-3.2.9.dist-info → streamingcommunity-3.3.1.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,8 @@
1
1
  # 21.05.24
2
2
 
3
+ import sys
4
+ import subprocess
5
+
3
6
 
4
7
  # External library
5
8
  from rich.console import Console
@@ -10,6 +13,7 @@ from rich.prompt import Prompt
10
13
  from StreamingCommunity.Api.Template import get_select_title
11
14
  from StreamingCommunity.Api.Template.config_loader import site_constant
12
15
  from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
16
+ from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
13
17
 
14
18
 
15
19
  # Logic class
@@ -32,8 +36,40 @@ console = Console()
32
36
  def get_user_input(string_to_search: str = None):
33
37
  """
34
38
  Asks the user to input a search term.
39
+ Handles both Telegram bot input and direct input.
40
+ If string_to_search is provided, it's returned directly (after stripping).
35
41
  """
36
- return msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
42
+ if string_to_search is not None:
43
+ return string_to_search.strip()
44
+
45
+ if site_constant.TELEGRAM_BOT:
46
+ bot = get_bot_instance()
47
+ user_response = bot.ask(
48
+ "key_search", # Request type
49
+ "Enter the search term\nor type 'back' to return to the menu: ",
50
+ None
51
+ )
52
+
53
+ if user_response is None:
54
+ bot.send_message("Timeout: No search term entered.", None)
55
+ return None
56
+
57
+ if user_response.lower() == 'back':
58
+ bot.send_message("Returning to the main menu...", None)
59
+
60
+ try:
61
+ # Restart the script
62
+ subprocess.Popen([sys.executable] + sys.argv)
63
+ sys.exit()
64
+
65
+ except Exception as e:
66
+ bot.send_message(f"Error during restart attempt: {e}", None)
67
+ return None # Return None if restart fails
68
+
69
+ return user_response.strip()
70
+
71
+ else:
72
+ return msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
37
73
 
38
74
  def process_search_result(select_title, selections=None):
39
75
  """
@@ -43,7 +79,18 @@ def process_search_result(select_title, selections=None):
43
79
  select_title (MediaItem): The selected media item
44
80
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
45
81
  {'season': season_selection, 'episode': episode_selection}
82
+
83
+ Returns:
84
+ bool: True if processing was successful, False otherwise
46
85
  """
86
+ if not select_title:
87
+ if site_constant.TELEGRAM_BOT:
88
+ bot = get_bot_instance()
89
+ bot.send_message("No title selected or selection cancelled.", None)
90
+ else:
91
+ console.print("[yellow]No title selected or selection cancelled.")
92
+ return False
93
+
47
94
  if select_title.type == 'tv':
48
95
  season_selection = None
49
96
  episode_selection = None
@@ -53,9 +100,11 @@ def process_search_result(select_title, selections=None):
53
100
  episode_selection = selections.get('episode')
54
101
 
55
102
  download_series(select_title, season_selection, episode_selection)
103
+ return True
56
104
 
57
105
  else:
58
106
  download_film(select_title)
107
+ return True
59
108
 
60
109
  def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
61
110
  """
@@ -68,26 +117,43 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
68
117
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
69
118
  {'season': season_selection, 'episode': episode_selection}
70
119
  """
120
+ bot = None
121
+ if site_constant.TELEGRAM_BOT:
122
+ bot = get_bot_instance()
123
+
71
124
  if direct_item:
72
125
  select_title = MediaItem(**direct_item)
73
126
  process_search_result(select_title, selections)
127
+ return True
128
+
129
+ # Get the user input for the search term
130
+ actual_search_query = get_user_input(string_to_search)
131
+
132
+ # Handle cases where user input is empty, or 'back' was handled (sys.exit or None return)
133
+ if not actual_search_query:
134
+ if bot:
135
+ if actual_search_query is None: # Specifically for timeout from bot.ask or failed restart
136
+ bot.send_message("Search term not provided or operation cancelled. Returning.", None)
74
137
  return
75
-
76
- if string_to_search is None:
77
- string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
78
-
79
- # Search on database
80
- len_database = title_search(string_to_search)
138
+
139
+ # Perform the database search
140
+ len_database = title_search(actual_search_query)
81
141
 
82
142
  # If only the database is needed, return the manager
83
143
  if get_onlyDatabase:
84
144
  return media_search_manager
85
-
145
+
86
146
  if len_database > 0:
87
- select_title = get_select_title(table_show_manager, media_search_manager,len_database)
147
+ select_title = get_select_title(table_show_manager, media_search_manager, len_database)
88
148
  process_search_result(select_title, selections)
149
+ return True
89
150
 
90
151
  else:
91
- # If no results are found, ask again
92
- console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
93
- search()
152
+ if bot:
153
+ bot.send_message(f"No results found for: '{actual_search_query}'", None)
154
+ else:
155
+ console.print(f"\n[red]Nothing matching was found for[white]: [purple]{actual_search_query}")
156
+
157
+ # Do not call search() recursively here to avoid infinite loops on no results.
158
+ # The flow should return to the caller (e.g., main menu in run.py).
159
+ return
@@ -1,6 +1,7 @@
1
1
  # 21.05.24
2
2
 
3
3
  import os
4
+ import sys
4
5
  from typing import Tuple
5
6
 
6
7
 
@@ -68,7 +69,7 @@ def download_film(select_title: MediaItem) -> Tuple[str, bool]:
68
69
  cdm_device_path = get_wvd_path()
69
70
  if not cdm_device_path or not isinstance(cdm_device_path, (str, bytes, os.PathLike)) or not os.path.isfile(cdm_device_path):
70
71
  console.print(f"[bold red] CDM file not found or invalid path: {cdm_device_path}[/bold red]")
71
- return None
72
+ sys.exit(0)
72
73
 
73
74
  license_url = generate_license_url(select_title.mpd_id)
74
75
 
@@ -1,6 +1,7 @@
1
1
  # 21.05.24
2
2
 
3
3
  import os
4
+ import sys
4
5
  from typing import Tuple
5
6
 
6
7
 
@@ -10,7 +11,7 @@ from rich.prompt import Prompt
10
11
 
11
12
 
12
13
  # Internal utilities
13
- from StreamingCommunity.Util.headers import get_headers
14
+ from StreamingCommunity.Util.headers import get_headers, get_userAgent
14
15
  from StreamingCommunity.Util.os import get_wvd_path
15
16
  from StreamingCommunity.Util.message import start_message
16
17
 
@@ -57,7 +58,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
57
58
 
58
59
  # Get episode information
59
60
  obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
60
- 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")
61
+ 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")
61
62
 
62
63
  # Define filename and path
63
64
  mp4_name = f"{map_episode_title(scrape_serie.series_name, index_season_selected, index_episode_selected, obj_episode.name)}.mp4"
@@ -66,6 +67,15 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
66
67
  # Get streaming URL
67
68
  master_playlist = VideoSource.extract_m3u8_url(obj_episode.url)
68
69
 
70
+ # HLS
71
+ if ".mpd" not in master_playlist:
72
+ r_proc = HLS_Downloader(
73
+ m3u8_url=master_playlist,
74
+ output_path=os.path.join(mp4_path, mp4_name)
75
+ ).start()
76
+ # Get streaming URL
77
+ master_playlist = VideoSource.extract_m3u8_url(obj_episode.url)
78
+
69
79
  # HLS
70
80
  if ".mpd" not in master_playlist:
71
81
  r_proc = HLS_Downloader(
@@ -80,19 +90,23 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
80
90
  cdm_device_path = get_wvd_path()
81
91
  if not cdm_device_path or not isinstance(cdm_device_path, (str, bytes, os.PathLike)) or not os.path.isfile(cdm_device_path):
82
92
  console.print(f"[bold red] CDM file not found or invalid path: {cdm_device_path}[/bold red]")
83
- return None
93
+ sys.exit(0)
84
94
 
85
- license_url = generate_license_url(obj_episode.mpd_id)
95
+ full_license_url = generate_license_url(obj_episode.mpd_id)
96
+ license_headers = {
97
+ 'nv-authorizations': full_license_url.split("?")[1].split("=")[1],
98
+ 'user-agent': get_userAgent(),
99
+ }
86
100
 
87
101
  dash_process = DASH_Downloader(
88
102
  cdm_device=cdm_device_path,
89
- license_url=license_url,
103
+ license_url=full_license_url.split("?")[0],
90
104
  mpd_url=master_playlist,
91
105
  output_path=os.path.join(mp4_path, mp4_name),
92
106
  )
93
107
  dash_process.parse_manifest(custom_headers=get_headers())
94
108
 
95
- if dash_process.download_and_decrypt():
109
+ if dash_process.download_and_decrypt(custom_headers=license_headers):
96
110
  dash_process.finalize_output()
97
111
 
98
112
  # Get final output path and status
@@ -192,4 +206,4 @@ def download_series(select_season: MediaItem, season_selection: str = None, epis
192
206
  if len(list_season_select) > 1 or index_season_selected == "*":
193
207
  download_episode(season_number, scrape_serie, download_all=True)
194
208
  else:
195
- download_episode(season_number, scrape_serie, download_all=False, episode_selection=episode_selection)
209
+ download_episode(season_number, scrape_serie, download_all=False, episode_selection=episode_selection)
@@ -15,7 +15,6 @@ from StreamingCommunity.Api.Template.config_loader import site_constant
15
15
  from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
16
16
  from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
17
17
 
18
-
19
18
  # Logic class
20
19
  from .site import title_search, table_show_manager, media_search_manager
21
20
  from .film import download_film
@@ -24,7 +23,7 @@ from .series import download_series
24
23
 
25
24
  # Variable
26
25
  indice = 0
27
- _useFor = "Film_&_Serie" # "Movies_&_Series"
26
+ _useFor = "Film_&_Serie"
28
27
  _priority = 0
29
28
  _engineDownload = "hls"
30
29
  _deprecate = False
@@ -79,6 +78,8 @@ def process_search_result(select_title, selections=None):
79
78
  select_title (MediaItem): The selected media item. Can be None if selection fails.
80
79
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
81
80
  e.g., {'season': season_selection, 'episode': episode_selection}
81
+ Returns:
82
+ bool: True if processing was successful, False otherwise
82
83
  """
83
84
  if not select_title:
84
85
  if site_constant.TELEGRAM_BOT:
@@ -86,7 +87,7 @@ def process_search_result(select_title, selections=None):
86
87
  bot.send_message("No title selected or selection cancelled.", None)
87
88
  else:
88
89
  console.print("[yellow]No title selected or selection cancelled.")
89
- return
90
+ return False
90
91
 
91
92
  if select_title.type == 'tv':
92
93
  season_selection = None
@@ -97,9 +98,11 @@ def process_search_result(select_title, selections=None):
97
98
  episode_selection = selections.get('episode')
98
99
 
99
100
  download_series(select_title, season_selection, episode_selection)
101
+ return True
100
102
 
101
103
  else:
102
104
  download_film(select_title)
105
+ return True
103
106
 
104
107
  def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
105
108
  """
@@ -120,14 +123,14 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
120
123
  if direct_item:
121
124
  select_title_obj = MediaItem(**direct_item)
122
125
  process_search_result(select_title_obj, selections)
123
- return
126
+ return True
124
127
 
125
128
  actual_search_query = get_user_input(string_to_search)
126
129
 
127
130
  # Handle cases where user input is empty, or 'back' was handled (sys.exit or None return)
128
- if not actual_search_query:
131
+ if not actual_search_query:
129
132
  if bot:
130
- if actual_search_query is None: # Specifically for timeout from bot.ask or failed restart
133
+ if actual_search_query is None: # Specifically for timeout from bot.ask or failed restart
131
134
  bot.send_message("Search term not provided or operation cancelled. Returning.", None)
132
135
  return
133
136
 
@@ -136,16 +139,16 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
136
139
 
137
140
  # If only the database object (media_search_manager populated by title_search) is needed
138
141
  if get_onlyDatabase:
139
- return media_search_manager
142
+ return media_search_manager
140
143
 
141
144
  if len_database > 0:
142
145
  select_title = get_select_title(table_show_manager, media_search_manager, len_database)
143
146
  process_search_result(select_title, selections)
147
+ return True
144
148
 
145
149
  else:
146
- no_results_message = f"No results found for: '{actual_search_query}'"
147
150
  if bot:
148
- bot.send_message(no_results_message, None)
151
+ bot.send_message(f"No results found for: '{actual_search_query}'", None)
149
152
  else:
150
153
  console.print(f"\n[red]Nothing matching was found for[white]: [purple]{actual_search_query}")
151
154
 
@@ -26,6 +26,7 @@ console = Console()
26
26
  media_search_manager = MediaManager()
27
27
  table_show_manager = TVShowManager()
28
28
  max_timeout = config_manager.get_int("REQUESTS", "timeout")
29
+ ssl_verify = config_manager.get_bool("REQUESTS", "verify")
29
30
 
30
31
 
31
32
  def title_search(query: str) -> int:
@@ -49,6 +50,7 @@ def title_search(query: str) -> int:
49
50
  f"{site_constant.FULL_URL}/it",
50
51
  headers={'user-agent': get_userAgent()},
51
52
  timeout=max_timeout,
53
+ verify=ssl_verify,
52
54
  follow_redirects=True
53
55
  )
54
56
  response.raise_for_status()
@@ -75,7 +77,8 @@ def title_search(query: str) -> int:
75
77
  'x-inertia': 'true',
76
78
  'x-inertia-version': version
77
79
  },
78
- timeout=max_timeout
80
+ timeout=max_timeout,
81
+ verify=ssl_verify
79
82
  )
80
83
  response.raise_for_status()
81
84
 
@@ -17,6 +17,7 @@ from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager
17
17
 
18
18
  # Variable
19
19
  max_timeout = config_manager.get_int("REQUESTS", "timeout")
20
+ ssl_verify = config_manager.get_bool("REQUESTS", "verify")
20
21
 
21
22
 
22
23
  class GetSerieInfo:
@@ -50,7 +51,8 @@ class GetSerieInfo:
50
51
  response = httpx.get(
51
52
  url=f"{self.url}/titles/{self.media_id}-{self.series_name}",
52
53
  headers=self.headers,
53
- timeout=max_timeout
54
+ timeout=max_timeout,
55
+ verify=ssl_verify
54
56
  )
55
57
  response.raise_for_status()
56
58
 
@@ -104,7 +106,8 @@ class GetSerieInfo:
104
106
  'x-inertia': 'true',
105
107
  'x-inertia-version': self.version,
106
108
  },
107
- timeout=max_timeout
109
+ timeout=max_timeout,
110
+ verify=ssl_verify
108
111
  )
109
112
 
110
113
  # Extract episodes from JSON response
@@ -1,5 +1,8 @@
1
1
  # 29.04.25
2
2
 
3
+ import sys
4
+ import subprocess
5
+
3
6
  # External library
4
7
  from rich.console import Console
5
8
  from rich.prompt import Prompt
@@ -9,6 +12,7 @@ from rich.prompt import Prompt
9
12
  from StreamingCommunity.Api.Template import get_select_title
10
13
  from StreamingCommunity.Api.Template.config_loader import site_constant
11
14
  from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
15
+ from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
12
16
 
13
17
 
14
18
  # Logic class
@@ -32,9 +36,39 @@ def get_user_input(string_to_search: str = None):
32
36
  """
33
37
  Asks the user to input a search term.
34
38
  Handles both Telegram bot input and direct input.
39
+ If string_to_search is provided, it's returned directly (after stripping).
35
40
  """
36
- string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
37
- return string_to_search
41
+ if string_to_search is not None:
42
+ return string_to_search.strip()
43
+
44
+ if site_constant.TELEGRAM_BOT:
45
+ bot = get_bot_instance()
46
+ user_response = bot.ask(
47
+ "key_search", # Request type
48
+ "Enter the search term\nor type 'back' to return to the menu: ",
49
+ None
50
+ )
51
+
52
+ if user_response is None:
53
+ bot.send_message("Timeout: No search term entered.", None)
54
+ return None
55
+
56
+ if user_response.lower() == 'back':
57
+ bot.send_message("Returning to the main menu...", None)
58
+
59
+ try:
60
+ # Restart the script
61
+ subprocess.Popen([sys.executable] + sys.argv)
62
+ sys.exit()
63
+
64
+ except Exception as e:
65
+ bot.send_message(f"Error during restart attempt: {e}", None)
66
+ return None # Return None if restart fails
67
+
68
+ return user_response.strip()
69
+
70
+ else:
71
+ return msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
38
72
 
39
73
  def process_search_result(select_title, selections=None):
40
74
  """
@@ -44,7 +78,18 @@ def process_search_result(select_title, selections=None):
44
78
  select_title (MediaItem): The selected media item
45
79
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
46
80
  {'season': season_selection, 'episode': episode_selection}
81
+
82
+ Returns:
83
+ bool: True if processing was successful, False otherwise
47
84
  """
85
+ if not select_title:
86
+ if site_constant.TELEGRAM_BOT:
87
+ bot = get_bot_instance()
88
+ bot.send_message("No title selected or selection cancelled.", None)
89
+ else:
90
+ console.print("[yellow]No title selected or selection cancelled.")
91
+ return False
92
+
48
93
  if select_title.type == 'tv':
49
94
  season_selection = None
50
95
  episode_selection = None
@@ -54,9 +99,11 @@ def process_search_result(select_title, selections=None):
54
99
  episode_selection = selections.get('episode')
55
100
 
56
101
  download_series(select_title, season_selection, episode_selection)
102
+ return True
57
103
 
58
104
  else:
59
105
  download_film(select_title)
106
+ return True
60
107
 
61
108
  def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
62
109
  """
@@ -69,26 +116,43 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
69
116
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
70
117
  {'season': season_selection, 'episode': episode_selection}
71
118
  """
119
+ bot = None
120
+ if site_constant.TELEGRAM_BOT:
121
+ bot = get_bot_instance()
122
+
72
123
  if direct_item:
73
124
  select_title = MediaItem(**direct_item)
74
125
  process_search_result(select_title, selections)
75
- return
126
+ return True
76
127
 
77
- if string_to_search is None:
78
- string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
128
+ # Get the user input for the search term
129
+ actual_search_query = get_user_input(string_to_search)
130
+
131
+ # Handle cases where user input is empty, or 'back' was handled (sys.exit or None return)
132
+ if not actual_search_query:
133
+ if bot:
134
+ if actual_search_query is None: # Specifically for timeout from bot.ask or failed restart
135
+ bot.send_message("Search term not provided or operation cancelled. Returning.", None)
136
+ return
79
137
 
80
- # Perform search on the database using the obtained query
81
- len_database = title_search(string_to_search)
138
+ # Perform the database search
139
+ len_database = title_search(actual_search_query)
82
140
 
83
141
  # If only the database is needed, return the manager
84
142
  if get_onlyDatabase:
85
143
  return media_search_manager
86
-
144
+
87
145
  if len_database > 0:
88
- select_title = get_select_title(table_show_manager, media_search_manager,len_database)
146
+ select_title = get_select_title(table_show_manager, media_search_manager, len_database)
89
147
  process_search_result(select_title, selections)
148
+ return True
90
149
 
91
150
  else:
92
- # If no results are found, ask again
93
- console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
94
- search()
151
+ if bot:
152
+ bot.send_message(f"No results found for: '{actual_search_query}'", None)
153
+ else:
154
+ console.print(f"\n[red]Nothing matching was found for[white]: [purple]{actual_search_query}")
155
+
156
+ # Do not call search() recursively here to avoid infinite loops on no results.
157
+ # The flow should return to the caller (e.g., main menu in run.py).
158
+ return
@@ -62,6 +62,14 @@ def get_widevine_keys(pssh, license_url, cdm_device_path, headers=None, payload=
62
62
 
63
63
  if response.status_code != 200:
64
64
  console.print(f"[bold red]License error:[/bold red] {response.status_code}, {response.text}")
65
+ console.print({
66
+ "url": license_url,
67
+ "headers": req_headers,
68
+ "content": payload,
69
+ "session_id": session_id.hex(),
70
+ "pssh": pssh
71
+ })
72
+
65
73
  return None
66
74
 
67
75
  # Handle (JSON) or classic (binary) license response