StreamingCommunity 2.5.6__py3-none-any.whl → 2.5.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 (65) hide show
  1. StreamingCommunity/Api/Player/ddl.py +2 -3
  2. StreamingCommunity/Api/Site/1337xx/__init__.py +5 -6
  3. StreamingCommunity/Api/Site/1337xx/site.py +7 -14
  4. StreamingCommunity/Api/Site/1337xx/title.py +3 -5
  5. StreamingCommunity/Api/Site/altadefinizionegratis/__init__.py +7 -6
  6. StreamingCommunity/Api/Site/altadefinizionegratis/film.py +14 -19
  7. StreamingCommunity/Api/Site/altadefinizionegratis/site.py +6 -14
  8. StreamingCommunity/Api/Site/animeunity/__init__.py +7 -7
  9. StreamingCommunity/Api/Site/animeunity/film_serie.py +29 -31
  10. StreamingCommunity/Api/Site/animeunity/site.py +14 -22
  11. StreamingCommunity/Api/Site/cb01new/__init__.py +5 -4
  12. StreamingCommunity/Api/Site/cb01new/film.py +2 -5
  13. StreamingCommunity/Api/Site/cb01new/site.py +5 -13
  14. StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +5 -4
  15. StreamingCommunity/Api/Site/ddlstreamitaly/series.py +12 -49
  16. StreamingCommunity/Api/Site/ddlstreamitaly/site.py +6 -16
  17. StreamingCommunity/Api/Site/ddlstreamitaly/util/ScrapeSerie.py +2 -3
  18. StreamingCommunity/Api/Site/guardaserie/__init__.py +5 -4
  19. StreamingCommunity/Api/Site/guardaserie/series.py +12 -46
  20. StreamingCommunity/Api/Site/guardaserie/site.py +5 -13
  21. StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +10 -14
  22. StreamingCommunity/Api/Site/ilcorsaronero/__init__.py +5 -4
  23. StreamingCommunity/Api/Site/ilcorsaronero/site.py +5 -13
  24. StreamingCommunity/Api/Site/ilcorsaronero/title.py +3 -5
  25. StreamingCommunity/Api/Site/mostraguarda/__init__.py +2 -2
  26. StreamingCommunity/Api/Site/mostraguarda/film.py +4 -8
  27. StreamingCommunity/Api/Site/streamingcommunity/__init__.py +8 -7
  28. StreamingCommunity/Api/Site/streamingcommunity/film.py +14 -18
  29. StreamingCommunity/Api/Site/streamingcommunity/series.py +25 -76
  30. StreamingCommunity/Api/Site/streamingcommunity/site.py +11 -23
  31. StreamingCommunity/Api/Template/Util/__init__.py +8 -1
  32. StreamingCommunity/Api/Template/Util/manage_ep.py +46 -2
  33. StreamingCommunity/Api/Template/config_loader.py +71 -0
  34. StreamingCommunity/Lib/Downloader/HLS/downloader.py +60 -60
  35. StreamingCommunity/Lib/Downloader/HLS/segments.py +40 -15
  36. StreamingCommunity/Lib/Downloader/MP4/downloader.py +47 -40
  37. StreamingCommunity/Lib/FFmpeg/command.py +59 -3
  38. StreamingCommunity/Lib/M3U8/estimator.py +10 -12
  39. StreamingCommunity/Lib/M3U8/parser.py +12 -51
  40. StreamingCommunity/Lib/TMBD/tmdb.py +66 -99
  41. StreamingCommunity/TelegramHelp/telegram_bot.py +222 -68
  42. StreamingCommunity/Util/_jsonConfig.py +14 -13
  43. StreamingCommunity/Util/ffmpeg_installer.py +70 -64
  44. StreamingCommunity/Util/headers.py +11 -122
  45. StreamingCommunity/Util/os.py +65 -56
  46. StreamingCommunity/Util/table.py +62 -108
  47. StreamingCommunity/run.py +16 -11
  48. {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/METADATA +57 -23
  49. StreamingCommunity-2.5.8.dist-info/RECORD +86 -0
  50. StreamingCommunity/Api/Site/1337xx/costant.py +0 -15
  51. StreamingCommunity/Api/Site/altadefinizionegratis/costant.py +0 -21
  52. StreamingCommunity/Api/Site/animeunity/costant.py +0 -21
  53. StreamingCommunity/Api/Site/cb01new/costant.py +0 -19
  54. StreamingCommunity/Api/Site/ddlstreamitaly/costant.py +0 -20
  55. StreamingCommunity/Api/Site/guardaserie/costant.py +0 -19
  56. StreamingCommunity/Api/Site/ilcorsaronero/costant.py +0 -19
  57. StreamingCommunity/Api/Site/mostraguarda/costant.py +0 -19
  58. StreamingCommunity/Api/Site/streamingcommunity/costant.py +0 -21
  59. StreamingCommunity/TelegramHelp/request_manager.py +0 -82
  60. StreamingCommunity/TelegramHelp/session.py +0 -56
  61. StreamingCommunity-2.5.6.dist-info/RECORD +0 -96
  62. {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/LICENSE +0 -0
  63. {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/WHEEL +0 -0
  64. {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/entry_points.txt +0 -0
  65. {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/top_level.txt +0 -0
@@ -13,6 +13,7 @@ import subprocess
13
13
  import contextlib
14
14
  import urllib.request
15
15
  import importlib.metadata
16
+ from pathlib import Path
16
17
 
17
18
 
18
19
  # External library
@@ -67,7 +68,7 @@ class OsManager:
67
68
 
68
69
  # Convert Windows separators to Unix
69
70
  normalized = path.replace('\\', '/')
70
-
71
+
71
72
  # Ensure absolute paths start with /
72
73
  if normalized.startswith('/'):
73
74
  return os.path.normpath(normalized)
@@ -82,17 +83,17 @@ class OsManager:
82
83
  # Decode and sanitize
83
84
  decoded = unidecode(filename)
84
85
  sanitized = sanitize_filename(decoded)
85
-
86
+
86
87
  # Split name and extension
87
88
  name, ext = os.path.splitext(sanitized)
88
-
89
+
89
90
  # Calculate available length for name considering the '...' and extension
90
91
  max_name_length = self.max_length - len('...') - len(ext)
91
-
92
+
92
93
  # Truncate name if it exceeds the max name length
93
94
  if len(name) > max_name_length:
94
95
  name = name[:max_name_length] + '...'
95
-
96
+
96
97
  # Ensure the final file name includes the extension
97
98
  return name + ext
98
99
 
@@ -103,7 +104,7 @@ class OsManager:
103
104
 
104
105
  # Decode unicode characters
105
106
  decoded = unidecode(path)
106
-
107
+
107
108
  # Basic path sanitization
108
109
  sanitized = sanitize_filepath(decoded)
109
110
 
@@ -121,7 +122,7 @@ class OsManager:
121
122
  if part
122
123
  ])
123
124
  return '\\'.join(sanitized_parts)
124
-
125
+
125
126
  # Handle drive letters
126
127
  elif len(path) >= 2 and path[1] == ':':
127
128
  drive = path[:2]
@@ -132,7 +133,7 @@ class OsManager:
132
133
  if part
133
134
  ]
134
135
  return '\\'.join(path_parts)
135
-
136
+
136
137
  # Regular path
137
138
  else:
138
139
  parts = path.replace('/', '\\').split('\\')
@@ -146,21 +147,21 @@ class OsManager:
146
147
  for part in parts
147
148
  if part
148
149
  ]
149
-
150
+
150
151
  result = '/'.join(sanitized_parts)
151
152
  if is_absolute:
152
153
  result = '/' + result
153
-
154
+
154
155
  return result
155
-
156
+
156
157
  def create_path(self, path: str, mode: int = 0o755) -> bool:
157
158
  """
158
159
  Create directory path with specified permissions.
159
-
160
+
160
161
  Args:
161
162
  path (str): Path to create.
162
163
  mode (int, optional): Directory permissions. Defaults to 0o755.
163
-
164
+
164
165
  Returns:
165
166
  bool: True if path created successfully, False otherwise.
166
167
  """
@@ -168,7 +169,7 @@ class OsManager:
168
169
  sanitized_path = self.get_sanitize_path(path)
169
170
  os.makedirs(sanitized_path, mode=mode, exist_ok=True)
170
171
  return True
171
-
172
+
172
173
  except Exception as e:
173
174
  logging.error(f"Path creation error: {e}")
174
175
  return False
@@ -176,21 +177,21 @@ class OsManager:
176
177
  def remove_folder(self, folder_path: str) -> bool:
177
178
  """
178
179
  Safely remove a folder.
179
-
180
+
180
181
  Args:
181
182
  folder_path (str): Path of directory to remove.
182
-
183
+
183
184
  Returns:
184
185
  bool: Removal status.
185
186
  """
186
187
  try:
187
188
  shutil.rmtree(folder_path)
188
189
  return True
189
-
190
+
190
191
  except OSError as e:
191
192
  logging.error(f"Folder removal error: {e}")
192
193
  return False
193
-
194
+
194
195
  def remove_files_except_one(self, folder_path: str, keep_file: str) -> None:
195
196
  """
196
197
  Delete all files in a folder except for one specified file.
@@ -201,21 +202,27 @@ class OsManager:
201
202
  """
202
203
 
203
204
  try:
204
- # List all files in the folder
205
- files_in_folder = os.listdir(folder_path)
205
+ # First, try to make all files writable
206
+ for root, dirs, files in os.walk(self.temp_dir):
207
+ for dir_name in dirs:
208
+ dir_path = os.path.join(root, dir_name)
209
+ os.chmod(dir_path, 0o755) # rwxr-xr-x
210
+ for file_name in files:
211
+ file_path = os.path.join(root, file_name)
212
+ os.chmod(file_path, 0o644) # rw-r--r--
206
213
 
207
- # Iterate over each file in the folder
208
- for file_name in files_in_folder:
209
- file_path = os.path.join(folder_path, file_name)
214
+ # Then remove the directory tree
215
+ shutil.rmtree(self.temp_dir, ignore_errors=True)
216
+
217
+ # If directory still exists after rmtree, try force remove
218
+ if os.path.exists(self.temp_dir):
219
+ import subprocess
220
+ subprocess.run(['rm', '-rf', self.temp_dir], check=True)
210
221
 
211
- # Check if the file is not the one to keep and is a regular file
212
- if file_name != keep_file and os.path.isfile(file_path):
213
- os.remove(file_path) # Delete the file
214
-
215
222
  except Exception as e:
216
- logging.error(f"An error occurred: {e}")
217
- raise
218
-
223
+ logging.error(f"Failed to cleanup temporary directory: {str(e)}")
224
+ pass
225
+
219
226
  def check_file(self, file_path: str) -> bool:
220
227
  """
221
228
  Check if a file exists at the given file path.
@@ -229,7 +236,7 @@ class OsManager:
229
236
  try:
230
237
  logging.info(f"Check if file exists: {file_path}")
231
238
  return os.path.exists(file_path)
232
-
239
+
233
240
  except Exception as e:
234
241
  logging.error(f"An error occurred while checking file existence: {e}")
235
242
  return False
@@ -280,12 +287,12 @@ class InternManager():
280
287
  def check_internet():
281
288
  while True:
282
289
  try:
283
- httpx.get("https://www.google.com", timeout=15)
290
+ httpx.get("https://www.google.com", timeout=5)
284
291
  break
285
292
 
286
- except urllib.error.URLError:
293
+ except Exception as e:
287
294
  console.log("[bold red]Internet is not available. Waiting...[/bold red]")
288
- time.sleep(5)
295
+ time.sleep(2)
289
296
 
290
297
 
291
298
  class OsSummary:
@@ -299,7 +306,7 @@ class OsSummary:
299
306
  """Get the binary directory based on OS."""
300
307
  system = platform.system().lower()
301
308
  home = os.path.expanduser('~')
302
-
309
+
303
310
  if system == 'windows':
304
311
  return os.path.join(os.path.splitdrive(home)[0] + os.path.sep, 'binary')
305
312
  elif system == 'darwin':
@@ -315,41 +322,41 @@ class OsSummary:
315
322
  try:
316
323
  result = subprocess.check_output(command, text=True).strip()
317
324
  return result.split('\n')[0] if result else None
318
-
325
+
319
326
  except subprocess.CalledProcessError:
320
327
  return None
321
328
 
322
329
  def get_library_version(self, lib_name: str):
323
330
  """
324
331
  Retrieve the version of a Python library.
325
-
332
+
326
333
  Args:
327
334
  lib_name (str): The name of the Python library.
328
-
335
+
329
336
  Returns:
330
337
  str: The library name followed by its version, or `-not installed` if not found.
331
338
  """
332
339
  try:
333
340
  version = importlib.metadata.version(lib_name)
334
341
  return f"{lib_name}-{version}"
335
-
342
+
336
343
  except importlib.metadata.PackageNotFoundError:
337
344
  return f"{lib_name}-not installed"
338
345
 
339
346
  def download_requirements(self, url: str, filename: str):
340
347
  """
341
348
  Download the requirements.txt file from the specified URL if not found locally using requests.
342
-
349
+
343
350
  Args:
344
351
  url (str): The URL to download the requirements file from.
345
352
  filename (str): The local filename to save the requirements file as.
346
353
  """
347
354
  try:
348
355
  import requests
349
-
356
+
350
357
  logging.info(f"{filename} not found locally. Downloading from {url}...")
351
358
  response = requests.get(url)
352
-
359
+
353
360
  if response.status_code == 200:
354
361
  with open(filename, 'wb') as f:
355
362
  f.write(response.content)
@@ -357,7 +364,7 @@ class OsSummary:
357
364
  else:
358
365
  logging.error(f"Failed to download {filename}. HTTP Status code: {response.status_code}")
359
366
  sys.exit(0)
360
-
367
+
361
368
  except Exception as e:
362
369
  logging.error(f"Failed to download {filename}: {e}")
363
370
  sys.exit(0)
@@ -373,7 +380,7 @@ class OsSummary:
373
380
  console.print(f"Installing {lib_name}...", style="bold yellow")
374
381
  subprocess.check_call([sys.executable, "-m", "pip", "install", lib_name])
375
382
  console.print(f"{lib_name} installed successfully!", style="bold green")
376
-
383
+
377
384
  except subprocess.CalledProcessError as e:
378
385
  console.print(f"Failed to install {lib_name}: {e}", style="bold red")
379
386
  sys.exit(1)
@@ -400,7 +407,7 @@ class OsSummary:
400
407
  arch = platform.machine()
401
408
  os_info = platform.platform()
402
409
  glibc_version = 'glibc ' + '.'.join(map(str, platform.libc_ver()[1]))
403
-
410
+
404
411
  console.print(f"[cyan]Python[white]: [bold red]{python_version} ({python_implementation} {arch}) - {os_info} ({glibc_version})[/bold red]")
405
412
  logging.info(f"Python: {python_version} ({python_implementation} {arch}) - {os_info} ({glibc_version})")
406
413
 
@@ -408,10 +415,10 @@ class OsSummary:
408
415
  binary_dir = self.get_binary_directory()
409
416
  system = platform.system().lower()
410
417
  arch = platform.machine().lower()
411
-
418
+
412
419
  # Map architecture names
413
420
  arch_map = {
414
- 'amd64': 'x64',
421
+ 'amd64': 'x64',
415
422
  'x86_64': 'x64',
416
423
  'x64': 'x64',
417
424
  'arm64': 'arm64',
@@ -424,15 +431,15 @@ class OsSummary:
424
431
 
425
432
  # Check binary directory
426
433
  if os.path.exists(binary_dir):
427
-
434
+
428
435
  # Search for any file containing 'ffmpeg' and the architecture
429
436
  ffmpeg_files = glob.glob(os.path.join(binary_dir, f'*ffmpeg*{arch}*'))
430
437
  ffprobe_files = glob.glob(os.path.join(binary_dir, f'*ffprobe*{arch}*'))
431
-
438
+
432
439
  if ffmpeg_files and ffprobe_files:
433
440
  self.ffmpeg_path = ffmpeg_files[0]
434
441
  self.ffprobe_path = ffprobe_files[0]
435
-
442
+
436
443
  # Set executable permissions if needed
437
444
  if system != 'windows':
438
445
  os.chmod(self.ffmpeg_path, 0o755)
@@ -451,24 +458,26 @@ class OsSummary:
451
458
  # Handle requirements.txt
452
459
  if not getattr(sys, 'frozen', False):
453
460
  requirements_file = 'requirements.txt'
454
-
461
+
462
+ requirements_file = Path(__file__).parent.parent.parent / requirements_file
463
+
455
464
  if not os.path.exists(requirements_file):
456
465
  self.download_requirements(
457
466
  'https://raw.githubusercontent.com/Arrowar/StreamingCommunity/refs/heads/main/requirements.txt',
458
467
  requirements_file
459
468
  )
460
-
469
+
461
470
  optional_libraries = [line.strip().split("=")[0] for line in open(requirements_file, 'r', encoding='utf-8-sig')]
462
-
471
+
463
472
  for lib in optional_libraries:
464
- installed_version = self.get_library_version(lib)
473
+ installed_version = self.get_library_version(lib.split("<")[0])
465
474
  if 'not installed' in installed_version:
466
475
  user_response = msg.ask(f"{lib} is not installed. Do you want to install it? (yes/no)", default="y")
467
476
  if user_response.lower().strip() in ["yes", "y"]:
468
477
  self.install_library(lib)
469
478
  else:
470
479
  logging.info(f"Library: {installed_version}")
471
-
480
+
472
481
  #console.print(f"[cyan]Libraries[white]: [bold red]{', '.join([self.get_library_version(lib) for lib in optional_libraries])}[/bold red]\n")
473
482
  logging.info(f"Libraries: {', '.join([self.get_library_version(lib) for lib in optional_libraries])}")
474
483
 
@@ -495,6 +504,6 @@ def compute_sha1_hash(input_string: str) -> str:
495
504
  """
496
505
  # Compute the SHA-1 hash
497
506
  hashed_string = hashlib.sha1(input_string.encode()).hexdigest()
498
-
507
+
499
508
  # Return the hashed string
500
509
  return hashed_string
@@ -4,6 +4,8 @@ import os
4
4
  import sys
5
5
  import logging
6
6
  import importlib
7
+ from pathlib import Path
8
+ from typing import Dict, List, Any
7
9
 
8
10
 
9
11
  # External library
@@ -11,7 +13,6 @@ from rich.console import Console
11
13
  from rich.table import Table
12
14
  from rich.prompt import Prompt
13
15
  from rich.style import Style
14
- from typing import Dict, List, Any
15
16
 
16
17
 
17
18
  # Internal utilities
@@ -24,13 +25,12 @@ from StreamingCommunity.Util._jsonConfig import config_manager
24
25
  TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
25
26
 
26
27
 
28
+
27
29
  class TVShowManager:
28
30
  def __init__(self):
29
- """
30
- Initialize TVShowManager with provided column information.
31
- """
31
+ """Initialize TVShowManager with default values."""
32
32
  self.console = Console()
33
- self.tv_shows: List[Dict[str, Any]] = [] # List to store TV show data as dictionaries
33
+ self.tv_shows: List[Dict[str, Any]] = []
34
34
  self.slice_start: int = 0
35
35
  self.slice_end: int = 5
36
36
  self.step: int = self.slice_end
@@ -55,7 +55,7 @@ class TVShowManager:
55
55
  """
56
56
  self.column_info = column_info
57
57
 
58
- def add_tv_show(self, tv_show: Dict[str, Any]):
58
+ def add_tv_show(self, tv_show: Dict[str, Any]) -> None:
59
59
  """
60
60
  Add a TV show to the list of TV shows.
61
61
 
@@ -64,7 +64,7 @@ class TVShowManager:
64
64
  """
65
65
  self.tv_shows.append(tv_show)
66
66
 
67
- def display_data(self, data_slice: List[Dict[str, Any]]):
67
+ def display_data(self, data_slice: List[Dict[str, Any]]) -> None:
68
68
  """
69
69
  Display TV show data in a tabular format.
70
70
 
@@ -76,21 +76,17 @@ class TVShowManager:
76
76
  # Add columns dynamically based on provided column information
77
77
  for col_name, col_style in self.column_info.items():
78
78
  color = col_style.get("color", None)
79
- if color:
80
- style = Style(color=color)
81
- else:
82
- style = None
79
+ style = Style(color=color) if color else None
83
80
  table.add_column(col_name, style=style, justify='center')
84
81
 
85
82
  # Add rows dynamically based on available TV show data
86
83
  for entry in data_slice:
87
- # Create row data while handling missing keys
88
- row_data = [entry.get(col_name, '') for col_name in self.column_info.keys()]
84
+ row_data = [str(entry.get(col_name, '')) for col_name in self.column_info.keys()]
89
85
  table.add_row(*row_data)
90
86
 
91
87
  self.console.print(table)
92
88
 
93
- def run_back_command(self, research_func: dict):
89
+ def run_back_command(self, research_func: dict) -> None:
94
90
  """
95
91
  Executes a back-end search command by dynamically importing a module and invoking its search function.
96
92
 
@@ -99,46 +95,33 @@ class TVShowManager:
99
95
  - 'folder' (str): The absolute path to the directory containing the module to be executed.
100
96
  """
101
97
  try:
102
-
103
98
  # Get site name from folder
104
- site_name = (os.path.basename(research_func['folder']))
99
+ site_name = Path(research_func['folder']).name
105
100
 
106
101
  # Find the project root directory
107
102
  current_path = research_func['folder']
108
103
  while not os.path.exists(os.path.join(current_path, 'StreamingCommunity')):
109
104
  current_path = os.path.dirname(current_path)
110
105
 
111
- # Add project root to Python path
112
106
  project_root = current_path
113
- #print(f"[DEBUG] Project Root: {project_root}")
114
-
115
107
  if project_root not in sys.path:
116
108
  sys.path.insert(0, project_root)
117
109
 
118
110
  # Import using full absolute import
119
111
  module_path = f'StreamingCommunity.Api.Site.{site_name}'
120
- #print(f"[DEBUG] Importing module: {module_path}")
121
-
122
- # Import the module
123
112
  module = importlib.import_module(module_path)
124
113
 
125
- # Get the search function
114
+ # Get and call the search function
126
115
  search_func = getattr(module, 'search')
127
-
128
- # Call the search function with the search string
129
116
  search_func(None)
130
117
 
131
118
  except Exception as e:
132
119
  self.console.print(f"[red]Error during search: {e}")
120
+ logging.exception("Error during search execution")
133
121
 
134
- # Print detailed traceback
135
- import traceback
136
- traceback.print_exc()
137
-
138
- # Optionally remove the path if you want to clean up
139
- if project_root in sys.path:
140
- sys.path.remove(project_root)
141
-
122
+ finally:
123
+ if project_root in sys.path:
124
+ sys.path.remove(project_root)
142
125
 
143
126
  def run(self, force_int_input: bool = False, max_int_input: int = 0) -> str:
144
127
  """
@@ -152,121 +135,92 @@ class TVShowManager:
152
135
  str: Last command executed before breaking out of the loop.
153
136
  """
154
137
  total_items = len(self.tv_shows)
155
- last_command = "" # Variable to store the last command executed
138
+ last_command = ""
139
+ is_telegram = config_manager.get_bool('DEFAULT', 'telegram_bot')
140
+ bot = get_bot_instance() if is_telegram else None
156
141
 
157
- if TELEGRAM_BOT:
158
- bot = get_bot_instance()
159
-
160
142
  while True:
161
143
  start_message()
162
-
163
- # Display table
164
144
  self.display_data(self.tv_shows[self.slice_start:self.slice_end])
165
145
 
166
146
  # Find research function from call stack
167
- research_func = None
168
- for reverse_fun in get_call_stack():
169
- if reverse_fun['function'] == 'search' and reverse_fun['script'] == '__init__.py':
170
- research_func = reverse_fun
171
- logging.info(f"Found research_func: {research_func}")
147
+ research_func = next((
148
+ f for f in get_call_stack()
149
+ if f['function'] == 'search' and f['script'] == '__init__.py'
150
+ ), None)
172
151
 
173
- # Handling user input for loading more items or quitting
152
+ # Handle pagination and user input
174
153
  if self.slice_end < total_items:
175
154
  self.console.print(f"\n[green]Press [red]Enter [green]for next page, [red]'q' [green]to quit, or [red]'back' [green]to search.")
176
155
 
177
156
  if not force_int_input:
178
- if TELEGRAM_BOT:
179
- self.console.print(f"\n[cyan]Insert media index [yellow](e.g., 1), [red]* [cyan]to download all media, "
180
- "[yellow](e.g., 1-2) [cyan]for a range of media, or [yellow](e.g., 3-*) [cyan]to download from a specific index to the end")
181
- key = bot.ask(
182
- "select_title_episode",
183
- f"Inserisci l'indice dei media (ad esempio, 1), * per scaricare tutti i media, (ad esempio, 1-2) per un intervallo di media, o (ad esempio, 3-*) per scaricare dal un indice specifico fino alla fine.",
184
- None
185
- )
186
- else:
187
- key = Prompt.ask(
188
- "\n[cyan]Insert media index [yellow](e.g., 1), [red]* [cyan]to download all media, "
189
- "[yellow](e.g., 1-2) [cyan]for a range of media, or [yellow](e.g., 3-*) [cyan]to download from a specific index to the end"
190
- )
157
+ prompt_msg = ("\n[cyan]Insert media index [yellow](e.g., 1), [red]* [cyan]to download all media, "
158
+ "[yellow](e.g., 1-2) [cyan]for a range of media, or [yellow](e.g., 3-*) [cyan]to download from a specific index to the end")
191
159
 
160
+ if is_telegram:
161
+ key = bot.ask("select_title_episode", prompt_msg, None)
162
+ else:
163
+ key = Prompt.ask(prompt_msg)
192
164
  else:
193
- choices = [str(i) for i in range(0, max_int_input)]
194
- choices.extend(["q", "quit", "b", "back"])
195
- if TELEGRAM_BOT:
196
- self.console.print(f"[cyan]Insert media [red]index")
197
- key = bot.ask(
198
- "select_title",
199
- f"Scegli il contenuto da scaricare:\n📺 Serie TV - 🎞️ Film - 🌀 Anime\noppure `back` per tornare indietro",
200
- None
201
- )
165
+ choices = [str(i) for i in range(max_int_input + 1)] + ["q", "quit", "b", "back"]
166
+ prompt_msg = "[cyan]Insert media [red]index"
167
+ telegram_msg = "Scegli il contenuto da scaricare:\n📺 Serie TV - 🎞️ Film - 🌀 Anime\noppure `back` per tornare indietro"
168
+
169
+ if is_telegram:
170
+ key = bot.ask("select_title", telegram_msg, None)
202
171
  else:
203
- key = Prompt.ask("[cyan]Insert media [red]index", choices=choices, show_choices=False)
204
-
172
+ key = Prompt.ask(prompt_msg, choices=choices, show_choices=False)
173
+
205
174
  last_command = key
206
175
 
207
- if key.lower() == "q" or key.lower() == "quit":
176
+ if key.lower() in ["q", "quit"]:
208
177
  break
209
-
210
178
  elif key == "":
211
179
  self.slice_start += self.step
212
180
  self.slice_end += self.step
213
181
  if self.slice_end > total_items:
214
182
  self.slice_end = total_items
215
-
216
- elif (key.lower() == "b" or key.lower() == "back") and research_func:
183
+ elif (key.lower() in ["b", "back"]) and research_func:
217
184
  self.run_back_command(research_func)
218
-
219
185
  else:
220
186
  break
221
187
 
222
188
  else:
223
- # Last slice, ensure all remaining items are shown
224
- self.console.print(f"\n [green]You've reached the end. [red]Enter [green]for first page, [red]'q' [green]to quit, or [red]'back' [green]to search.")
189
+ # Last page handling
190
+ self.console.print(f"\n[green]You've reached the end. [red]Enter [green]for first page, [red]'q' [green]to quit, or [red]'back' [green]to search.")
191
+
225
192
  if not force_int_input:
226
- if TELEGRAM_BOT:
227
- self.console.print(f"\n[cyan]Insert media index [yellow](e.g., 1), [red]* [cyan]to download all media, "
228
- "[yellow](e.g., 1-2) [cyan]for a range of media, or [yellow](e.g., 3-*) [cyan]to download from a specific index to the end")
229
- key = bot.ask(
230
- "select_title_episode",
231
- f"Inserisci l'indice dei media (ad esempio, 1), * per scaricare tutti i media, (ad esempio, 1-2) per un intervallo di media, o (ad esempio, 3-*) per scaricare dal un indice specifico fino alla fine.",
232
- None
233
- )
193
+ prompt_msg = ("\n[cyan]Insert media index [yellow](e.g., 1), [red]* [cyan]to download all media, "
194
+ "[yellow](e.g., 1-2) [cyan]for a range of media, or [yellow](e.g., 3-*) [cyan]to download from a specific index to the end")
195
+
196
+ if is_telegram:
197
+ key = bot.ask("select_title_episode", prompt_msg, None)
234
198
  else:
235
- key = Prompt.ask(
236
- "\n[cyan]Insert media index [yellow](e.g., 1), [red]* [cyan]to download all media, "
237
- "[yellow](e.g., 1-2) [cyan]for a range of media, or [yellow](e.g., 3-*) [cyan]to download from a specific index to the end"
238
- )
239
-
199
+ key = Prompt.ask(prompt_msg)
240
200
  else:
241
- choices = [str(i) for i in range(0, max_int_input)]
242
- choices.extend(["q", "quit", "b", "back"])
243
-
244
- if TELEGRAM_BOT:
245
- self.console.print(f"[cyan]Insert media [red]index")
246
- key = bot.ask(
247
- "select_title",
248
- f"Scegli il contenuto da scaricare:\n📺 Serie TV - 🎞️ Film - 🌀 Anime\n oppure `back` per tornare indietro",
249
- None
250
- )
201
+ choices = [str(i) for i in range(max_int_input + 1)] + ["q", "quit", "b", "back"]
202
+ prompt_msg = "[cyan]Insert media [red]index"
203
+ telegram_msg = "Scegli il contenuto da scaricare:\n📺 Serie TV - 🎞️ Film - 🌀 Anime\noppure `back` per tornare indietro"
204
+
205
+ if is_telegram:
206
+ key = bot.ask("select_title", telegram_msg, None)
251
207
  else:
252
- key = Prompt.ask("[cyan]Insert media [red]index", choices=choices, show_choices=False)
253
-
208
+ key = Prompt.ask(prompt_msg, choices=choices, show_choices=False)
209
+
254
210
  last_command = key
255
211
 
256
- if key.lower() == "q" or key.lower() == "quit":
212
+ if key.lower() in ["q", "quit"]:
257
213
  break
258
-
259
214
  elif key == "":
260
215
  self.slice_start = 0
261
216
  self.slice_end = self.step
262
-
263
- elif (key.lower() == "b" or key.lower() == "back") and research_func:
217
+ elif (key.lower() in ["b", "back"]) and research_func:
264
218
  self.run_back_command(research_func)
265
-
266
219
  else:
267
220
  break
268
-
221
+
269
222
  return last_command
270
223
 
271
- def clear(self):
224
+ def clear(self) -> None:
225
+ """Clear all TV shows data."""
272
226
  self.tv_shows = []