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.
- StreamingCommunity/Api/Player/ddl.py +2 -3
- StreamingCommunity/Api/Site/1337xx/__init__.py +5 -6
- StreamingCommunity/Api/Site/1337xx/site.py +7 -14
- StreamingCommunity/Api/Site/1337xx/title.py +3 -5
- StreamingCommunity/Api/Site/altadefinizionegratis/__init__.py +7 -6
- StreamingCommunity/Api/Site/altadefinizionegratis/film.py +14 -19
- StreamingCommunity/Api/Site/altadefinizionegratis/site.py +6 -14
- StreamingCommunity/Api/Site/animeunity/__init__.py +7 -7
- StreamingCommunity/Api/Site/animeunity/film_serie.py +29 -31
- StreamingCommunity/Api/Site/animeunity/site.py +14 -22
- StreamingCommunity/Api/Site/cb01new/__init__.py +5 -4
- StreamingCommunity/Api/Site/cb01new/film.py +2 -5
- StreamingCommunity/Api/Site/cb01new/site.py +5 -13
- StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +5 -4
- StreamingCommunity/Api/Site/ddlstreamitaly/series.py +12 -49
- StreamingCommunity/Api/Site/ddlstreamitaly/site.py +6 -16
- StreamingCommunity/Api/Site/ddlstreamitaly/util/ScrapeSerie.py +2 -3
- StreamingCommunity/Api/Site/guardaserie/__init__.py +5 -4
- StreamingCommunity/Api/Site/guardaserie/series.py +12 -46
- StreamingCommunity/Api/Site/guardaserie/site.py +5 -13
- StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +10 -14
- StreamingCommunity/Api/Site/ilcorsaronero/__init__.py +5 -4
- StreamingCommunity/Api/Site/ilcorsaronero/site.py +5 -13
- StreamingCommunity/Api/Site/ilcorsaronero/title.py +3 -5
- StreamingCommunity/Api/Site/mostraguarda/__init__.py +2 -2
- StreamingCommunity/Api/Site/mostraguarda/film.py +4 -8
- StreamingCommunity/Api/Site/streamingcommunity/__init__.py +8 -7
- StreamingCommunity/Api/Site/streamingcommunity/film.py +14 -18
- StreamingCommunity/Api/Site/streamingcommunity/series.py +25 -76
- StreamingCommunity/Api/Site/streamingcommunity/site.py +11 -23
- StreamingCommunity/Api/Template/Util/__init__.py +8 -1
- StreamingCommunity/Api/Template/Util/manage_ep.py +46 -2
- StreamingCommunity/Api/Template/config_loader.py +71 -0
- StreamingCommunity/Lib/Downloader/HLS/downloader.py +60 -60
- StreamingCommunity/Lib/Downloader/HLS/segments.py +40 -15
- StreamingCommunity/Lib/Downloader/MP4/downloader.py +47 -40
- StreamingCommunity/Lib/FFmpeg/command.py +59 -3
- StreamingCommunity/Lib/M3U8/estimator.py +10 -12
- StreamingCommunity/Lib/M3U8/parser.py +12 -51
- StreamingCommunity/Lib/TMBD/tmdb.py +66 -99
- StreamingCommunity/TelegramHelp/telegram_bot.py +222 -68
- StreamingCommunity/Util/_jsonConfig.py +14 -13
- StreamingCommunity/Util/ffmpeg_installer.py +70 -64
- StreamingCommunity/Util/headers.py +11 -122
- StreamingCommunity/Util/os.py +65 -56
- StreamingCommunity/Util/table.py +62 -108
- StreamingCommunity/run.py +16 -11
- {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/METADATA +57 -23
- StreamingCommunity-2.5.8.dist-info/RECORD +86 -0
- StreamingCommunity/Api/Site/1337xx/costant.py +0 -15
- StreamingCommunity/Api/Site/altadefinizionegratis/costant.py +0 -21
- StreamingCommunity/Api/Site/animeunity/costant.py +0 -21
- StreamingCommunity/Api/Site/cb01new/costant.py +0 -19
- StreamingCommunity/Api/Site/ddlstreamitaly/costant.py +0 -20
- StreamingCommunity/Api/Site/guardaserie/costant.py +0 -19
- StreamingCommunity/Api/Site/ilcorsaronero/costant.py +0 -19
- StreamingCommunity/Api/Site/mostraguarda/costant.py +0 -19
- StreamingCommunity/Api/Site/streamingcommunity/costant.py +0 -21
- StreamingCommunity/TelegramHelp/request_manager.py +0 -82
- StreamingCommunity/TelegramHelp/session.py +0 -56
- StreamingCommunity-2.5.6.dist-info/RECORD +0 -96
- {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/LICENSE +0 -0
- {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/WHEEL +0 -0
- {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/entry_points.txt +0 -0
- {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/top_level.txt +0 -0
StreamingCommunity/Util/os.py
CHANGED
|
@@ -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
|
-
#
|
|
205
|
-
|
|
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
|
-
#
|
|
208
|
-
|
|
209
|
-
|
|
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"
|
|
217
|
-
|
|
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=
|
|
290
|
+
httpx.get("https://www.google.com", timeout=5)
|
|
284
291
|
break
|
|
285
292
|
|
|
286
|
-
except
|
|
293
|
+
except Exception as e:
|
|
287
294
|
console.log("[bold red]Internet is not available. Waiting...[/bold red]")
|
|
288
|
-
time.sleep(
|
|
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
|
StreamingCommunity/Util/table.py
CHANGED
|
@@ -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]] = []
|
|
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
|
-
|
|
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 = (
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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 = ""
|
|
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 =
|
|
168
|
-
|
|
169
|
-
if
|
|
170
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
179
|
-
|
|
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(
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
204
|
-
|
|
172
|
+
key = Prompt.ask(prompt_msg, choices=choices, show_choices=False)
|
|
173
|
+
|
|
205
174
|
last_command = key
|
|
206
175
|
|
|
207
|
-
if key.lower()
|
|
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
|
|
224
|
-
self.console.print(f"\n
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
|
|
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(
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
253
|
-
|
|
208
|
+
key = Prompt.ask(prompt_msg, choices=choices, show_choices=False)
|
|
209
|
+
|
|
254
210
|
last_command = key
|
|
255
211
|
|
|
256
|
-
if key.lower()
|
|
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 = []
|