StreamingCommunity 1.8.0__py3-none-any.whl → 1.9.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 (99) hide show
  1. StreamingCommunity/{Src/Api → Api}/Player/ddl.py +3 -3
  2. StreamingCommunity/{Src/Api → Api}/Player/maxstream.py +2 -2
  3. StreamingCommunity/{Src/Api → Api}/Player/supervideo.py +2 -2
  4. StreamingCommunity/{Src/Api → Api}/Player/vixcloud.py +3 -3
  5. StreamingCommunity/{Src/Api → Api}/Site/1337xx/__init__.py +1 -1
  6. StreamingCommunity/{Src/Api → Api}/Site/1337xx/costant.py +3 -3
  7. StreamingCommunity/{Src/Api → Api}/Site/1337xx/site.py +7 -7
  8. StreamingCommunity/{Src/Api → Api}/Site/1337xx/title.py +6 -6
  9. StreamingCommunity/{Src/Api → Api}/Site/altadefinizione/__init__.py +1 -1
  10. StreamingCommunity/{Src/Api/Site/mostraguarda → Api/Site/altadefinizione}/costant.py +3 -3
  11. StreamingCommunity/{Src/Api → Api}/Site/altadefinizione/film.py +8 -8
  12. StreamingCommunity/{Src/Api → Api}/Site/altadefinizione/site.py +7 -7
  13. StreamingCommunity/{Src/Api → Api}/Site/animeunity/__init__.py +1 -1
  14. StreamingCommunity/{Src/Api/Site/altadefinizione → Api/Site/animeunity}/costant.py +3 -3
  15. StreamingCommunity/{Src/Api → Api}/Site/animeunity/film_serie.py +18 -19
  16. StreamingCommunity/{Src/Api → Api}/Site/animeunity/site.py +6 -6
  17. StreamingCommunity/{Src/Api → Api}/Site/animeunity/util/ScrapeSerie.py +3 -3
  18. StreamingCommunity/{Src/Api → Api}/Site/bitsearch/__init__.py +1 -1
  19. StreamingCommunity/{Src/Api → Api}/Site/bitsearch/costant.py +3 -3
  20. StreamingCommunity/{Src/Api → Api}/Site/bitsearch/site.py +7 -7
  21. StreamingCommunity/{Src/Api → Api}/Site/bitsearch/title.py +5 -5
  22. StreamingCommunity/{Src/Api → Api}/Site/cb01new/__init__.py +1 -1
  23. StreamingCommunity/{Src/Api → Api}/Site/cb01new/costant.py +3 -3
  24. StreamingCommunity/{Src/Api → Api}/Site/cb01new/film.py +8 -8
  25. StreamingCommunity/{Src/Api → Api}/Site/cb01new/site.py +6 -6
  26. StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/__init__.py +1 -1
  27. StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/costant.py +3 -3
  28. StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/series.py +11 -12
  29. StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/site.py +7 -7
  30. StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/util/ScrapeSerie.py +5 -3
  31. StreamingCommunity/{Src/Api → Api}/Site/guardaserie/__init__.py +1 -1
  32. StreamingCommunity/{Src/Api/Site/piratebays → Api/Site/guardaserie}/costant.py +3 -3
  33. StreamingCommunity/{Src/Api → Api}/Site/guardaserie/series.py +11 -11
  34. StreamingCommunity/{Src/Api → Api}/Site/guardaserie/site.py +7 -7
  35. StreamingCommunity/{Src/Api/Site/guardaserie/Player → Api/Site/guardaserie/util}/ScrapeSerie.py +2 -2
  36. StreamingCommunity/{Src/Api → Api}/Site/mostraguarda/__init__.py +2 -2
  37. StreamingCommunity/{Src/Api/Site/animeunity → Api/Site/mostraguarda}/costant.py +3 -3
  38. StreamingCommunity/{Src/Api → Api}/Site/mostraguarda/film.py +9 -9
  39. StreamingCommunity/{Src/Api → Api}/Site/piratebays/__init__.py +1 -1
  40. StreamingCommunity/{Src/Api/Site/guardaserie → Api/Site/piratebays}/costant.py +3 -3
  41. StreamingCommunity/{Src/Api → Api}/Site/piratebays/site.py +6 -6
  42. StreamingCommunity/{Src/Api → Api}/Site/piratebays/title.py +5 -5
  43. StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/__init__.py +1 -1
  44. StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/costant.py +3 -3
  45. StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/film.py +10 -10
  46. StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/series.py +20 -18
  47. StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/site.py +7 -7
  48. StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/util/ScrapeSerie.py +3 -3
  49. StreamingCommunity/{Src/Api → Api}/Template/Util/get_domain.py +3 -3
  50. StreamingCommunity/{Src/Api → Api}/Template/Util/manage_ep.py +2 -2
  51. StreamingCommunity/{Src/Api → Api}/Template/Util/recall_search.py +1 -1
  52. StreamingCommunity/{Src/Api → Api}/Template/site.py +1 -1
  53. StreamingCommunity/{Src/Lib → Lib}/Downloader/HLS/downloader.py +4 -4
  54. StreamingCommunity/{Src/Lib → Lib}/Downloader/HLS/proxyes.py +3 -3
  55. StreamingCommunity/{Src/Lib → Lib}/Downloader/HLS/segments.py +42 -44
  56. StreamingCommunity/{Src/Lib → Lib}/Downloader/MP4/downloader.py +5 -5
  57. StreamingCommunity/{Src/Lib → Lib}/Downloader/TOR/downloader.py +3 -3
  58. StreamingCommunity/{Src/Lib → Lib}/Driver/driver_1.py +1 -1
  59. StreamingCommunity/{Src/Lib → Lib}/FFmpeg/capture.py +2 -2
  60. StreamingCommunity/{Src/Lib → Lib}/FFmpeg/command.py +3 -3
  61. StreamingCommunity/{Src/Lib → Lib}/FFmpeg/util.py +1 -1
  62. StreamingCommunity/{Src/Lib → Lib}/M3U8/decryptor.py +59 -24
  63. StreamingCommunity/{Src/Lib → Lib}/M3U8/estimator.py +6 -3
  64. StreamingCommunity/{Src/Lib → Lib}/M3U8/parser.py +1 -1
  65. StreamingCommunity/{Src/Lib → Lib}/TMBD/tmdb.py +1 -1
  66. StreamingCommunity/{Src/Upload → Upload}/update.py +6 -2
  67. StreamingCommunity/{Src/Upload → Upload}/version.py +1 -1
  68. StreamingCommunity/Util/ffmpeg_installer.py +275 -0
  69. StreamingCommunity/{Src/Util → Util}/headers.py +1 -1
  70. StreamingCommunity/{Src/Util → Util}/logger.py +1 -1
  71. StreamingCommunity/{Src/Util → Util}/message.py +2 -2
  72. StreamingCommunity/{Src/Util → Util}/os.py +118 -21
  73. StreamingCommunity/run.py +18 -12
  74. {StreamingCommunity-1.8.0.dist-info → StreamingCommunity-1.9.1.dist-info}/METADATA +126 -60
  75. StreamingCommunity-1.9.1.dist-info/RECORD +95 -0
  76. {StreamingCommunity-1.8.0.dist-info → StreamingCommunity-1.9.1.dist-info}/WHEEL +1 -1
  77. StreamingCommunity/Src/Api/Site/animeunity/anime.py +0 -126
  78. StreamingCommunity/Src/Api/Site/ddlstreamitaly/Player/ScrapeSerie.py +0 -83
  79. StreamingCommunity/Src/Api/Site/guardaserie/util/ScrapeSerie.py +0 -110
  80. StreamingCommunity-1.8.0.dist-info/RECORD +0 -97
  81. /StreamingCommunity/{Src/Api → Api}/Player/Helper/Vixcloud/js_parser.py +0 -0
  82. /StreamingCommunity/{Src/Api → Api}/Player/Helper/Vixcloud/util.py +0 -0
  83. /StreamingCommunity/{Src/Api → Api}/Template/Class/SearchType.py +0 -0
  84. /StreamingCommunity/{Src/Api → Api}/Template/Util/__init__.py +0 -0
  85. /StreamingCommunity/{Src/Api → Api}/Template/__init__.py +0 -0
  86. /StreamingCommunity/{Src/Lib → Lib}/Downloader/__init__.py +0 -0
  87. /StreamingCommunity/{Src/Lib → Lib}/FFmpeg/__init__.py +0 -0
  88. /StreamingCommunity/{Src/Lib → Lib}/M3U8/__init__.py +0 -0
  89. /StreamingCommunity/{Src/Lib → Lib}/M3U8/url_fixer.py +0 -0
  90. /StreamingCommunity/{Src/Lib → Lib}/TMBD/__init__.py +0 -0
  91. /StreamingCommunity/{Src/Lib → Lib}/TMBD/obj_tmbd.py +0 -0
  92. /StreamingCommunity/{Src/Util → Util}/_jsonConfig.py +0 -0
  93. /StreamingCommunity/{Src/Util → Util}/call_stack.py +0 -0
  94. /StreamingCommunity/{Src/Util → Util}/color.py +0 -0
  95. /StreamingCommunity/{Src/Util → Util}/console.py +0 -0
  96. /StreamingCommunity/{Src/Util → Util}/table.py +0 -0
  97. {StreamingCommunity-1.8.0.dist-info → StreamingCommunity-1.9.1.dist-info}/LICENSE +0 -0
  98. {StreamingCommunity-1.8.0.dist-info → StreamingCommunity-1.9.1.dist-info}/entry_points.txt +0 -0
  99. {StreamingCommunity-1.8.0.dist-info → StreamingCommunity-1.9.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,275 @@
1
+ # 24.01.2024
2
+
3
+ import os
4
+ import platform
5
+ import subprocess
6
+ import zipfile
7
+ import tarfile
8
+ import logging
9
+ import requests
10
+ import shutil
11
+ import glob
12
+ from typing import Optional, Tuple
13
+
14
+
15
+ # External library
16
+ from rich.console import Console
17
+ from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn
18
+
19
+
20
+ # Variable
21
+ console = Console()
22
+ FFMPEG_CONFIGURATION = {
23
+ 'windows': {
24
+ 'base_dir': lambda home: os.path.join(os.path.splitdrive(home)[0] + os.path.sep, 'binary'),
25
+ 'download_url': 'https://github.com/GyanD/codexffmpeg/releases/download/{version}/ffmpeg-{version}-full_build.zip',
26
+ 'file_extension': '.zip',
27
+ 'executables': ['ffmpeg.exe', 'ffprobe.exe', 'ffplay.exe']
28
+ },
29
+ 'darwin': {
30
+ 'base_dir': lambda home: os.path.join(home, 'Applications', 'binary'),
31
+ 'download_url': 'https://evermeet.cx/ffmpeg/ffmpeg-{version}.zip',
32
+ 'file_extension': '.zip',
33
+ 'executables': ['ffmpeg', 'ffprobe', 'ffplay']
34
+ },
35
+ 'linux': {
36
+ 'base_dir': lambda home: os.path.join(home, '.local', 'bin', 'binary'),
37
+ 'download_url': 'https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-{arch}-static.tar.xz',
38
+ 'file_extension': '.tar.xz',
39
+ 'executables': ['ffmpeg', 'ffprobe', 'ffplay']
40
+ }
41
+ }
42
+
43
+
44
+
45
+ class FFMPEGDownloader:
46
+ def __init__(self):
47
+ self.os_name = self._detect_system()
48
+ self.arch = self._detect_arch()
49
+ self.home_dir = os.path.expanduser('~')
50
+ self.base_dir = self._get_base_directory()
51
+
52
+ def _detect_system(self) -> str:
53
+ """Detect and normalize operating system name."""
54
+ system = platform.system().lower()
55
+
56
+ if system in FFMPEG_CONFIGURATION:
57
+ return system
58
+
59
+ raise ValueError(f"Unsupported operating system: {system}")
60
+
61
+ def _detect_arch(self) -> str:
62
+ """
63
+ Detect system architecture
64
+ """
65
+ machine = platform.machine().lower()
66
+
67
+ arch_map = {
68
+ 'amd64': 'x86_64',
69
+ 'x86_64': 'x86_64',
70
+ 'x64': 'x86_64',
71
+ 'arm64': 'arm64',
72
+ 'aarch64': 'arm64'
73
+ }
74
+
75
+ return arch_map.get(machine, machine)
76
+
77
+ def _get_base_directory(self) -> str:
78
+ """
79
+ Get base directory for binaries
80
+ """
81
+ base_dir = FFMPEG_CONFIGURATION[self.os_name]['base_dir'](self.home_dir)
82
+ os.makedirs(base_dir, exist_ok=True)
83
+
84
+ return base_dir
85
+
86
+ def _check_existing_binaries(self) -> Tuple[Optional[str], Optional[str]]:
87
+ """
88
+ Check if FFmpeg binaries already exist in the base directory
89
+ """
90
+ config = FFMPEG_CONFIGURATION[self.os_name]
91
+ executables = config['executables']
92
+
93
+ found_executables = []
94
+ for executable in executables:
95
+
96
+ # Search for exact executable in base directory
97
+ exe_paths = glob.glob(os.path.join(self.base_dir, executable))
98
+ if exe_paths:
99
+ found_executables.append(exe_paths[0])
100
+
101
+ # Return paths if both executables are found
102
+ if len(found_executables) == len(executables):
103
+ return tuple(found_executables)
104
+
105
+ return None, None
106
+
107
+ def _get_latest_version(self) -> str:
108
+ """
109
+ Get the latest FFmpeg version
110
+ """
111
+ try:
112
+ version_url = 'https://www.gyan.dev/ffmpeg/builds/release-version'
113
+ return requests.get(version_url).text.strip()
114
+
115
+ except Exception as e:
116
+ logging.error(f"Unable to get version: {e}")
117
+ return None
118
+
119
+ def _download_file(self, url: str, destination: str) -> bool:
120
+ """
121
+ Download with Rich progress bar
122
+ """
123
+ try:
124
+ response = requests.get(url, stream=True)
125
+ response.raise_for_status()
126
+
127
+ total_size = int(response.headers.get('content-length', 0))
128
+
129
+ with open(destination, 'wb') as file, \
130
+ Progress(
131
+ SpinnerColumn(),
132
+ TextColumn("[progress.description]{task.description}"),
133
+ BarColumn(),
134
+ TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
135
+ TimeRemainingColumn()
136
+ ) as progress:
137
+
138
+ download_task = progress.add_task("[green]Downloading FFmpeg", total=total_size)
139
+
140
+ for chunk in response.iter_content(chunk_size=8192):
141
+ size = file.write(chunk)
142
+ progress.update(download_task, advance=size)
143
+
144
+ return True
145
+
146
+ except Exception as e:
147
+ logging.error(f"Download error: {e}")
148
+ return False
149
+
150
+ def _extract_and_copy_binaries(self, archive_path: str) -> Tuple[Optional[str], Optional[str]]:
151
+ """
152
+ Extract archive and copy executables to base directory
153
+ """
154
+ try:
155
+ # Temporary extraction path
156
+ extraction_path = os.path.join(self.base_dir, 'temp_extract')
157
+ os.makedirs(extraction_path, exist_ok=True)
158
+
159
+ # Extract based on file type
160
+ if archive_path.endswith('.zip'):
161
+ with zipfile.ZipFile(archive_path, 'r') as zip_ref:
162
+ zip_ref.extractall(extraction_path)
163
+ elif archive_path.endswith('.tar.xz'):
164
+ import lzma
165
+ with lzma.open(archive_path, 'rb') as xz_file:
166
+ with tarfile.open(fileobj=xz_file) as tar_ref:
167
+ tar_ref.extractall(extraction_path)
168
+
169
+ # Find and copy executables
170
+ config = FFMPEG_CONFIGURATION[self.os_name]
171
+ executables = config['executables']
172
+
173
+ found_paths = []
174
+ for executable in executables:
175
+ # Find executable in extracted files
176
+ exe_paths = glob.glob(os.path.join(extraction_path, '**', executable), recursive=True)
177
+
178
+ if exe_paths:
179
+ # Copy to base directory
180
+ dest_path = os.path.join(self.base_dir, executable)
181
+ shutil.copy2(exe_paths[0], dest_path)
182
+
183
+ # Set execution permissions for Unix-like systems
184
+ if self.os_name != 'windows':
185
+ os.chmod(dest_path, 0o755)
186
+
187
+ found_paths.append(dest_path)
188
+
189
+ # Clean up temporary extraction directory
190
+ shutil.rmtree(extraction_path, ignore_errors=True)
191
+
192
+ # Remove downloaded archive
193
+ os.remove(archive_path)
194
+
195
+ # Return paths if both executables found
196
+ if len(found_paths) == len(executables):
197
+ return tuple(found_paths)
198
+
199
+ return None, None
200
+
201
+ except Exception as e:
202
+ logging.error(f"Extraction/copy error: {e}")
203
+ return None, None
204
+
205
+ def download(self) -> Tuple[Optional[str], Optional[str]]:
206
+ """
207
+ Main download procedure
208
+ Returns paths of ffmpeg and ffprobe
209
+ """
210
+ # First, check if binaries already exist in base directory
211
+ existing_ffmpeg, existing_ffprobe, existing_ffplay = self._check_existing_binaries()
212
+ if existing_ffmpeg and existing_ffprobe:
213
+ return existing_ffmpeg, existing_ffprobe
214
+
215
+ # Get latest version
216
+ version = self._get_latest_version()
217
+ if not version:
218
+ logging.error("Cannot proceed: version not found")
219
+ return None, None
220
+
221
+ # Prepare configurations
222
+ config = FFMPEG_CONFIGURATION[self.os_name]
223
+
224
+ # Build download URL
225
+ download_url = config['download_url'].format(
226
+ version=version,
227
+ arch=self.arch
228
+ )
229
+
230
+ # Download path
231
+ download_path = os.path.join(
232
+ self.base_dir,
233
+ f'ffmpeg-{version}{config["file_extension"]}'
234
+ )
235
+
236
+ # Download
237
+ console.print(
238
+ f"[bold blue]Downloading FFmpeg from:[/] {download_url}",
239
+ )
240
+ if not self._download_file(download_url, download_path):
241
+ return None, None
242
+
243
+ # Extract and copy binaries
244
+ ffmpeg_path, ffprobe_path = self._extract_and_copy_binaries(download_path)
245
+
246
+ if ffmpeg_path and ffprobe_path:
247
+ return ffmpeg_path, ffprobe_path
248
+
249
+ logging.error("FFmpeg executables not found")
250
+ return None, None
251
+
252
+
253
+ def check_ffmpeg():
254
+ try:
255
+ # First, use 'where' command to check existing binaries on Windows
256
+ if platform.system().lower() == 'windows':
257
+ ffmpeg_path = subprocess.check_output(['where', 'ffmpeg'], text=True).strip().split('\n')[0] if subprocess.call(['where', 'ffmpeg'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0 else None
258
+ ffprobe_path = subprocess.check_output(['where', 'ffprobe'], text=True).strip().split('\n')[0] if subprocess.call(['where', 'ffprobe'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0 else None
259
+
260
+ if ffmpeg_path and ffprobe_path:
261
+ return ffmpeg_path, ffprobe_path
262
+
263
+ # Fallback to which/shutil method for Unix-like systems
264
+ ffmpeg_path = shutil.which('ffmpeg')
265
+ ffprobe_path = shutil.which('ffprobe')
266
+
267
+ if ffmpeg_path and ffprobe_path:
268
+ return ffmpeg_path, ffprobe_path
269
+
270
+ downloader = FFMPEGDownloader()
271
+ return downloader.download()
272
+
273
+ except Exception as e:
274
+ logging.error(f"Error checking FFmpeg: {e}")
275
+ return None, None
@@ -9,7 +9,7 @@ from fake_useragent import UserAgent
9
9
 
10
10
 
11
11
  # Variable
12
- ua = UserAgent()
12
+ ua = UserAgent(use_external_data=True)
13
13
 
14
14
 
15
15
  def extract_versions(user_agent):
@@ -5,7 +5,7 @@ from logging.handlers import RotatingFileHandler
5
5
 
6
6
 
7
7
  # Internal utilities
8
- from StreamingCommunity.Src.Util._jsonConfig import config_manager
8
+ from StreamingCommunity.Util._jsonConfig import config_manager
9
9
 
10
10
 
11
11
  class Logger:
@@ -5,8 +5,8 @@ import platform
5
5
 
6
6
 
7
7
  # Internal utilities
8
- from StreamingCommunity.Src.Util.console import console
9
- from StreamingCommunity.Src.Util._jsonConfig import config_manager
8
+ from StreamingCommunity.Util.console import console
9
+ from StreamingCommunity.Util._jsonConfig import config_manager
10
10
 
11
11
 
12
12
  # Variable
@@ -3,14 +3,12 @@
3
3
  import io
4
4
  import os
5
5
  import sys
6
- import ssl
7
6
  import time
8
7
  import shutil
9
8
  import hashlib
10
9
  import logging
11
10
  import platform
12
11
  import unidecode
13
- import importlib
14
12
  import subprocess
15
13
  import contextlib
16
14
  import pathvalidate
@@ -23,7 +21,8 @@ import httpx
23
21
 
24
22
 
25
23
  # Internal utilities
26
- from StreamingCommunity.Src.Util.console import console
24
+ from .ffmpeg_installer import check_ffmpeg
25
+ from StreamingCommunity.Util.console import console, msg
27
26
 
28
27
 
29
28
  # Variable
@@ -318,30 +317,40 @@ class OsSummary():
318
317
 
319
318
  Returns:
320
319
  str: The version string of the executable.
321
-
322
- Raises:
323
- SystemExit: If the command is not found or fails to execute.
324
320
  """
325
-
326
321
  try:
327
322
  version_output = subprocess.check_output(command, stderr=subprocess.STDOUT).decode().split('\n')[0]
328
323
  return version_output.split(" ")[2]
329
324
 
330
325
  except (FileNotFoundError, subprocess.CalledProcessError):
331
- print(f"{command[0]} not found")
326
+ console.print(f"{command[0]} not found", style="bold red")
327
+ sys.exit(0)
328
+
329
+ def check_ffmpeg_location(self, command: list):
330
+ """
331
+ Run 'where ffmpeg' command to check FFmpeg's location.
332
+
333
+ Returns:
334
+ str: Location of FFmpeg executable or None if not found
335
+ """
336
+ try:
337
+ result = subprocess.check_output(command, stderr=subprocess.STDOUT, text=True).strip()
338
+ return result
339
+
340
+ except subprocess.CalledProcessError:
341
+ console.print("FFmpeg not found in system PATH", style="bold red")
332
342
  sys.exit(0)
333
343
 
334
344
  def get_library_version(self, lib_name: str):
335
345
  """
336
346
  Retrieve the version of a Python library.
337
-
347
+
338
348
  Args:
339
349
  lib_name (str): The name of the Python library.
340
-
350
+
341
351
  Returns:
342
352
  str: The library name followed by its version, or `-not installed` if not found.
343
353
  """
344
-
345
354
  try:
346
355
  version = importlib.metadata.version(lib_name)
347
356
  return f"{lib_name}-{version}"
@@ -349,7 +358,62 @@ class OsSummary():
349
358
  except importlib.metadata.PackageNotFoundError:
350
359
  return f"{lib_name}-not installed"
351
360
 
352
- def get_system_summary(self):
361
+ def download_requirements(self, url: str, filename: str):
362
+ """
363
+ Download the requirements.txt file from the specified URL if not found locally using requests.
364
+
365
+ Args:
366
+ url (str): The URL to download the requirements file from.
367
+ filename (str): The local filename to save the requirements file as.
368
+ """
369
+ try:
370
+ import requests
371
+
372
+ console.print(f"{filename} not found locally. Downloading from {url}...", style="bold yellow")
373
+ response = requests.get(url)
374
+
375
+ if response.status_code == 200:
376
+ with open(filename, 'wb') as f:
377
+ f.write(response.content)
378
+ console.print(f"{filename} successfully downloaded.", style="bold green")
379
+
380
+ else:
381
+ console.print(f"Failed to download {filename}. HTTP Status code: {response.status_code}", style="bold red")
382
+ sys.exit(1)
383
+
384
+ except Exception as e:
385
+ console.print(f"Failed to download {filename}: {e}", style="bold red")
386
+ sys.exit(1)
387
+
388
+ def install_library(self, lib_name: str):
389
+ """
390
+ Install a Python library using pip.
391
+
392
+ Args:
393
+ lib_name (str): The name of the library to install.
394
+ """
395
+ try:
396
+ console.print(f"Installing {lib_name}...", style="bold yellow")
397
+ subprocess.check_call([sys.executable, "-m", "pip", "install", lib_name])
398
+ console.print(f"{lib_name} installed successfully!", style="bold green")
399
+
400
+ except subprocess.CalledProcessError as e:
401
+ console.print(f"Failed to install {lib_name}: {e}", style="bold red")
402
+ sys.exit(1)
403
+
404
+ def check_python_version(self):
405
+ """
406
+ Check if the installed Python is the official CPython distribution.
407
+ Exits with a message if not the official version.
408
+ """
409
+ python_implementation = platform.python_implementation()
410
+
411
+ if python_implementation != "CPython":
412
+ console.print(f"[bold red]Warning: You are using a non-official Python distribution: {python_implementation}.[/bold red]")
413
+ console.print("Please install the official Python from [bold blue]https://www.python.org[/bold blue] and try again.", style="bold yellow")
414
+ sys.exit(0)
415
+
416
+ async def get_system_summary(self):
353
417
  """
354
418
  Generate a summary of the system environment.
355
419
 
@@ -360,6 +424,9 @@ class OsSummary():
360
424
  - Installed Python libraries as listed in `requirements.txt`.
361
425
  """
362
426
 
427
+ # Check if Python is the official CPython
428
+ self.check_python_version()
429
+
363
430
  # Check internet connectivity
364
431
  InternManager().check_internet()
365
432
  console.print("[bold blue]System Summary[/bold blue][white]:")
@@ -375,18 +442,48 @@ class OsSummary():
375
442
  logging.info(f"Python: {python_version} ({python_implementation} {arch}) - {os_info} ({glibc_version})")
376
443
 
377
444
  # ffmpeg and ffprobe versions
378
- ffmpeg_version = self.get_executable_version(['ffmpeg', '-version'])
379
- ffprobe_version = self.get_executable_version(['ffprobe', '-version'])
445
+ ffmpeg_path, ffprobe_path = check_ffmpeg()
446
+
447
+ # Locate ffmpeg and ffprobe
448
+ if "binary" not in ffmpeg_path:
449
+ ffmpeg_path = self.check_ffmpeg_location(['where', 'ffmpeg'])
450
+ if "binary" not in ffprobe_path:
451
+ ffprobe_path = self.check_ffmpeg_location(['where', 'ffprobe'])
452
+
453
+ ffmpeg_version = self.get_executable_version([ffprobe_path, '-version'])
454
+ ffprobe_version = self.get_executable_version([ffprobe_path, '-version'])
380
455
 
456
+ console.print(f"[cyan]Path[white]: [red]ffmpeg [bold yellow]'{ffmpeg_path}'[/bold yellow][white], [red]ffprobe '[bold yellow]{ffprobe_path}'[/bold yellow]")
381
457
  console.print(f"[cyan]Exe versions[white]: [bold red]ffmpeg {ffmpeg_version}, ffprobe {ffprobe_version}[/bold red]")
382
- logging.info(f"Dependencies: ffmpeg {ffmpeg_version}, ffprobe {ffprobe_version}")
383
458
 
384
- # Optional libraries versions
385
- """optional_libraries = [line.strip() for line in open('requirements.txt', 'r', encoding='utf-8-sig')]
386
- optional_libs_versions = [self.get_library_version(lib) for lib in optional_libraries]
387
-
388
- console.print(f"[cyan]Libraries[white]: [bold red]{', '.join(optional_libs_versions)}[/bold red]\n")
389
- logging.info(f"Libraries: {', '.join(optional_libs_versions)}")"""
459
+ # Check if requirements.txt exists, if not on pyinstaller
460
+ if not getattr(sys, 'frozen', False):
461
+ requirements_file = 'requirements.txt'
462
+
463
+ if not os.path.exists(requirements_file):
464
+ self.download_requirements(
465
+ 'https://raw.githubusercontent.com/Lovi-0/StreamingCommunity/refs/heads/main/requirements.txt',
466
+ requirements_file
467
+ )
468
+
469
+ # Read the optional libraries from the requirements file, get only name without version if "library==1.0.0"
470
+ optional_libraries = [line.strip().split("=")[0] for line in open(requirements_file, 'r', encoding='utf-8-sig')]
471
+
472
+ # Check if libraries are installed and prompt to install missing ones
473
+ for lib in optional_libraries:
474
+ installed_version = self.get_library_version(lib)
475
+
476
+ if 'not installed' in installed_version:
477
+ user_response = msg.ask(f"{lib} is not installed. Do you want to install it? (yes/no)", default="y")
478
+
479
+ if user_response.lower().strip() in ["yes", "y"]:
480
+ self.install_library(lib)
481
+
482
+ else:
483
+ logging.info(f"Library: {installed_version}")
484
+
485
+ console.print(f"[cyan]Libraries[white]: [bold red]{', '.join([self.get_library_version(lib) for lib in optional_libraries])}[/bold red]\n")
486
+ logging.info(f"Libraries: {', '.join([self.get_library_version(lib) for lib in optional_libraries])}")
390
487
 
391
488
 
392
489
 
StreamingCommunity/run.py CHANGED
@@ -12,13 +12,13 @@ from typing import Callable
12
12
 
13
13
 
14
14
  # Internal utilities
15
- from StreamingCommunity.Src.Util.message import start_message
16
- from StreamingCommunity.Src.Util.console import console, msg
17
- from StreamingCommunity.Src.Util._jsonConfig import config_manager
18
- from StreamingCommunity.Src.Upload.update import update as git_update
19
- from StreamingCommunity.Src.Util.os import os_summary
20
- from StreamingCommunity.Src.Lib.TMBD import tmdb
21
- from StreamingCommunity.Src.Util.logger import Logger
15
+ from StreamingCommunity.Util.message import start_message
16
+ from StreamingCommunity.Util.console import console, msg
17
+ from StreamingCommunity.Util._jsonConfig import config_manager
18
+ from StreamingCommunity.Upload.update import update as git_update
19
+ from StreamingCommunity.Util.os import OsSummary
20
+ from StreamingCommunity.Lib.TMBD import tmdb
21
+ from StreamingCommunity.Util.logger import Logger
22
22
 
23
23
 
24
24
  # Config
@@ -45,8 +45,13 @@ def load_search_functions():
45
45
  modules = []
46
46
  loaded_functions = {}
47
47
 
48
- # Traverse the Api directory
49
- api_dir = os.path.join(os.path.dirname(__file__), 'Src', 'Api', 'Site')
48
+ # Find api home directory
49
+ if getattr(sys, 'frozen', False): # Modalità PyInstaller
50
+ base_path = os.path.join(sys._MEIPASS, "StreamingCommunity")
51
+ else:
52
+ base_path = os.path.dirname(__file__)
53
+
54
+ api_dir = os.path.join(base_path, 'Api', 'Site')
50
55
  init_files = glob.glob(os.path.join(api_dir, '*', '__init__.py'))
51
56
 
52
57
  # Retrieve modules and their indices
@@ -58,7 +63,7 @@ def load_search_functions():
58
63
 
59
64
  try:
60
65
  # Dynamically import the module
61
- mod = importlib.import_module(f'StreamingCommunity.Src.Api.Site.{module_name}')
66
+ mod = importlib.import_module(f'StreamingCommunity.Api.Site.{module_name}')
62
67
 
63
68
  # Get 'indice' from the module
64
69
  indice = getattr(mod, 'indice', 0)
@@ -83,7 +88,7 @@ def load_search_functions():
83
88
  try:
84
89
 
85
90
  # Dynamically import the module
86
- mod = importlib.import_module(f'StreamingCommunity.Src.Api.Site.{module_name}')
91
+ mod = importlib.import_module(f'StreamingCommunity.Api.Site.{module_name}')
87
92
 
88
93
  # Get the search function from the module (assuming the function is named 'search' and defined in __init__.py)
89
94
  search_function = getattr(mod, 'search')
@@ -103,6 +108,7 @@ def initialize():
103
108
  start_message()
104
109
 
105
110
  # Get system info
111
+ os_summary = OsSummary()
106
112
  os_summary.get_system_summary()
107
113
 
108
114
  # Set terminal size for win 7
@@ -178,7 +184,7 @@ def main():
178
184
 
179
185
  # Display the category legend in a single line
180
186
  legend_text = " | ".join([f"[{color}]{category.capitalize()}[/{color}]" for category, color in color_map.items()])
181
- console.print(f"[bold green]Category Legend:[/bold green] {legend_text}")
187
+ console.print(f"\n[bold green]Category Legend:[/bold green] {legend_text}")
182
188
 
183
189
  # Construct the prompt message with color-coded site names
184
190
  prompt_message = "[green]Insert category [white](" + ", ".join(