StreamingCommunity 3.3.0__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 (27) hide show
  1. StreamingCommunity/Api/Site/altadefinizione/__init__.py +37 -17
  2. StreamingCommunity/Api/Site/animeunity/__init__.py +36 -16
  3. StreamingCommunity/Api/Site/animeworld/__init__.py +50 -6
  4. StreamingCommunity/Api/Site/crunchyroll/__init__.py +42 -16
  5. StreamingCommunity/Api/Site/crunchyroll/site.py +1 -1
  6. StreamingCommunity/Api/Site/guardaserie/__init__.py +50 -6
  7. StreamingCommunity/Api/Site/mediasetinfinity/__init__.py +43 -5
  8. StreamingCommunity/Api/Site/mediasetinfinity/film.py +1 -1
  9. StreamingCommunity/Api/Site/mediasetinfinity/site.py +6 -3
  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 +45 -14
  13. StreamingCommunity/Api/Site/raiplay/series.py +9 -5
  14. StreamingCommunity/Api/Site/streamingcommunity/__init__.py +7 -2
  15. StreamingCommunity/Api/Site/streamingwatch/__init__.py +44 -14
  16. StreamingCommunity/Lib/Downloader/DASH/downloader.py +87 -55
  17. StreamingCommunity/Lib/Downloader/HLS/downloader.py +18 -6
  18. StreamingCommunity/Lib/Downloader/HLS/segments.py +1 -1
  19. StreamingCommunity/Lib/FFmpeg/command.py +66 -7
  20. StreamingCommunity/Lib/FFmpeg/util.py +16 -13
  21. StreamingCommunity/Upload/version.py +1 -1
  22. {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/METADATA +2 -11
  23. {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/RECORD +27 -27
  24. {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/WHEEL +0 -0
  25. {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/entry_points.txt +0 -0
  26. {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/licenses/LICENSE +0 -0
  27. {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,5 @@
1
1
  # 16.03.25
2
2
 
3
-
4
3
  import logging
5
4
  from urllib.parse import urlparse
6
5
 
@@ -16,10 +15,6 @@ from StreamingCommunity.Util.config_json import config_manager
16
15
  from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager
17
16
 
18
17
 
19
- # Logic class
20
- from .get_license import get_bearer_token
21
-
22
-
23
18
  # Variable
24
19
  max_timeout = config_manager.get_int("REQUESTS", "timeout")
25
20
 
@@ -47,7 +42,10 @@ class GetSerieInfo:
47
42
  return self.serie_id
48
43
 
49
44
  def _get_public_id(self):
50
- """Ottiene il public ID tramite l'API watchlist"""
45
+ self.public_id = "PR1GhC"
46
+ return self.public_id
47
+
48
+ """
51
49
  bearer_token = get_bearer_token()
52
50
  headers = {
53
51
  'authorization': f'Bearer {bearer_token}',
@@ -73,6 +71,7 @@ class GetSerieInfo:
73
71
  else:
74
72
  logging.error(f"Failed to get public ID: {response.status_code}")
75
73
  return None
74
+ """
76
75
 
77
76
  def _get_series_data(self):
78
77
  """Ottiene i dati della serie tramite l'API"""
@@ -290,4 +289,4 @@ class GetSerieInfo:
290
289
  logging.error(f"Episode index {episode_index} is out of range for season {season_number}")
291
290
  return None
292
291
 
293
- return episodes[episode_index]
292
+ return episodes[episode_index]
@@ -1,11 +1,15 @@
1
1
  # 16.03.25
2
2
 
3
+ import json
4
+ import time
3
5
  from urllib.parse import urlencode
4
6
  import xml.etree.ElementTree as ET
5
7
 
6
8
 
7
9
  # External library
8
10
  import httpx
11
+ from rich.console import Console
12
+ from seleniumbase import Driver
9
13
 
10
14
 
11
15
  # Internal utilities
@@ -14,9 +18,143 @@ from StreamingCommunity.Util.headers import get_headers, get_userAgent
14
18
 
15
19
 
16
20
  # Variable
21
+ console = Console()
17
22
  MAX_TIMEOUT = config_manager.get_int("REQUESTS", "timeout")
23
+ beToken = None
24
+ network_data = []
18
25
 
19
26
 
27
+ def save_network_data(data):
28
+ """Save network data and check for beToken"""
29
+ global network_data
30
+
31
+ # Filter only for login API responses
32
+ if data.get('method') == 'Network.responseReceived':
33
+ params = data.get('params', {})
34
+ response = params.get('response', {})
35
+ url = response.get('url', '')
36
+
37
+ if "persona/login/v2.0" in url and params.get('type') != 'Preflight':
38
+ network_data.append(data)
39
+ console.print(f"[green]Found login API request to: {url}")
40
+
41
+
42
+ def generate_betoken(username: str, password: str, sleep_action: float = 1.0) -> str:
43
+ driver = Driver(uc=True, uc_cdp_events=True, incognito=True, headless=True)
44
+
45
+ try:
46
+ console.print("[cyan]Launching browser...")
47
+ be_token_holder = {"token": None}
48
+ global network_data
49
+ network_data = [] # Reset network data
50
+
51
+ # Load home page
52
+ console.print("[cyan]Navigating to Mediaset Play...")
53
+ driver.uc_open_with_reconnect("https://www.mediasetplay.mediaset.it", sleep_action)
54
+
55
+ # Add CDP listeners after opening the page
56
+ driver.add_cdp_listener(
57
+ "Network.responseReceived",
58
+ lambda data: save_network_data(data)
59
+ )
60
+ driver.sleep(sleep_action)
61
+
62
+ # Accept privacy policy if present
63
+ try:
64
+ driver.click("#rti-privacy-accept-btn-screen1-id", timeout=3)
65
+ except Exception:
66
+ pass
67
+
68
+ # Click Login using the specific div structure
69
+ console.print("[cyan]Clicking login button...")
70
+ try:
71
+ driver.click('div.dwv_v span:contains("Login")', timeout=5)
72
+ except Exception as e:
73
+ print(f"Error clicking login: {e}")
74
+ try:
75
+ driver.click('div.dwv_v', timeout=3)
76
+ except Exception:
77
+ pass
78
+
79
+ driver.sleep(sleep_action)
80
+
81
+ # Click "Accedi con email e password" using the specific input
82
+ console.print("[cyan]Clicking email/password login...")
83
+ try:
84
+ driver.click('input.gigya-input-submit[value="Accedi con email e password"]', timeout=5)
85
+ except Exception as e:
86
+ print(f"Error clicking email login: {e}")
87
+ return None
88
+
89
+ driver.sleep(sleep_action)
90
+
91
+ # Fill login credentials using specific IDs
92
+ console.print("[cyan]Filling login credentials...")
93
+ try:
94
+ email_input = 'input[name="username"].gigya-input-text'
95
+ driver.wait_for_element(email_input, timeout=5)
96
+ driver.type(email_input, username)
97
+
98
+ password_input = 'input[name="password"].gigya-input-password'
99
+ driver.wait_for_element(password_input, timeout=5)
100
+ driver.type(password_input, password)
101
+
102
+ except Exception as e:
103
+ print(f"Error filling credentials: {e}")
104
+ return None
105
+
106
+ driver.sleep(sleep_action)
107
+
108
+ # Click Continue/Procedi using the submit button
109
+ console.print("[cyan]Clicking continue button...")
110
+ try:
111
+ driver.click('input.gigya-input-submit[type="submit"][value="Continua"]', timeout=5)
112
+ except Exception as e:
113
+ print(f"Error clicking continue: {e}")
114
+ return None
115
+
116
+ # Wait for login response and parse network data
117
+ console.print("[cyan]Waiting for login response...")
118
+ for attempt in range(30):
119
+ driver.sleep(0.3)
120
+
121
+ # Check network data for beToken - skip preflight requests
122
+ for data in network_data:
123
+ if data.get('method') == 'Network.responseReceived':
124
+ params = data.get('params', {})
125
+ response = params.get('response', {})
126
+ url = response.get('url', '')
127
+ request_type = params.get('type', '')
128
+
129
+ if "persona/login/v2.0" in url and request_type != 'Preflight':
130
+ request_id = params.get('requestId')
131
+
132
+ if request_id:
133
+ try:
134
+ response_body = driver.execute_cdp_cmd(
135
+ 'Network.getResponseBody',
136
+ {'requestId': request_id}
137
+ )
138
+ body = response_body.get('body', '')
139
+
140
+ if body:
141
+ response_data = json.loads(body)
142
+ be_token = response_data.get("response", {}).get("beToken")
143
+
144
+ if be_token:
145
+ be_token_holder["token"] = be_token
146
+ console.print("[green]Login successful! BeToken found!")
147
+ return be_token
148
+
149
+ except Exception:
150
+ continue
151
+
152
+ console.print(f"[yellow]Login completed. Total network events captured: {len(network_data)}")
153
+ return be_token_holder["token"]
154
+
155
+ finally:
156
+ driver.quit()
157
+
20
158
  def get_bearer_token():
21
159
  """
22
160
  Gets the BEARER_TOKEN for authentication.
@@ -24,6 +162,29 @@ def get_bearer_token():
24
162
  Returns:
25
163
  str: The bearer token string.
26
164
  """
165
+ global beToken
166
+
167
+ # Read beToken from config if already present
168
+ beToken = config_manager.get_dict("SITE_LOGIN", "mediasetinfinity")["beToken"]
169
+ if beToken is not None and len(beToken) != 0:
170
+ return beToken
171
+
172
+ username = config_manager.get_dict("SITE_LOGIN", "mediasetinfinity").get("username", "")
173
+ password = config_manager.get_dict("SITE_LOGIN", "mediasetinfinity").get("password", "")
174
+
175
+ if username and password:
176
+ beToken = generate_betoken(username, password)
177
+
178
+ if beToken is not None:
179
+
180
+ # Save current beToken
181
+ current_value = config_manager.get("SITE_LOGIN", "mediasetinfinity", dict)
182
+ current_value["beToken"] = beToken
183
+ config_manager.set_key("SITE_LOGIN", "mediasetinfinity", current_value)
184
+ config_manager.save_config()
185
+
186
+ return beToken
187
+
27
188
  return config_manager.get_dict("SITE_LOGIN", "mediasetinfinity")["beToken"]
28
189
 
29
190
  def get_playback_url(BEARER_TOKEN, CONTENT_ID):
@@ -158,6 +319,7 @@ def get_tracking_info(BEARER_TOKEN, PLAYBACK_JSON):
158
319
  response.raise_for_status()
159
320
 
160
321
  smil_xml = response.text
322
+ time.sleep(0.2)
161
323
  results = parse_smil_for_tracking_and_video(smil_xml)
162
324
  return results
163
325
 
@@ -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
@@ -33,8 +36,40 @@ console = Console()
33
36
  def get_user_input(string_to_search: str = None):
34
37
  """
35
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).
36
41
  """
37
- 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()
38
73
 
39
74
  def process_search_result(select_title, selections=None):
40
75
  """
@@ -44,6 +79,9 @@ def process_search_result(select_title, selections=None):
44
79
  select_title (MediaItem): The selected media item
45
80
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
46
81
  {'season': season_selection, 'episode': episode_selection}
82
+
83
+ Returns:
84
+ bool: True if processing was successful, False otherwise
47
85
  """
48
86
  if not select_title:
49
87
  if site_constant.TELEGRAM_BOT:
@@ -51,7 +89,7 @@ def process_search_result(select_title, selections=None):
51
89
  bot.send_message("No title selected or selection cancelled.", None)
52
90
  else:
53
91
  console.print("[yellow]No title selected or selection cancelled.")
54
- return
92
+ return False
55
93
 
56
94
  if select_title.type == 'tv':
57
95
  season_selection = None
@@ -62,24 +100,16 @@ def process_search_result(select_title, selections=None):
62
100
  episode_selection = selections.get('episode')
63
101
 
64
102
  download_series(select_title, season_selection, episode_selection)
103
+ return True
65
104
 
66
105
  else:
67
106
  download_film(select_title)
107
+ return True
68
108
 
69
109
  def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
70
110
  """
71
111
  Main function of the application for search.
72
112
 
73
- Parameters:
74
- string_to_search (str, optional): String to search for
75
- get_onlyDatabase (bool, optional): If True, return only the database object
76
- direct_item (dict, optional): Direct item to process (bypass search)
77
- selections (dict, optional): Dictionary containing selection inputs that bypass manual input
78
- {'season': season_selection, 'episode': episode_selection}
79
- """
80
- """
81
- Main function of the application for search.
82
-
83
113
  Parameters:
84
114
  string_to_search (str, optional): String to search for
85
115
  get_onlyDatabase (bool, optional): If True, return only the database object
@@ -94,7 +124,7 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
94
124
  if direct_item:
95
125
  select_title = MediaItem(**direct_item)
96
126
  process_search_result(select_title, selections)
97
- return
127
+ return True
98
128
 
99
129
  # Get the user input for the search term
100
130
  actual_search_query = get_user_input(string_to_search)
@@ -116,6 +146,7 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
116
146
  if len_database > 0:
117
147
  select_title = get_select_title(table_show_manager, media_search_manager, len_database)
118
148
  process_search_result(select_title, selections)
149
+ return True
119
150
 
120
151
  else:
121
152
  if bot:
@@ -125,4 +156,4 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
125
156
 
126
157
  # Do not call search() recursively here to avoid infinite loops on no results.
127
158
  # The flow should return to the caller (e.g., main menu in run.py).
128
- return
159
+ return
@@ -11,7 +11,7 @@ from rich.prompt import Prompt
11
11
 
12
12
 
13
13
  # Internal utilities
14
- from StreamingCommunity.Util.headers import get_headers
14
+ from StreamingCommunity.Util.headers import get_headers, get_userAgent
15
15
  from StreamingCommunity.Util.os import get_wvd_path
16
16
  from StreamingCommunity.Util.message import start_message
17
17
 
@@ -92,17 +92,21 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
92
92
  console.print(f"[bold red] CDM file not found or invalid path: {cdm_device_path}[/bold red]")
93
93
  sys.exit(0)
94
94
 
95
- 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
+ }
96
100
 
97
101
  dash_process = DASH_Downloader(
98
102
  cdm_device=cdm_device_path,
99
- license_url=license_url,
103
+ license_url=full_license_url.split("?")[0],
100
104
  mpd_url=master_playlist,
101
105
  output_path=os.path.join(mp4_path, mp4_name),
102
106
  )
103
107
  dash_process.parse_manifest(custom_headers=get_headers())
104
108
 
105
- if dash_process.download_and_decrypt():
109
+ if dash_process.download_and_decrypt(custom_headers=license_headers):
106
110
  dash_process.finalize_output()
107
111
 
108
112
  # Get final output path and status
@@ -202,4 +206,4 @@ def download_series(select_season: MediaItem, season_selection: str = None, epis
202
206
  if len(list_season_select) > 1 or index_season_selected == "*":
203
207
  download_episode(season_number, scrape_serie, download_all=True)
204
208
  else:
205
- 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)
@@ -78,6 +78,8 @@ def process_search_result(select_title, selections=None):
78
78
  select_title (MediaItem): The selected media item. Can be None if selection fails.
79
79
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
80
80
  e.g., {'season': season_selection, 'episode': episode_selection}
81
+ Returns:
82
+ bool: True if processing was successful, False otherwise
81
83
  """
82
84
  if not select_title:
83
85
  if site_constant.TELEGRAM_BOT:
@@ -85,7 +87,7 @@ def process_search_result(select_title, selections=None):
85
87
  bot.send_message("No title selected or selection cancelled.", None)
86
88
  else:
87
89
  console.print("[yellow]No title selected or selection cancelled.")
88
- return
90
+ return False
89
91
 
90
92
  if select_title.type == 'tv':
91
93
  season_selection = None
@@ -96,9 +98,11 @@ def process_search_result(select_title, selections=None):
96
98
  episode_selection = selections.get('episode')
97
99
 
98
100
  download_series(select_title, season_selection, episode_selection)
101
+ return True
99
102
 
100
103
  else:
101
104
  download_film(select_title)
105
+ return True
102
106
 
103
107
  def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
104
108
  """
@@ -119,7 +123,7 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
119
123
  if direct_item:
120
124
  select_title_obj = MediaItem(**direct_item)
121
125
  process_search_result(select_title_obj, selections)
122
- return
126
+ return True
123
127
 
124
128
  actual_search_query = get_user_input(string_to_search)
125
129
 
@@ -140,6 +144,7 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
140
144
  if len_database > 0:
141
145
  select_title = get_select_title(table_show_manager, media_search_manager, len_database)
142
146
  process_search_result(select_title, selections)
147
+ return True
143
148
 
144
149
  else:
145
150
  if bot:
@@ -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
@@ -33,9 +36,39 @@ def get_user_input(string_to_search: str = None):
33
36
  """
34
37
  Asks the user to input a search term.
35
38
  Handles both Telegram bot input and direct input.
39
+ If string_to_search is provided, it's returned directly (after stripping).
36
40
  """
37
- string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
38
- 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()
39
72
 
40
73
  def process_search_result(select_title, selections=None):
41
74
  """
@@ -45,6 +78,9 @@ def process_search_result(select_title, selections=None):
45
78
  select_title (MediaItem): The selected media item
46
79
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
47
80
  {'season': season_selection, 'episode': episode_selection}
81
+
82
+ Returns:
83
+ bool: True if processing was successful, False otherwise
48
84
  """
49
85
  if not select_title:
50
86
  if site_constant.TELEGRAM_BOT:
@@ -52,7 +88,8 @@ def process_search_result(select_title, selections=None):
52
88
  bot.send_message("No title selected or selection cancelled.", None)
53
89
  else:
54
90
  console.print("[yellow]No title selected or selection cancelled.")
55
- return
91
+ return False
92
+
56
93
  if select_title.type == 'tv':
57
94
  season_selection = None
58
95
  episode_selection = None
@@ -62,24 +99,16 @@ def process_search_result(select_title, selections=None):
62
99
  episode_selection = selections.get('episode')
63
100
 
64
101
  download_series(select_title, season_selection, episode_selection)
102
+ return True
65
103
 
66
104
  else:
67
105
  download_film(select_title)
106
+ return True
68
107
 
69
108
  def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
70
109
  """
71
110
  Main function of the application for search.
72
111
 
73
- Parameters:
74
- string_to_search (str, optional): String to search for
75
- get_onlyDatabase (bool, optional): If True, return only the database object
76
- direct_item (dict, optional): Direct item to process (bypass search)
77
- selections (dict, optional): Dictionary containing selection inputs that bypass manual input
78
- {'season': season_selection, 'episode': episode_selection}
79
- """
80
- """
81
- Main function of the application for search.
82
-
83
112
  Parameters:
84
113
  string_to_search (str, optional): String to search for
85
114
  get_onlyDatabase (bool, optional): If True, return only the database object
@@ -94,7 +123,7 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
94
123
  if direct_item:
95
124
  select_title = MediaItem(**direct_item)
96
125
  process_search_result(select_title, selections)
97
- return
126
+ return True
98
127
 
99
128
  # Get the user input for the search term
100
129
  actual_search_query = get_user_input(string_to_search)
@@ -116,6 +145,7 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
116
145
  if len_database > 0:
117
146
  select_title = get_select_title(table_show_manager, media_search_manager, len_database)
118
147
  process_search_result(select_title, selections)
148
+ return True
119
149
 
120
150
  else:
121
151
  if bot: