StreamingCommunity 3.3.0__py3-none-any.whl → 3.3.2__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/altadefinizione/__init__.py +37 -17
- StreamingCommunity/Api/Site/animeunity/__init__.py +36 -16
- StreamingCommunity/Api/Site/animeworld/__init__.py +50 -6
- StreamingCommunity/Api/Site/crunchyroll/__init__.py +42 -16
- StreamingCommunity/Api/Site/crunchyroll/site.py +1 -1
- StreamingCommunity/Api/Site/guardaserie/__init__.py +50 -6
- StreamingCommunity/Api/Site/mediasetinfinity/__init__.py +43 -5
- StreamingCommunity/Api/Site/mediasetinfinity/film.py +1 -1
- StreamingCommunity/Api/Site/mediasetinfinity/site.py +6 -3
- StreamingCommunity/Api/Site/mediasetinfinity/util/ScrapeSerie.py +6 -7
- StreamingCommunity/Api/Site/mediasetinfinity/util/get_license.py +189 -0
- StreamingCommunity/Api/Site/raiplay/__init__.py +45 -14
- StreamingCommunity/Api/Site/raiplay/series.py +9 -5
- StreamingCommunity/Api/Site/raiplay/site.py +6 -4
- StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py +6 -2
- StreamingCommunity/Api/Site/streamingcommunity/__init__.py +7 -2
- StreamingCommunity/Api/Site/streamingcommunity/site.py +0 -3
- StreamingCommunity/Api/Site/streamingwatch/__init__.py +44 -14
- StreamingCommunity/Api/Site/streamingwatch/site.py +0 -3
- StreamingCommunity/Lib/Downloader/DASH/cdm_helpher.py +1 -18
- StreamingCommunity/Lib/Downloader/DASH/downloader.py +88 -52
- StreamingCommunity/Lib/Downloader/HLS/downloader.py +38 -14
- StreamingCommunity/Lib/Downloader/HLS/segments.py +1 -1
- StreamingCommunity/Lib/FFmpeg/command.py +66 -7
- StreamingCommunity/Lib/FFmpeg/util.py +16 -13
- StreamingCommunity/Lib/M3U8/decryptor.py +0 -14
- StreamingCommunity/Lib/TMBD/tmdb.py +0 -12
- StreamingCommunity/Upload/version.py +1 -1
- StreamingCommunity/Util/{bento4_installer.py → installer/bento4_install.py} +15 -33
- StreamingCommunity/Util/installer/binary_paths.py +83 -0
- StreamingCommunity/Util/{ffmpeg_installer.py → installer/ffmpeg_install.py} +11 -54
- StreamingCommunity/Util/logger.py +3 -8
- StreamingCommunity/Util/os.py +67 -68
- StreamingCommunity/run.py +1 -1
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.2.dist-info}/METADATA +313 -498
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.2.dist-info}/RECORD +40 -39
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.2.dist-info}/WHEEL +0 -0
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.2.dist-info}/entry_points.txt +0 -0
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.2.dist-info}/licenses/LICENSE +0 -0
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.2.dist-info}/top_level.txt +0 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# 18.07.25
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
-
import platform
|
|
5
|
-
import logging
|
|
6
4
|
import shutil
|
|
7
5
|
import zipfile
|
|
6
|
+
import logging
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
# External library
|
|
@@ -13,12 +12,15 @@ from rich.console import Console
|
|
|
13
12
|
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn
|
|
14
13
|
|
|
15
14
|
|
|
15
|
+
# Internal utilities
|
|
16
|
+
from .binary_paths import binary_paths
|
|
17
|
+
|
|
18
|
+
|
|
16
19
|
# Variable
|
|
17
20
|
console = Console()
|
|
18
21
|
|
|
19
22
|
BENTO4_CONFIGURATION = {
|
|
20
23
|
'windows': {
|
|
21
|
-
'base_dir': lambda home: os.path.join(os.path.splitdrive(home)[0] + os.sep, 'binary'),
|
|
22
24
|
'download_url': 'https://www.bok.net/Bento4/binaries/Bento4-SDK-{version}.{platform}.zip',
|
|
23
25
|
'versions': {
|
|
24
26
|
'x64': 'x86_64-microsoft-win32',
|
|
@@ -27,7 +29,6 @@ BENTO4_CONFIGURATION = {
|
|
|
27
29
|
'executables': ['mp4decrypt.exe']
|
|
28
30
|
},
|
|
29
31
|
'darwin': {
|
|
30
|
-
'base_dir': lambda home: os.path.join(home, 'Applications', 'binary'),
|
|
31
32
|
'download_url': 'https://www.bok.net/Bento4/binaries/Bento4-SDK-{version}.{platform}.zip',
|
|
32
33
|
'versions': {
|
|
33
34
|
'x64': 'universal-apple-macosx',
|
|
@@ -36,7 +37,6 @@ BENTO4_CONFIGURATION = {
|
|
|
36
37
|
'executables': ['mp4decrypt']
|
|
37
38
|
},
|
|
38
39
|
'linux': {
|
|
39
|
-
'base_dir': lambda home: os.path.join(home, '.local', 'bin', 'binary'),
|
|
40
40
|
'download_url': 'https://www.bok.net/Bento4/binaries/Bento4-SDK-{version}.{platform}.zip',
|
|
41
41
|
'versions': {
|
|
42
42
|
'x64': 'x86_64-unknown-linux',
|
|
@@ -50,26 +50,11 @@ BENTO4_CONFIGURATION = {
|
|
|
50
50
|
|
|
51
51
|
class Bento4Downloader:
|
|
52
52
|
def __init__(self):
|
|
53
|
-
self.os_name =
|
|
54
|
-
self.arch =
|
|
55
|
-
self.home_dir =
|
|
56
|
-
self.base_dir =
|
|
53
|
+
self.os_name = binary_paths.system
|
|
54
|
+
self.arch = binary_paths.arch
|
|
55
|
+
self.home_dir = binary_paths.home_dir
|
|
56
|
+
self.base_dir = binary_paths.ensure_binary_directory()
|
|
57
57
|
self.version = "1-6-0-641" # Latest stable version as of Nov 2023
|
|
58
|
-
os.makedirs(self.base_dir, exist_ok=True)
|
|
59
|
-
|
|
60
|
-
def _detect_arch(self) -> str:
|
|
61
|
-
machine = platform.machine().lower()
|
|
62
|
-
arch_map = {
|
|
63
|
-
'amd64': 'x64',
|
|
64
|
-
'x86_64': 'x64',
|
|
65
|
-
'x64': 'x64',
|
|
66
|
-
'arm64': 'arm64',
|
|
67
|
-
'aarch64': 'arm64',
|
|
68
|
-
'x86': 'x86',
|
|
69
|
-
'i386': 'x86',
|
|
70
|
-
'i686': 'x86'
|
|
71
|
-
}
|
|
72
|
-
return arch_map.get(machine, machine)
|
|
73
58
|
|
|
74
59
|
def _download_file(self, url: str, destination: str) -> bool:
|
|
75
60
|
try:
|
|
@@ -160,32 +145,29 @@ class Bento4Downloader:
|
|
|
160
145
|
console.print(f"[bold red]Error downloading Bento4: {str(e)}[/]")
|
|
161
146
|
return []
|
|
162
147
|
|
|
148
|
+
|
|
163
149
|
def check_mp4decrypt() -> str:
|
|
164
150
|
"""Check for mp4decrypt in the system and download if not found."""
|
|
165
151
|
try:
|
|
166
152
|
# First check if mp4decrypt is in PATH
|
|
167
|
-
mp4decrypt = "mp4decrypt.exe" if
|
|
153
|
+
mp4decrypt = "mp4decrypt.exe" if binary_paths.system == "windows" else "mp4decrypt"
|
|
168
154
|
mp4decrypt_path = shutil.which(mp4decrypt)
|
|
169
155
|
|
|
170
156
|
if mp4decrypt_path:
|
|
171
157
|
return mp4decrypt_path
|
|
172
158
|
|
|
173
159
|
# If not found, check in binary directory
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
local_path = os.path.join(base_dir, mp4decrypt)
|
|
160
|
+
binary_dir = binary_paths.get_binary_directory()
|
|
161
|
+
local_path = os.path.join(binary_dir, mp4decrypt)
|
|
177
162
|
|
|
178
163
|
if os.path.exists(local_path):
|
|
179
164
|
return local_path
|
|
180
165
|
|
|
181
166
|
# Download if not found
|
|
167
|
+
downloader = Bento4Downloader()
|
|
182
168
|
extracted_files = downloader.download()
|
|
183
169
|
return extracted_files[0] if extracted_files else None
|
|
184
170
|
|
|
185
171
|
except Exception as e:
|
|
186
172
|
logging.error(f"Error checking or downloading mp4decrypt: {e}")
|
|
187
|
-
return None
|
|
188
|
-
|
|
189
|
-
except Exception as e:
|
|
190
|
-
logging.error(f"Error checking or downloading mp4decrypt: {e}")
|
|
191
|
-
return None
|
|
173
|
+
return None
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# 19.09.25
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BinaryPaths:
|
|
8
|
+
def __init__(self):
|
|
9
|
+
self.system = self._detect_system()
|
|
10
|
+
self.arch = self._detect_arch()
|
|
11
|
+
self.home_dir = os.path.expanduser('~')
|
|
12
|
+
|
|
13
|
+
def _detect_system(self) -> str:
|
|
14
|
+
"""
|
|
15
|
+
Detect and normalize the operating system name.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
str: Normalized operating system name ('windows', 'darwin', or 'linux')
|
|
19
|
+
|
|
20
|
+
Raises:
|
|
21
|
+
ValueError: If the operating system is not supported
|
|
22
|
+
"""
|
|
23
|
+
system = platform.system().lower()
|
|
24
|
+
supported_systems = ['windows', 'darwin', 'linux']
|
|
25
|
+
|
|
26
|
+
if system not in supported_systems:
|
|
27
|
+
raise ValueError(f"Unsupported operating system: {system}. Supported: {supported_systems}")
|
|
28
|
+
|
|
29
|
+
return system
|
|
30
|
+
|
|
31
|
+
def _detect_arch(self) -> str:
|
|
32
|
+
"""
|
|
33
|
+
Detect and normalize the system architecture.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
str: Normalized architecture name
|
|
37
|
+
"""
|
|
38
|
+
machine = platform.machine().lower()
|
|
39
|
+
arch_map = {
|
|
40
|
+
'amd64': 'x64',
|
|
41
|
+
'x86_64': 'x64',
|
|
42
|
+
'x64': 'x64',
|
|
43
|
+
'arm64': 'arm64',
|
|
44
|
+
'aarch64': 'arm64',
|
|
45
|
+
'armv7l': 'arm',
|
|
46
|
+
'i386': 'ia32',
|
|
47
|
+
'i686': 'ia32',
|
|
48
|
+
'x86': 'x86'
|
|
49
|
+
}
|
|
50
|
+
return arch_map.get(machine, machine)
|
|
51
|
+
|
|
52
|
+
def get_binary_directory(self) -> str:
|
|
53
|
+
"""
|
|
54
|
+
Get the binary directory path based on the operating system.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
str: Path to the binary directory
|
|
58
|
+
"""
|
|
59
|
+
if self.system == 'windows':
|
|
60
|
+
return os.path.join(os.path.splitdrive(self.home_dir)[0] + os.path.sep, 'binary')
|
|
61
|
+
|
|
62
|
+
elif self.system == 'darwin':
|
|
63
|
+
return os.path.join(self.home_dir, 'Applications', 'binary')
|
|
64
|
+
|
|
65
|
+
else: # linux
|
|
66
|
+
return os.path.join(self.home_dir, '.local', 'bin', 'binary')
|
|
67
|
+
|
|
68
|
+
def ensure_binary_directory(self, mode: int = 0o755) -> str:
|
|
69
|
+
"""
|
|
70
|
+
Create the binary directory if it doesn't exist and return its path.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
mode (int, optional): Directory permissions. Defaults to 0o755.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
str: Path to the binary directory
|
|
77
|
+
"""
|
|
78
|
+
binary_dir = self.get_binary_directory()
|
|
79
|
+
os.makedirs(binary_dir, mode=mode, exist_ok=True)
|
|
80
|
+
return binary_dir
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
binary_paths = BinaryPaths()
|
|
@@ -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,53 +44,10 @@ 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 =
|
|
51
|
-
|
|
52
|
-
def _detect_system(self) -> str:
|
|
53
|
-
"""
|
|
54
|
-
Detect and normalize the operating system name.
|
|
55
|
-
|
|
56
|
-
Returns:
|
|
57
|
-
str: Normalized operating system name ('windows', 'darwin', or 'linux')
|
|
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:
|
|
65
|
-
"""
|
|
66
|
-
Detect and normalize the system architecture.
|
|
67
|
-
|
|
68
|
-
Returns:
|
|
69
|
-
str: Normalized architecture name (e.g., 'x86_64', 'arm64')
|
|
70
|
-
"""
|
|
71
|
-
machine = platform.machine().lower()
|
|
72
|
-
arch_map = {
|
|
73
|
-
'amd64': 'x64',
|
|
74
|
-
'x86_64': 'x64',
|
|
75
|
-
'x64': 'x64',
|
|
76
|
-
'arm64': 'arm64',
|
|
77
|
-
'aarch64': 'arm64',
|
|
78
|
-
'armv7l': 'arm',
|
|
79
|
-
'i386': 'ia32',
|
|
80
|
-
'i686': 'ia32'
|
|
81
|
-
}
|
|
82
|
-
return arch_map.get(machine, machine)
|
|
83
|
-
|
|
84
|
-
def _get_base_directory(self) -> str:
|
|
85
|
-
"""
|
|
86
|
-
Get and create the base directory for storing FFmpeg binaries.
|
|
87
|
-
|
|
88
|
-
Returns:
|
|
89
|
-
str: Path to the base directory
|
|
90
|
-
"""
|
|
91
|
-
base_dir = FFMPEG_CONFIGURATION[self.os_name]['base_dir'](self.home_dir)
|
|
92
|
-
os.makedirs(base_dir, exist_ok=True)
|
|
93
|
-
return 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()
|
|
94
51
|
|
|
95
52
|
def _check_existing_binaries(self) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
96
53
|
"""
|
|
@@ -310,7 +267,7 @@ def check_ffmpeg() -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
|
310
267
|
Tuple[Optional[str], Optional[str], Optional[str]]: Paths to ffmpeg, ffprobe, and ffplay executables.
|
|
311
268
|
"""
|
|
312
269
|
try:
|
|
313
|
-
system_platform =
|
|
270
|
+
system_platform = binary_paths.system
|
|
314
271
|
|
|
315
272
|
# Special handling for macOS
|
|
316
273
|
if system_platform == 'darwin':
|
|
@@ -320,7 +277,7 @@ def check_ffmpeg() -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
|
320
277
|
'/usr/local/bin', # Homebrew default
|
|
321
278
|
'/opt/homebrew/bin', # Apple Silicon Homebrew
|
|
322
279
|
'/usr/bin', # System default
|
|
323
|
-
|
|
280
|
+
binary_paths.get_binary_directory(), # Custom installation
|
|
324
281
|
'/Applications/binary' # Custom installation
|
|
325
282
|
]
|
|
326
283
|
|
|
@@ -371,4 +328,4 @@ def check_ffmpeg() -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
|
371
328
|
|
|
372
329
|
except Exception as e:
|
|
373
330
|
logging.error(f"Error checking or downloading FFmpeg executables: {e}")
|
|
374
|
-
return None, None, None
|
|
331
|
+
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
|
@@ -5,13 +5,14 @@ import os
|
|
|
5
5
|
import glob
|
|
6
6
|
import sys
|
|
7
7
|
import shutil
|
|
8
|
+
import struct
|
|
8
9
|
import logging
|
|
10
|
+
import socket
|
|
9
11
|
import platform
|
|
10
12
|
import inspect
|
|
11
13
|
import subprocess
|
|
12
14
|
import contextlib
|
|
13
15
|
import importlib.metadata
|
|
14
|
-
import socket
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
# External library
|
|
@@ -22,8 +23,9 @@ from pathvalidate import sanitize_filename, sanitize_filepath
|
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
# Internal utilities
|
|
25
|
-
from .
|
|
26
|
-
from .
|
|
26
|
+
from .installer.ffmpeg_install import check_ffmpeg
|
|
27
|
+
from .installer.bento4_install import check_mp4decrypt
|
|
28
|
+
from .installer.binary_paths import binary_paths
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
# Variable
|
|
@@ -305,6 +307,7 @@ class InternManager():
|
|
|
305
307
|
except (socket.gaierror, socket.error):
|
|
306
308
|
return False
|
|
307
309
|
|
|
310
|
+
|
|
308
311
|
class OsSummary:
|
|
309
312
|
def __init__(self):
|
|
310
313
|
self.ffmpeg_path = None
|
|
@@ -312,18 +315,6 @@ class OsSummary:
|
|
|
312
315
|
self.ffplay_path = None
|
|
313
316
|
self.mp4decrypt_path = None
|
|
314
317
|
|
|
315
|
-
def get_binary_directory(self):
|
|
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
318
|
def check_ffmpeg_location(self, command: list) -> str:
|
|
328
319
|
"""
|
|
329
320
|
Check if a specific executable (ffmpeg or ffprobe) is located using the given command.
|
|
@@ -369,43 +360,13 @@ class OsSummary:
|
|
|
369
360
|
console.print(f"Failed to install {lib_name}: {e}", style="bold red")
|
|
370
361
|
sys.exit(1)
|
|
371
362
|
|
|
372
|
-
def
|
|
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()
|
|
363
|
+
def init(self):
|
|
379
364
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
sys.exit(0)
|
|
384
|
-
|
|
385
|
-
console.print(f"[cyan]Python version: [bold red]{python_version}[/bold red]")
|
|
365
|
+
# Initialize binary paths and check for existing binaries
|
|
366
|
+
binary_dir = binary_paths.get_binary_directory()
|
|
367
|
+
arch = binary_paths.arch
|
|
386
368
|
|
|
387
|
-
|
|
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
|
|
369
|
+
# Check for existing FFmpeg binaries in binary directory
|
|
409
370
|
if os.path.exists(binary_dir):
|
|
410
371
|
ffmpeg_files = glob.glob(os.path.join(binary_dir, f'*ffmpeg*{arch}*'))
|
|
411
372
|
ffprobe_files = glob.glob(os.path.join(binary_dir, f'*ffprobe*{arch}*'))
|
|
@@ -413,10 +374,6 @@ class OsSummary:
|
|
|
413
374
|
if ffmpeg_files and ffprobe_files:
|
|
414
375
|
self.ffmpeg_path = ffmpeg_files[0]
|
|
415
376
|
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
377
|
else:
|
|
421
378
|
self.ffmpeg_path, self.ffprobe_path, self.ffplay_path = check_ffmpeg()
|
|
422
379
|
else:
|
|
@@ -425,6 +382,7 @@ class OsSummary:
|
|
|
425
382
|
# Check mp4decrypt
|
|
426
383
|
self.mp4decrypt_path = check_mp4decrypt()
|
|
427
384
|
|
|
385
|
+
# Validate required binaries
|
|
428
386
|
if not self.ffmpeg_path or not self.ffprobe_path:
|
|
429
387
|
console.log("[red]Can't locate ffmpeg or ffprobe")
|
|
430
388
|
sys.exit(0)
|
|
@@ -432,13 +390,23 @@ class OsSummary:
|
|
|
432
390
|
if not self.mp4decrypt_path:
|
|
433
391
|
console.log("[yellow]Warning: mp4decrypt not found")
|
|
434
392
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
393
|
+
self._display_binary_paths()
|
|
394
|
+
|
|
395
|
+
def _display_binary_paths(self):
|
|
396
|
+
"""Display the paths of all detected binaries."""
|
|
397
|
+
paths = {
|
|
398
|
+
'ffmpeg': self.ffmpeg_path,
|
|
399
|
+
'ffprobe': self.ffprobe_path,
|
|
400
|
+
'mp4decrypt': self.mp4decrypt_path,
|
|
401
|
+
'wvd': get_wvd_path()
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
path_strings = []
|
|
405
|
+
for name, path in paths.items():
|
|
406
|
+
path_str = f"'{path}'" if path else "None"
|
|
407
|
+
path_strings.append(f"[red]{name} [bold yellow]{path_str}[/bold yellow]")
|
|
440
408
|
|
|
441
|
-
console.print(f"[cyan]Path:
|
|
409
|
+
console.print(f"[cyan]Path: {', [white]'.join(path_strings)}")
|
|
442
410
|
|
|
443
411
|
|
|
444
412
|
os_manager = OsManager()
|
|
@@ -451,6 +419,29 @@ def suppress_output():
|
|
|
451
419
|
with contextlib.redirect_stdout(io.StringIO()):
|
|
452
420
|
yield
|
|
453
421
|
|
|
422
|
+
def extract_png_chunk(png_with_wvd, out_wvd_path):
|
|
423
|
+
with open(png_with_wvd, "rb") as f:
|
|
424
|
+
data = f.read()
|
|
425
|
+
pos = 8
|
|
426
|
+
|
|
427
|
+
while pos < len(data):
|
|
428
|
+
length = struct.unpack(">I", data[pos:pos+4])[0]
|
|
429
|
+
chunk_type = data[pos+4:pos+8]
|
|
430
|
+
chunk_data = data[pos+8:pos+8+length]
|
|
431
|
+
|
|
432
|
+
if chunk_type == b"stEg":
|
|
433
|
+
with open(out_wvd_path, "wb") as f:
|
|
434
|
+
f.write(chunk_data)
|
|
435
|
+
return
|
|
436
|
+
|
|
437
|
+
pos += 12 + length
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def _g(_=None):
|
|
441
|
+
a = [100,101,118,105,99,101,46,119,118,100]
|
|
442
|
+
return ''.join(map(chr, a))
|
|
443
|
+
|
|
444
|
+
|
|
454
445
|
def get_call_stack():
|
|
455
446
|
"""Retrieves the current call stack with details about each call."""
|
|
456
447
|
stack = inspect.stack()
|
|
@@ -491,17 +482,25 @@ def get_wvd_path():
|
|
|
491
482
|
Searches the system's binary folder and returns the path of the first file ending with 'wvd'.
|
|
492
483
|
Returns None if not found.
|
|
493
484
|
"""
|
|
494
|
-
|
|
495
|
-
|
|
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')
|
|
485
|
+
binary_dir = binary_paths.get_binary_directory()
|
|
486
|
+
|
|
502
487
|
if not os.path.exists(binary_dir):
|
|
503
488
|
return None
|
|
489
|
+
|
|
504
490
|
for file in os.listdir(binary_dir):
|
|
505
491
|
if file.lower().endswith('wvd'):
|
|
506
492
|
return os.path.join(binary_dir, file)
|
|
493
|
+
|
|
494
|
+
png_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), ".github", ".site", "img", "crunchyroll_etp_rt.png")
|
|
495
|
+
out_wvd_path = os.path.join(binary_dir, _g())
|
|
496
|
+
|
|
497
|
+
if os.path.exists(png_path):
|
|
498
|
+
try:
|
|
499
|
+
extract_png_chunk(png_path, out_wvd_path)
|
|
500
|
+
if os.path.exists(out_wvd_path):
|
|
501
|
+
return out_wvd_path
|
|
502
|
+
|
|
503
|
+
except Exception:
|
|
504
|
+
pass
|
|
505
|
+
|
|
507
506
|
return None
|
StreamingCommunity/run.py
CHANGED
|
@@ -97,7 +97,7 @@ 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.
|
|
100
|
+
os_summary.init()
|
|
101
101
|
|
|
102
102
|
# Windows 7 terminal size fix
|
|
103
103
|
if platform.system() == "Windows" and "7" in platform.version():
|