StreamingCommunity 3.3.1__py3-none-any.whl → 3.3.3__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/Site/mediasetinfinity/util/get_license.py +28 -1
- StreamingCommunity/Api/Site/raiplay/site.py +6 -4
- StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py +6 -2
- StreamingCommunity/Api/Site/streamingcommunity/site.py +0 -3
- StreamingCommunity/Api/Site/streamingwatch/site.py +0 -3
- StreamingCommunity/Lib/Downloader/DASH/cdm_helpher.py +1 -18
- StreamingCommunity/Lib/Downloader/DASH/downloader.py +18 -14
- StreamingCommunity/Lib/Downloader/HLS/downloader.py +22 -10
- StreamingCommunity/Lib/Downloader/HLS/segments.py +126 -72
- StreamingCommunity/Lib/M3U8/decryptor.py +0 -14
- StreamingCommunity/Lib/M3U8/estimator.py +44 -34
- StreamingCommunity/Lib/TMBD/tmdb.py +0 -12
- StreamingCommunity/Upload/update.py +1 -1
- StreamingCommunity/Upload/version.py +1 -1
- StreamingCommunity/Util/{bento4_installer.py → installer/bento4_install.py} +56 -44
- StreamingCommunity/Util/installer/binary_paths.py +83 -0
- StreamingCommunity/Util/installer/device_install.py +133 -0
- StreamingCommunity/Util/{ffmpeg_installer.py → installer/ffmpeg_install.py} +100 -138
- StreamingCommunity/Util/logger.py +3 -8
- StreamingCommunity/Util/os.py +34 -150
- StreamingCommunity/run.py +2 -3
- {streamingcommunity-3.3.1.dist-info → streamingcommunity-3.3.3.dist-info}/METADATA +295 -532
- {streamingcommunity-3.3.1.dist-info → streamingcommunity-3.3.3.dist-info}/RECORD +27 -28
- StreamingCommunity/Api/Player/ddl.py +0 -82
- StreamingCommunity/Api/Player/maxstream.py +0 -141
- StreamingCommunity/Api/Player/mixdrop.py +0 -146
- {streamingcommunity-3.3.1.dist-info → streamingcommunity-3.3.3.dist-info}/WHEEL +0 -0
- {streamingcommunity-3.3.1.dist-info → streamingcommunity-3.3.3.dist-info}/entry_points.txt +0 -0
- {streamingcommunity-3.3.1.dist-info → streamingcommunity-3.3.3.dist-info}/licenses/LICENSE +0 -0
- {streamingcommunity-3.3.1.dist-info → streamingcommunity-3.3.3.dist-info}/top_level.txt +0 -0
|
@@ -5,7 +5,6 @@ import glob
|
|
|
5
5
|
import gzip
|
|
6
6
|
import shutil
|
|
7
7
|
import logging
|
|
8
|
-
import platform
|
|
9
8
|
import subprocess
|
|
10
9
|
from typing import Optional, Tuple
|
|
11
10
|
|
|
@@ -16,25 +15,26 @@ from rich.console import Console
|
|
|
16
15
|
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn
|
|
17
16
|
|
|
18
17
|
|
|
18
|
+
# Internal utilities
|
|
19
|
+
from .binary_paths import binary_paths
|
|
20
|
+
|
|
21
|
+
|
|
19
22
|
# Variable
|
|
20
23
|
console = Console()
|
|
21
24
|
|
|
22
25
|
|
|
23
26
|
FFMPEG_CONFIGURATION = {
|
|
24
27
|
'windows': {
|
|
25
|
-
'base_dir': lambda home: os.path.join(os.path.splitdrive(home)[0] + os.path.sep, 'binary'),
|
|
26
28
|
'download_url': 'https://github.com/eugeneware/ffmpeg-static/releases/latest/download/ffmpeg-win32-{arch}.gz',
|
|
27
29
|
'file_extension': '.gz',
|
|
28
30
|
'executables': ['ffmpeg-win32-{arch}', 'ffprobe-win32-{arch}']
|
|
29
31
|
},
|
|
30
32
|
'darwin': {
|
|
31
|
-
'base_dir': lambda home: os.path.join(home, 'Applications', 'binary'),
|
|
32
33
|
'download_url': 'https://github.com/eugeneware/ffmpeg-static/releases/latest/download/ffmpeg-darwin-{arch}.gz',
|
|
33
34
|
'file_extension': '.gz',
|
|
34
35
|
'executables': ['ffmpeg-darwin-{arch}', 'ffprobe-darwin-{arch}']
|
|
35
36
|
},
|
|
36
37
|
'linux': {
|
|
37
|
-
'base_dir': lambda home: os.path.join(home, '.local', 'bin', 'binary'),
|
|
38
38
|
'download_url': 'https://github.com/eugeneware/ffmpeg-static/releases/latest/download/ffmpeg-linux-{arch}.gz',
|
|
39
39
|
'file_extension': '.gz',
|
|
40
40
|
'executables': ['ffmpeg-linux-{arch}', 'ffprobe-linux-{arch}']
|
|
@@ -44,88 +44,98 @@ FFMPEG_CONFIGURATION = {
|
|
|
44
44
|
|
|
45
45
|
class FFMPEGDownloader:
|
|
46
46
|
def __init__(self):
|
|
47
|
-
self.os_name =
|
|
48
|
-
self.arch =
|
|
49
|
-
self.home_dir =
|
|
50
|
-
self.base_dir =
|
|
47
|
+
self.os_name = binary_paths.system
|
|
48
|
+
self.arch = binary_paths.arch
|
|
49
|
+
self.home_dir = binary_paths.home_dir
|
|
50
|
+
self.base_dir = binary_paths.ensure_binary_directory()
|
|
51
51
|
|
|
52
|
-
def
|
|
52
|
+
def _check_existing_binaries(self) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
53
53
|
"""
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
Check if FFmpeg binaries already exist.
|
|
55
|
+
Order: system PATH (where/which) -> binary directory
|
|
56
|
+
|
|
56
57
|
Returns:
|
|
57
|
-
str
|
|
58
|
-
"""
|
|
59
|
-
system = platform.system().lower()
|
|
60
|
-
if system in FFMPEG_CONFIGURATION:
|
|
61
|
-
return system
|
|
62
|
-
raise ValueError(f"Unsupported operating system: {system}")
|
|
63
|
-
|
|
64
|
-
def _detect_arch(self) -> str:
|
|
58
|
+
Tuple[Optional[str], Optional[str], Optional[str]]: Paths to ffmpeg, ffprobe, ffplay
|
|
65
59
|
"""
|
|
66
|
-
|
|
60
|
+
try:
|
|
67
61
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
62
|
+
# STEP 1: Check system PATH first
|
|
63
|
+
console.print("[cyan]Checking for FFmpeg in system PATH...[/]")
|
|
64
|
+
if self.os_name == 'windows':
|
|
65
|
+
try:
|
|
66
|
+
ffmpeg_path = subprocess.check_output(
|
|
67
|
+
['where', 'ffmpeg'], stderr=subprocess.DEVNULL, text=True
|
|
68
|
+
).strip().split('\n')[0]
|
|
69
|
+
|
|
70
|
+
ffprobe_path = subprocess.check_output(
|
|
71
|
+
['where', 'ffprobe'], stderr=subprocess.DEVNULL, text=True
|
|
72
|
+
).strip().split('\n')[0]
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
ffplay_path = subprocess.check_output(
|
|
76
|
+
['where', 'ffplay'], stderr=subprocess.DEVNULL, text=True
|
|
77
|
+
).strip().split('\n')[0]
|
|
78
|
+
except subprocess.CalledProcessError:
|
|
79
|
+
ffplay_path = None
|
|
80
|
+
|
|
81
|
+
return ffmpeg_path, ffprobe_path, ffplay_path
|
|
82
|
+
|
|
83
|
+
except subprocess.CalledProcessError:
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
else:
|
|
87
|
+
ffmpeg_path = shutil.which('ffmpeg')
|
|
88
|
+
ffprobe_path = shutil.which('ffprobe')
|
|
89
|
+
ffplay_path = shutil.which('ffplay')
|
|
90
|
+
|
|
91
|
+
if ffmpeg_path and ffprobe_path:
|
|
92
|
+
return ffmpeg_path, ffprobe_path, ffplay_path
|
|
87
93
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return base_dir
|
|
94
|
+
# STEP 2: Check in binary directory
|
|
95
|
+
console.print("[cyan]Checking for FFmpeg in binary directory...[/]")
|
|
96
|
+
config = FFMPEG_CONFIGURATION[self.os_name]
|
|
97
|
+
executables = [exe.format(arch=self.arch) for exe in config['executables']]
|
|
98
|
+
found_executables = []
|
|
94
99
|
|
|
95
|
-
def _check_existing_binaries(self) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
96
|
-
"""
|
|
97
|
-
Check if FFmpeg binaries already exist in the base directory.
|
|
98
|
-
Enhanced to check both the binary directory and system paths on macOS.
|
|
99
|
-
"""
|
|
100
|
-
config = FFMPEG_CONFIGURATION[self.os_name]
|
|
101
|
-
executables = config['executables']
|
|
102
|
-
found_executables = []
|
|
103
|
-
|
|
104
|
-
# For macOS, check both binary directory and system paths
|
|
105
|
-
if self.os_name == 'darwin':
|
|
106
|
-
potential_paths = [
|
|
107
|
-
'/usr/local/bin',
|
|
108
|
-
'/opt/homebrew/bin',
|
|
109
|
-
'/usr/bin',
|
|
110
|
-
self.base_dir
|
|
111
|
-
]
|
|
112
|
-
|
|
113
|
-
for executable in executables:
|
|
114
|
-
found = None
|
|
115
|
-
for path in potential_paths:
|
|
116
|
-
full_path = os.path.join(path, executable)
|
|
117
|
-
if os.path.exists(full_path) and os.access(full_path, os.X_OK):
|
|
118
|
-
found = full_path
|
|
119
|
-
break
|
|
120
|
-
found_executables.append(found)
|
|
121
|
-
else:
|
|
122
|
-
|
|
123
|
-
# Original behavior for other operating systems
|
|
124
100
|
for executable in executables:
|
|
125
|
-
exe_paths = glob.glob(os.path.join(self.base_dir, executable))
|
|
126
|
-
found_executables.append(exe_paths[0] if exe_paths else None)
|
|
127
101
|
|
|
128
|
-
|
|
102
|
+
# Check for exact match first
|
|
103
|
+
exe_paths = glob.glob(os.path.join(self.base_dir, executable))
|
|
104
|
+
if exe_paths:
|
|
105
|
+
found_executables.append(exe_paths[0])
|
|
106
|
+
|
|
107
|
+
else:
|
|
108
|
+
# Check for standard names
|
|
109
|
+
if self.os_name == 'windows':
|
|
110
|
+
if 'ffmpeg' in executable:
|
|
111
|
+
standard_path = os.path.join(self.base_dir, 'ffmpeg.exe')
|
|
112
|
+
elif 'ffprobe' in executable:
|
|
113
|
+
standard_path = os.path.join(self.base_dir, 'ffprobe.exe')
|
|
114
|
+
else:
|
|
115
|
+
standard_path = None
|
|
116
|
+
else:
|
|
117
|
+
if 'ffmpeg' in executable:
|
|
118
|
+
standard_path = os.path.join(self.base_dir, 'ffmpeg')
|
|
119
|
+
elif 'ffprobe' in executable:
|
|
120
|
+
standard_path = os.path.join(self.base_dir, 'ffprobe')
|
|
121
|
+
else:
|
|
122
|
+
standard_path = None
|
|
123
|
+
|
|
124
|
+
if standard_path and os.path.exists(standard_path):
|
|
125
|
+
found_executables.append(standard_path)
|
|
126
|
+
else:
|
|
127
|
+
found_executables.append(None)
|
|
128
|
+
|
|
129
|
+
# Return found executables if we have at least ffmpeg and ffprobe
|
|
130
|
+
if len(found_executables) >= 2 and found_executables[0] and found_executables[1]:
|
|
131
|
+
ffplay_path = found_executables[2] if len(found_executables) > 2 else None
|
|
132
|
+
return found_executables[0], found_executables[1], ffplay_path
|
|
133
|
+
|
|
134
|
+
return (None, None, None)
|
|
135
|
+
|
|
136
|
+
except Exception as e:
|
|
137
|
+
logging.error(f"Error checking existing binaries: {e}")
|
|
138
|
+
return (None, None, None)
|
|
129
139
|
|
|
130
140
|
def _get_latest_version(self, repo: str) -> Optional[str]:
|
|
131
141
|
"""
|
|
@@ -253,7 +263,6 @@ class FFMPEGDownloader:
|
|
|
253
263
|
ffprobe_path = shutil.which('ffprobe')
|
|
254
264
|
|
|
255
265
|
if ffmpeg_path and ffprobe_path:
|
|
256
|
-
console.print("[bold green]FFmpeg successfully installed via apt[/]")
|
|
257
266
|
return ffmpeg_path, ffprobe_path, None
|
|
258
267
|
else:
|
|
259
268
|
console.print("[bold yellow]Failed to install FFmpeg via apt. Proceeding with static download.[/]")
|
|
@@ -285,7 +294,6 @@ class FFMPEGDownloader:
|
|
|
285
294
|
# Extract the file
|
|
286
295
|
if self._extract_file(download_path, final_path):
|
|
287
296
|
successful_extractions.append(final_path)
|
|
288
|
-
console.print(f"[bold green]Successfully installed {executable}[/]")
|
|
289
297
|
else:
|
|
290
298
|
console.print(f"[bold red]Failed to extract {executable}[/]")
|
|
291
299
|
|
|
@@ -304,71 +312,25 @@ class FFMPEGDownloader:
|
|
|
304
312
|
def check_ffmpeg() -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
305
313
|
"""
|
|
306
314
|
Check for FFmpeg executables in the system and download them if not found.
|
|
307
|
-
|
|
308
|
-
|
|
315
|
+
Order: system PATH (where/which) -> binary directory -> download
|
|
316
|
+
|
|
309
317
|
Returns:
|
|
310
|
-
Tuple[Optional[str], Optional[str], Optional[str]]: Paths to ffmpeg, ffprobe, and ffplay
|
|
318
|
+
Tuple[Optional[str], Optional[str], Optional[str]]: Paths to ffmpeg, ffprobe, and ffplay
|
|
311
319
|
"""
|
|
312
320
|
try:
|
|
313
|
-
|
|
321
|
+
# Create downloader instance to use its existing check method
|
|
322
|
+
downloader = FFMPEGDownloader()
|
|
314
323
|
|
|
315
|
-
#
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
'/opt/homebrew/bin', # Apple Silicon Homebrew
|
|
322
|
-
'/usr/bin', # System default
|
|
323
|
-
os.path.expanduser('~/Applications/binary'), # Custom installation
|
|
324
|
-
'/Applications/binary' # Custom installation
|
|
325
|
-
]
|
|
326
|
-
|
|
327
|
-
for path in potential_paths:
|
|
328
|
-
ffmpeg_path = os.path.join(path, 'ffmpeg')
|
|
329
|
-
ffprobe_path = os.path.join(path, 'ffprobe')
|
|
330
|
-
ffplay_path = os.path.join(path, 'ffplay')
|
|
331
|
-
|
|
332
|
-
if (os.path.exists(ffmpeg_path) and os.path.exists(ffprobe_path) and
|
|
333
|
-
os.access(ffmpeg_path, os.X_OK) and os.access(ffprobe_path, os.X_OK)):
|
|
334
|
-
|
|
335
|
-
# Return found executables, with ffplay being optional
|
|
336
|
-
ffplay_path = ffplay_path if os.path.exists(ffplay_path) else None
|
|
337
|
-
return ffmpeg_path, ffprobe_path, ffplay_path
|
|
338
|
-
|
|
339
|
-
# Windows detection
|
|
340
|
-
elif system_platform == 'windows':
|
|
341
|
-
try:
|
|
342
|
-
ffmpeg_path = subprocess.check_output(
|
|
343
|
-
['where', 'ffmpeg'], stderr=subprocess.DEVNULL, text=True
|
|
344
|
-
).strip().split('\n')[0]
|
|
345
|
-
|
|
346
|
-
ffprobe_path = subprocess.check_output(
|
|
347
|
-
['where', 'ffprobe'], stderr=subprocess.DEVNULL, text=True
|
|
348
|
-
).strip().split('\n')[0]
|
|
349
|
-
|
|
350
|
-
ffplay_path = subprocess.check_output(
|
|
351
|
-
['where', 'ffplay'], stderr=subprocess.DEVNULL, text=True
|
|
352
|
-
).strip().split('\n')[0]
|
|
353
|
-
|
|
354
|
-
return ffmpeg_path, ffprobe_path, ffplay_path
|
|
355
|
-
|
|
356
|
-
except subprocess.CalledProcessError:
|
|
357
|
-
logging.warning("One or more FFmpeg binaries were not found with command where")
|
|
358
|
-
|
|
359
|
-
# Linux detection
|
|
360
|
-
else:
|
|
361
|
-
ffmpeg_path = shutil.which('ffmpeg')
|
|
362
|
-
ffprobe_path = shutil.which('ffprobe')
|
|
363
|
-
ffplay_path = shutil.which('ffplay')
|
|
364
|
-
|
|
365
|
-
if ffmpeg_path and ffprobe_path:
|
|
366
|
-
return ffmpeg_path, ffprobe_path, ffplay_path
|
|
324
|
+
# STEP 1 & 2: Check existing binaries (system PATH + binary directory)
|
|
325
|
+
ffmpeg_path, ffprobe_path, ffplay_path = downloader._check_existing_binaries()
|
|
326
|
+
|
|
327
|
+
# If found, return them
|
|
328
|
+
if ffmpeg_path and ffprobe_path:
|
|
329
|
+
return ffmpeg_path, ffprobe_path, ffplay_path
|
|
367
330
|
|
|
368
|
-
#
|
|
369
|
-
downloader = FFMPEGDownloader()
|
|
331
|
+
# STEP 3: Download if not found
|
|
370
332
|
return downloader.download()
|
|
371
333
|
|
|
372
334
|
except Exception as e:
|
|
373
335
|
logging.error(f"Error checking or downloading FFmpeg executables: {e}")
|
|
374
|
-
return None, None, None
|
|
336
|
+
return None, None, None
|
|
@@ -13,7 +13,6 @@ class Logger:
|
|
|
13
13
|
_instance = None
|
|
14
14
|
|
|
15
15
|
def __new__(cls):
|
|
16
|
-
# Singleton pattern to avoid multiple logger instances
|
|
17
16
|
if cls._instance is None:
|
|
18
17
|
cls._instance = super(Logger, cls).__new__(cls)
|
|
19
18
|
cls._instance._initialized = False
|
|
@@ -24,10 +23,8 @@ class Logger:
|
|
|
24
23
|
if getattr(self, '_initialized', False):
|
|
25
24
|
return
|
|
26
25
|
|
|
27
|
-
# Fetch only the debug setting from config
|
|
28
|
-
self.debug_mode = config_manager.get_bool("DEFAULT", "debug")
|
|
29
|
-
|
|
30
26
|
# Configure root logger
|
|
27
|
+
self.debug_mode = config_manager.get_bool('DEFAULT', "debug")
|
|
31
28
|
self.logger = logging.getLogger('')
|
|
32
29
|
|
|
33
30
|
# Remove any existing handlers to avoid duplication
|
|
@@ -45,16 +42,14 @@ class Logger:
|
|
|
45
42
|
|
|
46
43
|
else:
|
|
47
44
|
self.logger.setLevel(logging.ERROR)
|
|
48
|
-
|
|
49
|
-
# Configure console logging (terminal output) regardless of debug mode
|
|
50
|
-
self._configure_console_logging()
|
|
45
|
+
self._configure_console_logging()
|
|
51
46
|
|
|
52
47
|
self._initialized = True
|
|
53
48
|
|
|
54
49
|
def _configure_console_logging(self):
|
|
55
50
|
"""Configure console logging output to terminal."""
|
|
56
51
|
console_handler = logging.StreamHandler()
|
|
57
|
-
console_handler.setLevel(logging.
|
|
52
|
+
console_handler.setLevel(logging.ERROR)
|
|
58
53
|
formatter = logging.Formatter('[%(filename)s:%(lineno)s - %(funcName)20s() ] %(asctime)s - %(levelname)s - %(message)s')
|
|
59
54
|
console_handler.setFormatter(formatter)
|
|
60
55
|
self.logger.addHandler(console_handler)
|
StreamingCommunity/Util/os.py
CHANGED
|
@@ -2,16 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import io
|
|
4
4
|
import os
|
|
5
|
-
import
|
|
6
|
-
import sys
|
|
5
|
+
import time
|
|
7
6
|
import shutil
|
|
8
7
|
import logging
|
|
8
|
+
import socket
|
|
9
9
|
import platform
|
|
10
10
|
import inspect
|
|
11
|
-
import subprocess
|
|
12
11
|
import contextlib
|
|
13
|
-
import importlib.metadata
|
|
14
|
-
import socket
|
|
15
12
|
|
|
16
13
|
|
|
17
14
|
# External library
|
|
@@ -22,8 +19,9 @@ from pathvalidate import sanitize_filename, sanitize_filepath
|
|
|
22
19
|
|
|
23
20
|
|
|
24
21
|
# Internal utilities
|
|
25
|
-
from .
|
|
26
|
-
from .
|
|
22
|
+
from .installer.ffmpeg_install import check_ffmpeg
|
|
23
|
+
from .installer.bento4_install import check_mp4decrypt
|
|
24
|
+
from .installer.device_install import check_wvd_path
|
|
27
25
|
|
|
28
26
|
|
|
29
27
|
# Variable
|
|
@@ -243,7 +241,7 @@ class OsManager:
|
|
|
243
241
|
return False
|
|
244
242
|
|
|
245
243
|
|
|
246
|
-
class
|
|
244
|
+
class InternetManager():
|
|
247
245
|
def format_file_size(self, size_bytes: float) -> str:
|
|
248
246
|
"""
|
|
249
247
|
Formats a file size from bytes into a human-readable string representation.
|
|
@@ -305,144 +303,46 @@ class InternManager():
|
|
|
305
303
|
except (socket.gaierror, socket.error):
|
|
306
304
|
return False
|
|
307
305
|
|
|
306
|
+
|
|
308
307
|
class OsSummary:
|
|
309
308
|
def __init__(self):
|
|
310
309
|
self.ffmpeg_path = None
|
|
311
310
|
self.ffprobe_path = None
|
|
312
311
|
self.ffplay_path = None
|
|
313
312
|
self.mp4decrypt_path = None
|
|
313
|
+
self.wvd_path = None
|
|
314
|
+
self.init()
|
|
314
315
|
|
|
315
|
-
def
|
|
316
|
-
"""Get the binary directory based on OS."""
|
|
317
|
-
system = platform.system().lower()
|
|
318
|
-
home = os.path.expanduser('~')
|
|
319
|
-
|
|
320
|
-
if system == 'windows':
|
|
321
|
-
return os.path.join(os.path.splitdrive(home)[0] + os.path.sep, 'binary')
|
|
322
|
-
elif system == 'darwin':
|
|
323
|
-
return os.path.join(home, 'Applications', 'binary')
|
|
324
|
-
else: # linux
|
|
325
|
-
return os.path.join(home, '.local', 'bin', 'binary')
|
|
326
|
-
|
|
327
|
-
def check_ffmpeg_location(self, command: list) -> str:
|
|
328
|
-
"""
|
|
329
|
-
Check if a specific executable (ffmpeg or ffprobe) is located using the given command.
|
|
330
|
-
Returns the path of the executable or None if not found.
|
|
331
|
-
"""
|
|
332
|
-
try:
|
|
333
|
-
result = subprocess.check_output(command, text=True).strip()
|
|
334
|
-
return result.split('\n')[0] if result else None
|
|
335
|
-
|
|
336
|
-
except subprocess.CalledProcessError:
|
|
337
|
-
return None
|
|
338
|
-
|
|
339
|
-
def get_library_version(self, lib_name: str):
|
|
340
|
-
"""
|
|
341
|
-
Retrieve the version of a Python library.
|
|
342
|
-
|
|
343
|
-
Args:
|
|
344
|
-
lib_name (str): The name of the Python library.
|
|
345
|
-
|
|
346
|
-
Returns:
|
|
347
|
-
str: The library name followed by its version, or `-not installed` if not found.
|
|
348
|
-
"""
|
|
349
|
-
try:
|
|
350
|
-
version = importlib.metadata.version(lib_name)
|
|
351
|
-
return f"{lib_name}-{version}"
|
|
352
|
-
|
|
353
|
-
except importlib.metadata.PackageNotFoundError:
|
|
354
|
-
return f"{lib_name}-not installed"
|
|
355
|
-
|
|
356
|
-
def install_library(self, lib_name: str):
|
|
357
|
-
"""
|
|
358
|
-
Install a Python library using pip.
|
|
359
|
-
|
|
360
|
-
Args:
|
|
361
|
-
lib_name (str): The name of the library to install.
|
|
362
|
-
"""
|
|
363
|
-
try:
|
|
364
|
-
console.print(f"Installing {lib_name}...", style="bold yellow")
|
|
365
|
-
subprocess.check_call([sys.executable, "-m", "pip", "install", lib_name])
|
|
366
|
-
console.print(f"{lib_name} installed successfully!", style="bold green")
|
|
367
|
-
|
|
368
|
-
except subprocess.CalledProcessError as e:
|
|
369
|
-
console.print(f"Failed to install {lib_name}: {e}", style="bold red")
|
|
370
|
-
sys.exit(1)
|
|
371
|
-
|
|
372
|
-
def check_python_version(self):
|
|
373
|
-
"""
|
|
374
|
-
Check if the installed Python is the official CPython distribution.
|
|
375
|
-
Exits with a message if not the official version.
|
|
376
|
-
"""
|
|
377
|
-
python_implementation = platform.python_implementation()
|
|
378
|
-
python_version = platform.python_version()
|
|
379
|
-
|
|
380
|
-
if python_implementation != "CPython":
|
|
381
|
-
console.print(f"[bold red]Warning: You are using a non-official Python distribution: {python_implementation}.[/bold red]")
|
|
382
|
-
console.print("Please install the official Python from [bold blue]https://www.python.org[/bold blue] and try again.", style="bold yellow")
|
|
383
|
-
sys.exit(0)
|
|
384
|
-
|
|
385
|
-
console.print(f"[cyan]Python version: [bold red]{python_version}[/bold red]")
|
|
386
|
-
|
|
387
|
-
def get_system_summary(self):
|
|
388
|
-
self.check_python_version()
|
|
389
|
-
|
|
390
|
-
# FFmpeg detection
|
|
391
|
-
binary_dir = self.get_binary_directory()
|
|
392
|
-
system = platform.system().lower()
|
|
393
|
-
arch = platform.machine().lower()
|
|
394
|
-
|
|
395
|
-
# Map architecture names
|
|
396
|
-
arch_map = {
|
|
397
|
-
'amd64': 'x64',
|
|
398
|
-
'x86_64': 'x64',
|
|
399
|
-
'x64': 'x64',
|
|
400
|
-
'arm64': 'arm64',
|
|
401
|
-
'aarch64': 'arm64',
|
|
402
|
-
'armv7l': 'arm',
|
|
403
|
-
'i386': 'ia32',
|
|
404
|
-
'i686': 'ia32'
|
|
405
|
-
}
|
|
406
|
-
arch = arch_map.get(arch, arch)
|
|
407
|
-
|
|
408
|
-
# Check FFmpeg binaries
|
|
409
|
-
if os.path.exists(binary_dir):
|
|
410
|
-
ffmpeg_files = glob.glob(os.path.join(binary_dir, f'*ffmpeg*{arch}*'))
|
|
411
|
-
ffprobe_files = glob.glob(os.path.join(binary_dir, f'*ffprobe*{arch}*'))
|
|
316
|
+
def init(self):
|
|
412
317
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
self.ffprobe_path = ffprobe_files[0]
|
|
416
|
-
|
|
417
|
-
if system != 'windows':
|
|
418
|
-
os.chmod(self.ffmpeg_path, 0o755)
|
|
419
|
-
os.chmod(self.ffprobe_path, 0o755)
|
|
420
|
-
else:
|
|
421
|
-
self.ffmpeg_path, self.ffprobe_path, self.ffplay_path = check_ffmpeg()
|
|
422
|
-
else:
|
|
423
|
-
self.ffmpeg_path, self.ffprobe_path, self.ffplay_path = check_ffmpeg()
|
|
424
|
-
|
|
425
|
-
# Check mp4decrypt
|
|
318
|
+
# Check for binaries
|
|
319
|
+
self.ffmpeg_path, self.ffprobe_path, _ = check_ffmpeg()
|
|
426
320
|
self.mp4decrypt_path = check_mp4decrypt()
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
321
|
+
self.wvd_path = check_wvd_path()
|
|
322
|
+
|
|
323
|
+
self._display_binary_paths()
|
|
324
|
+
time.sleep(0.3)
|
|
325
|
+
|
|
326
|
+
def _display_binary_paths(self):
|
|
327
|
+
"""Display the paths of all detected binaries."""
|
|
328
|
+
paths = {
|
|
329
|
+
'ffmpeg': self.ffmpeg_path,
|
|
330
|
+
'ffprobe': self.ffprobe_path,
|
|
331
|
+
'mp4decrypt': self.mp4decrypt_path,
|
|
332
|
+
'wvd': self.wvd_path
|
|
333
|
+
}
|
|
434
334
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
wvd_str = f"'{wvd_path}'" if wvd_path else "None"
|
|
335
|
+
path_strings = []
|
|
336
|
+
for name, path in paths.items():
|
|
337
|
+
path_str = f"'{path}'" if path else "None"
|
|
338
|
+
path_strings.append(f"[red]{name} [bold yellow]{path_str}[/bold yellow]")
|
|
440
339
|
|
|
441
|
-
console.print(f"[cyan]Path:
|
|
340
|
+
console.print(f"[cyan]Path: {', [white]'.join(path_strings)}")
|
|
442
341
|
|
|
443
342
|
|
|
343
|
+
# Initialize the os_summary, internet_manager, and os_manager when the module is imported
|
|
444
344
|
os_manager = OsManager()
|
|
445
|
-
internet_manager =
|
|
345
|
+
internet_manager = InternetManager()
|
|
446
346
|
os_summary = OsSummary()
|
|
447
347
|
|
|
448
348
|
|
|
@@ -487,21 +387,5 @@ def get_mp4decrypt_path():
|
|
|
487
387
|
return os_summary.mp4decrypt_path
|
|
488
388
|
|
|
489
389
|
def get_wvd_path():
|
|
490
|
-
"""
|
|
491
|
-
|
|
492
|
-
Returns None if not found.
|
|
493
|
-
"""
|
|
494
|
-
system = platform.system().lower()
|
|
495
|
-
home = os.path.expanduser('~')
|
|
496
|
-
if system == 'windows':
|
|
497
|
-
binary_dir = os.path.join(os.path.splitdrive(home)[0] + os.path.sep, 'binary')
|
|
498
|
-
elif system == 'darwin':
|
|
499
|
-
binary_dir = os.path.join(home, 'Applications', 'binary')
|
|
500
|
-
else:
|
|
501
|
-
binary_dir = os.path.join(home, '.local', 'bin', 'binary')
|
|
502
|
-
if not os.path.exists(binary_dir):
|
|
503
|
-
return None
|
|
504
|
-
for file in os.listdir(binary_dir):
|
|
505
|
-
if file.lower().endswith('wvd'):
|
|
506
|
-
return os.path.join(binary_dir, file)
|
|
507
|
-
return None
|
|
390
|
+
"""Returns the path of wvd."""
|
|
391
|
+
return os_summary.wvd_path
|
StreamingCommunity/run.py
CHANGED
|
@@ -24,7 +24,7 @@ from rich.prompt import Prompt
|
|
|
24
24
|
from .global_search import global_search
|
|
25
25
|
from StreamingCommunity.Util.message import start_message
|
|
26
26
|
from StreamingCommunity.Util.config_json import config_manager
|
|
27
|
-
from StreamingCommunity.Util.os import
|
|
27
|
+
from StreamingCommunity.Util.os import internet_manager, os_manager
|
|
28
28
|
from StreamingCommunity.Util.logger import Logger
|
|
29
29
|
from StreamingCommunity.Lib.TMBD import tmdb
|
|
30
30
|
from StreamingCommunity.Upload.update import update as git_update
|
|
@@ -97,7 +97,6 @@ def load_search_functions() -> Dict[str, Tuple]:
|
|
|
97
97
|
def initialize():
|
|
98
98
|
"""Initialize the application with system checks and setup."""
|
|
99
99
|
start_message()
|
|
100
|
-
os_summary.get_system_summary()
|
|
101
100
|
|
|
102
101
|
# Windows 7 terminal size fix
|
|
103
102
|
if platform.system() == "Windows" and "7" in platform.version():
|
|
@@ -107,7 +106,7 @@ def initialize():
|
|
|
107
106
|
if sys.version_info < (3, 7):
|
|
108
107
|
console.log("[red]Install python version > 3.7.16")
|
|
109
108
|
sys.exit(0)
|
|
110
|
-
|
|
109
|
+
|
|
111
110
|
# Show trending content
|
|
112
111
|
if SHOW_TRENDING:
|
|
113
112
|
print()
|