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.
- StreamingCommunity/{Src/Api → Api}/Player/ddl.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Player/maxstream.py +2 -2
- StreamingCommunity/{Src/Api → Api}/Player/supervideo.py +2 -2
- StreamingCommunity/{Src/Api → Api}/Player/vixcloud.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/1337xx/__init__.py +1 -1
- StreamingCommunity/{Src/Api → Api}/Site/1337xx/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/1337xx/site.py +7 -7
- StreamingCommunity/{Src/Api → Api}/Site/1337xx/title.py +6 -6
- StreamingCommunity/{Src/Api → Api}/Site/altadefinizione/__init__.py +1 -1
- StreamingCommunity/{Src/Api/Site/mostraguarda → Api/Site/altadefinizione}/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/altadefinizione/film.py +8 -8
- StreamingCommunity/{Src/Api → Api}/Site/altadefinizione/site.py +7 -7
- StreamingCommunity/{Src/Api → Api}/Site/animeunity/__init__.py +1 -1
- StreamingCommunity/{Src/Api/Site/altadefinizione → Api/Site/animeunity}/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/animeunity/film_serie.py +18 -19
- StreamingCommunity/{Src/Api → Api}/Site/animeunity/site.py +6 -6
- StreamingCommunity/{Src/Api → Api}/Site/animeunity/util/ScrapeSerie.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/bitsearch/__init__.py +1 -1
- StreamingCommunity/{Src/Api → Api}/Site/bitsearch/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/bitsearch/site.py +7 -7
- StreamingCommunity/{Src/Api → Api}/Site/bitsearch/title.py +5 -5
- StreamingCommunity/{Src/Api → Api}/Site/cb01new/__init__.py +1 -1
- StreamingCommunity/{Src/Api → Api}/Site/cb01new/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/cb01new/film.py +8 -8
- StreamingCommunity/{Src/Api → Api}/Site/cb01new/site.py +6 -6
- StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/__init__.py +1 -1
- StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/series.py +11 -12
- StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/site.py +7 -7
- StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/util/ScrapeSerie.py +5 -3
- StreamingCommunity/{Src/Api → Api}/Site/guardaserie/__init__.py +1 -1
- StreamingCommunity/{Src/Api/Site/piratebays → Api/Site/guardaserie}/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/guardaserie/series.py +11 -11
- StreamingCommunity/{Src/Api → Api}/Site/guardaserie/site.py +7 -7
- StreamingCommunity/{Src/Api/Site/guardaserie/Player → Api/Site/guardaserie/util}/ScrapeSerie.py +2 -2
- StreamingCommunity/{Src/Api → Api}/Site/mostraguarda/__init__.py +2 -2
- StreamingCommunity/{Src/Api/Site/animeunity → Api/Site/mostraguarda}/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/mostraguarda/film.py +9 -9
- StreamingCommunity/{Src/Api → Api}/Site/piratebays/__init__.py +1 -1
- StreamingCommunity/{Src/Api/Site/guardaserie → Api/Site/piratebays}/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/piratebays/site.py +6 -6
- StreamingCommunity/{Src/Api → Api}/Site/piratebays/title.py +5 -5
- StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/__init__.py +1 -1
- StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/film.py +10 -10
- StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/series.py +20 -18
- StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/site.py +7 -7
- StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/util/ScrapeSerie.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Template/Util/get_domain.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Template/Util/manage_ep.py +2 -2
- StreamingCommunity/{Src/Api → Api}/Template/Util/recall_search.py +1 -1
- StreamingCommunity/{Src/Api → Api}/Template/site.py +1 -1
- StreamingCommunity/{Src/Lib → Lib}/Downloader/HLS/downloader.py +4 -4
- StreamingCommunity/{Src/Lib → Lib}/Downloader/HLS/proxyes.py +3 -3
- StreamingCommunity/{Src/Lib → Lib}/Downloader/HLS/segments.py +42 -44
- StreamingCommunity/{Src/Lib → Lib}/Downloader/MP4/downloader.py +5 -5
- StreamingCommunity/{Src/Lib → Lib}/Downloader/TOR/downloader.py +3 -3
- StreamingCommunity/{Src/Lib → Lib}/Driver/driver_1.py +1 -1
- StreamingCommunity/{Src/Lib → Lib}/FFmpeg/capture.py +2 -2
- StreamingCommunity/{Src/Lib → Lib}/FFmpeg/command.py +3 -3
- StreamingCommunity/{Src/Lib → Lib}/FFmpeg/util.py +1 -1
- StreamingCommunity/{Src/Lib → Lib}/M3U8/decryptor.py +59 -24
- StreamingCommunity/{Src/Lib → Lib}/M3U8/estimator.py +6 -3
- StreamingCommunity/{Src/Lib → Lib}/M3U8/parser.py +1 -1
- StreamingCommunity/{Src/Lib → Lib}/TMBD/tmdb.py +1 -1
- StreamingCommunity/{Src/Upload → Upload}/update.py +6 -2
- StreamingCommunity/{Src/Upload → Upload}/version.py +1 -1
- StreamingCommunity/Util/ffmpeg_installer.py +275 -0
- StreamingCommunity/{Src/Util → Util}/headers.py +1 -1
- StreamingCommunity/{Src/Util → Util}/logger.py +1 -1
- StreamingCommunity/{Src/Util → Util}/message.py +2 -2
- StreamingCommunity/{Src/Util → Util}/os.py +118 -21
- StreamingCommunity/run.py +18 -12
- {StreamingCommunity-1.8.0.dist-info → StreamingCommunity-1.9.1.dist-info}/METADATA +126 -60
- StreamingCommunity-1.9.1.dist-info/RECORD +95 -0
- {StreamingCommunity-1.8.0.dist-info → StreamingCommunity-1.9.1.dist-info}/WHEEL +1 -1
- StreamingCommunity/Src/Api/Site/animeunity/anime.py +0 -126
- StreamingCommunity/Src/Api/Site/ddlstreamitaly/Player/ScrapeSerie.py +0 -83
- StreamingCommunity/Src/Api/Site/guardaserie/util/ScrapeSerie.py +0 -110
- StreamingCommunity-1.8.0.dist-info/RECORD +0 -97
- /StreamingCommunity/{Src/Api → Api}/Player/Helper/Vixcloud/js_parser.py +0 -0
- /StreamingCommunity/{Src/Api → Api}/Player/Helper/Vixcloud/util.py +0 -0
- /StreamingCommunity/{Src/Api → Api}/Template/Class/SearchType.py +0 -0
- /StreamingCommunity/{Src/Api → Api}/Template/Util/__init__.py +0 -0
- /StreamingCommunity/{Src/Api → Api}/Template/__init__.py +0 -0
- /StreamingCommunity/{Src/Lib → Lib}/Downloader/__init__.py +0 -0
- /StreamingCommunity/{Src/Lib → Lib}/FFmpeg/__init__.py +0 -0
- /StreamingCommunity/{Src/Lib → Lib}/M3U8/__init__.py +0 -0
- /StreamingCommunity/{Src/Lib → Lib}/M3U8/url_fixer.py +0 -0
- /StreamingCommunity/{Src/Lib → Lib}/TMBD/__init__.py +0 -0
- /StreamingCommunity/{Src/Lib → Lib}/TMBD/obj_tmbd.py +0 -0
- /StreamingCommunity/{Src/Util → Util}/_jsonConfig.py +0 -0
- /StreamingCommunity/{Src/Util → Util}/call_stack.py +0 -0
- /StreamingCommunity/{Src/Util → Util}/color.py +0 -0
- /StreamingCommunity/{Src/Util → Util}/console.py +0 -0
- /StreamingCommunity/{Src/Util → Util}/table.py +0 -0
- {StreamingCommunity-1.8.0.dist-info → StreamingCommunity-1.9.1.dist-info}/LICENSE +0 -0
- {StreamingCommunity-1.8.0.dist-info → StreamingCommunity-1.9.1.dist-info}/entry_points.txt +0 -0
- {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
|
|
@@ -5,8 +5,8 @@ import platform
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
# Internal utilities
|
|
8
|
-
from StreamingCommunity.
|
|
9
|
-
from StreamingCommunity.
|
|
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
|
|
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
|
|
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
|
-
|
|
379
|
-
|
|
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
|
-
#
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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.
|
|
16
|
-
from StreamingCommunity.
|
|
17
|
-
from StreamingCommunity.
|
|
18
|
-
from StreamingCommunity.
|
|
19
|
-
from StreamingCommunity.
|
|
20
|
-
from StreamingCommunity.
|
|
21
|
-
from StreamingCommunity.
|
|
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
|
-
#
|
|
49
|
-
|
|
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.
|
|
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.
|
|
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(
|