StreamingCommunity 3.3.2__py3-none-any.whl → 3.3.5__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/Lib/Downloader/DASH/decrypt.py +4 -1
- StreamingCommunity/Lib/Downloader/HLS/segments.py +126 -72
- StreamingCommunity/Lib/M3U8/estimator.py +44 -34
- StreamingCommunity/Upload/update.py +1 -1
- StreamingCommunity/Upload/version.py +1 -1
- StreamingCommunity/Util/installer/bento4_install.py +42 -12
- StreamingCommunity/Util/installer/device_install.py +133 -0
- StreamingCommunity/Util/installer/ffmpeg_install.py +93 -88
- StreamingCommunity/Util/os.py +14 -129
- StreamingCommunity/run.py +2 -3
- {streamingcommunity-3.3.2.dist-info → streamingcommunity-3.3.5.dist-info}/METADATA +8 -69
- {streamingcommunity-3.3.2.dist-info → streamingcommunity-3.3.5.dist-info}/RECORD +16 -18
- StreamingCommunity/Api/Player/ddl.py +0 -82
- StreamingCommunity/Api/Player/maxstream.py +0 -141
- StreamingCommunity/Api/Player/mixdrop.py +0 -146
- {streamingcommunity-3.3.2.dist-info → streamingcommunity-3.3.5.dist-info}/WHEEL +0 -0
- {streamingcommunity-3.3.2.dist-info → streamingcommunity-3.3.5.dist-info}/entry_points.txt +0 -0
- {streamingcommunity-3.3.2.dist-info → streamingcommunity-3.3.5.dist-info}/licenses/LICENSE +0 -0
- {streamingcommunity-3.3.2.dist-info → streamingcommunity-3.3.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# 18.07.25
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import struct
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# External library
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Internal utilities
|
|
14
|
+
from .binary_paths import binary_paths
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# Variable
|
|
18
|
+
console = Console()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DeviceDownloader:
|
|
22
|
+
def __init__(self):
|
|
23
|
+
self.base_dir = binary_paths.ensure_binary_directory()
|
|
24
|
+
|
|
25
|
+
def extract_png_chunk(self, png_with_wvd: str, out_wvd_path: str) -> bool:
|
|
26
|
+
"""Extract WVD data"""
|
|
27
|
+
try:
|
|
28
|
+
with open(png_with_wvd, "rb") as f:
|
|
29
|
+
data = f.read()
|
|
30
|
+
pos = 8
|
|
31
|
+
|
|
32
|
+
while pos < len(data):
|
|
33
|
+
length = struct.unpack(">I", data[pos:pos+4])[0]
|
|
34
|
+
chunk_type = data[pos+4:pos+8]
|
|
35
|
+
chunk_data = data[pos+8:pos+8+length]
|
|
36
|
+
|
|
37
|
+
if chunk_type == b"stEg":
|
|
38
|
+
with open(out_wvd_path, "wb") as f:
|
|
39
|
+
f.write(chunk_data)
|
|
40
|
+
return True
|
|
41
|
+
|
|
42
|
+
pos += 12 + length
|
|
43
|
+
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
except Exception as e:
|
|
47
|
+
logging.error(f"Error extracting PNG chunk: {e}")
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
def _check_existing_wvd(self) -> Optional[str]:
|
|
51
|
+
"""Check for existing WVD files in binary directory."""
|
|
52
|
+
try:
|
|
53
|
+
console.print("[cyan]Checking for existing device.wvd files...")
|
|
54
|
+
if not os.path.exists(self.base_dir):
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
# Look for any .wvd file first
|
|
58
|
+
for file in os.listdir(self.base_dir):
|
|
59
|
+
if file.lower().endswith('.wvd'):
|
|
60
|
+
wvd_path = os.path.join(self.base_dir, file)
|
|
61
|
+
if os.path.exists(wvd_path) and os.path.getsize(wvd_path) > 0:
|
|
62
|
+
logging.info(f"Found existing .wvd file: {file}")
|
|
63
|
+
return wvd_path
|
|
64
|
+
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
except Exception as e:
|
|
68
|
+
logging.error(f"Error checking existing WVD files: {e}")
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
def _find_png_recursively(self, start_dir: str = ".") -> Optional[str]:
|
|
72
|
+
"""Find crunchyroll_etp_rt.png recursively starting from start_dir."""
|
|
73
|
+
target_filename = "crunchyroll_etp_rt.png"
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
for root, dirs, files in os.walk(start_dir):
|
|
77
|
+
if target_filename in files:
|
|
78
|
+
png_path = os.path.join(root, target_filename)
|
|
79
|
+
logging.info(f"Found PNG file at: {png_path}")
|
|
80
|
+
return png_path
|
|
81
|
+
|
|
82
|
+
logging.warning(f"PNG file '{target_filename}' not found in '{start_dir}' and subdirectories")
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
except Exception as e:
|
|
86
|
+
logging.error(f"Error during recursive PNG search: {e}")
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
def download(self) -> Optional[str]:
|
|
90
|
+
"""
|
|
91
|
+
Main method to extract WVD file from PNG.
|
|
92
|
+
"""
|
|
93
|
+
try:
|
|
94
|
+
png_path = self._find_png_recursively()
|
|
95
|
+
if not png_path:
|
|
96
|
+
logging.error("PNG file not found, cannot extract device.wvd")
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
device_wvd_path = os.path.join(self.base_dir, 'device.wvd')
|
|
100
|
+
|
|
101
|
+
if self.extract_png_chunk(png_path, device_wvd_path):
|
|
102
|
+
if os.path.exists(device_wvd_path) and os.path.getsize(device_wvd_path) > 0:
|
|
103
|
+
logging.info("Successfully extracted device.wvd from PNG")
|
|
104
|
+
return device_wvd_path
|
|
105
|
+
else:
|
|
106
|
+
logging.error("Extraction completed but resulting file is invalid")
|
|
107
|
+
return None
|
|
108
|
+
else:
|
|
109
|
+
logging.error("Failed to extract device.wvd from PNG")
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
except Exception as e:
|
|
113
|
+
logging.error(f"Error during WVD extraction: {e}")
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def check_wvd_path() -> Optional[str]:
|
|
118
|
+
"""
|
|
119
|
+
Check for device.wvd file in binary directory and extract from PNG if not found.
|
|
120
|
+
"""
|
|
121
|
+
try:
|
|
122
|
+
downloader = DeviceDownloader()
|
|
123
|
+
|
|
124
|
+
existing_wvd = downloader._check_existing_wvd()
|
|
125
|
+
if existing_wvd:
|
|
126
|
+
return existing_wvd
|
|
127
|
+
|
|
128
|
+
logging.info("device.wvd not found, attempting extraction from PNG")
|
|
129
|
+
return downloader.download()
|
|
130
|
+
|
|
131
|
+
except Exception as e:
|
|
132
|
+
logging.error(f"Error checking for device.wvd: {e}")
|
|
133
|
+
return None
|
|
@@ -51,38 +51,91 @@ class FFMPEGDownloader:
|
|
|
51
51
|
|
|
52
52
|
def _check_existing_binaries(self) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
53
53
|
"""
|
|
54
|
-
Check if FFmpeg binaries already exist
|
|
55
|
-
|
|
54
|
+
Check if FFmpeg binaries already exist.
|
|
55
|
+
Order: system PATH (where/which) -> binary directory
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Tuple[Optional[str], Optional[str], Optional[str]]: Paths to ffmpeg, ffprobe, ffplay
|
|
56
59
|
"""
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
60
|
+
try:
|
|
61
|
+
|
|
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
|
|
69
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
|
|
93
|
+
|
|
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 = []
|
|
99
|
+
|
|
70
100
|
for executable in executables:
|
|
71
|
-
found = None
|
|
72
|
-
for path in potential_paths:
|
|
73
|
-
full_path = os.path.join(path, executable)
|
|
74
|
-
if os.path.exists(full_path) and os.access(full_path, os.X_OK):
|
|
75
|
-
found = full_path
|
|
76
|
-
break
|
|
77
|
-
found_executables.append(found)
|
|
78
|
-
else:
|
|
79
|
-
|
|
80
|
-
# Original behavior for other operating systems
|
|
81
|
-
for executable in executables:
|
|
82
|
-
exe_paths = glob.glob(os.path.join(self.base_dir, executable))
|
|
83
|
-
found_executables.append(exe_paths[0] if exe_paths else None)
|
|
84
101
|
|
|
85
|
-
|
|
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)
|
|
86
139
|
|
|
87
140
|
def _get_latest_version(self, repo: str) -> Optional[str]:
|
|
88
141
|
"""
|
|
@@ -210,7 +263,6 @@ class FFMPEGDownloader:
|
|
|
210
263
|
ffprobe_path = shutil.which('ffprobe')
|
|
211
264
|
|
|
212
265
|
if ffmpeg_path and ffprobe_path:
|
|
213
|
-
console.print("[bold green]FFmpeg successfully installed via apt[/]")
|
|
214
266
|
return ffmpeg_path, ffprobe_path, None
|
|
215
267
|
else:
|
|
216
268
|
console.print("[bold yellow]Failed to install FFmpeg via apt. Proceeding with static download.[/]")
|
|
@@ -242,7 +294,6 @@ class FFMPEGDownloader:
|
|
|
242
294
|
# Extract the file
|
|
243
295
|
if self._extract_file(download_path, final_path):
|
|
244
296
|
successful_extractions.append(final_path)
|
|
245
|
-
console.print(f"[bold green]Successfully installed {executable}[/]")
|
|
246
297
|
else:
|
|
247
298
|
console.print(f"[bold red]Failed to extract {executable}[/]")
|
|
248
299
|
|
|
@@ -261,69 +312,23 @@ class FFMPEGDownloader:
|
|
|
261
312
|
def check_ffmpeg() -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
262
313
|
"""
|
|
263
314
|
Check for FFmpeg executables in the system and download them if not found.
|
|
264
|
-
|
|
265
|
-
|
|
315
|
+
Order: system PATH (where/which) -> binary directory -> download
|
|
316
|
+
|
|
266
317
|
Returns:
|
|
267
|
-
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
|
|
268
319
|
"""
|
|
269
320
|
try:
|
|
270
|
-
|
|
321
|
+
# Create downloader instance to use its existing check method
|
|
322
|
+
downloader = FFMPEGDownloader()
|
|
271
323
|
|
|
272
|
-
#
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
'/opt/homebrew/bin', # Apple Silicon Homebrew
|
|
279
|
-
'/usr/bin', # System default
|
|
280
|
-
binary_paths.get_binary_directory(), # Custom installation
|
|
281
|
-
'/Applications/binary' # Custom installation
|
|
282
|
-
]
|
|
283
|
-
|
|
284
|
-
for path in potential_paths:
|
|
285
|
-
ffmpeg_path = os.path.join(path, 'ffmpeg')
|
|
286
|
-
ffprobe_path = os.path.join(path, 'ffprobe')
|
|
287
|
-
ffplay_path = os.path.join(path, 'ffplay')
|
|
288
|
-
|
|
289
|
-
if (os.path.exists(ffmpeg_path) and os.path.exists(ffprobe_path) and
|
|
290
|
-
os.access(ffmpeg_path, os.X_OK) and os.access(ffprobe_path, os.X_OK)):
|
|
291
|
-
|
|
292
|
-
# Return found executables, with ffplay being optional
|
|
293
|
-
ffplay_path = ffplay_path if os.path.exists(ffplay_path) else None
|
|
294
|
-
return ffmpeg_path, ffprobe_path, ffplay_path
|
|
295
|
-
|
|
296
|
-
# Windows detection
|
|
297
|
-
elif system_platform == 'windows':
|
|
298
|
-
try:
|
|
299
|
-
ffmpeg_path = subprocess.check_output(
|
|
300
|
-
['where', 'ffmpeg'], stderr=subprocess.DEVNULL, text=True
|
|
301
|
-
).strip().split('\n')[0]
|
|
302
|
-
|
|
303
|
-
ffprobe_path = subprocess.check_output(
|
|
304
|
-
['where', 'ffprobe'], stderr=subprocess.DEVNULL, text=True
|
|
305
|
-
).strip().split('\n')[0]
|
|
306
|
-
|
|
307
|
-
ffplay_path = subprocess.check_output(
|
|
308
|
-
['where', 'ffplay'], stderr=subprocess.DEVNULL, text=True
|
|
309
|
-
).strip().split('\n')[0]
|
|
310
|
-
|
|
311
|
-
return ffmpeg_path, ffprobe_path, ffplay_path
|
|
312
|
-
|
|
313
|
-
except subprocess.CalledProcessError:
|
|
314
|
-
logging.warning("One or more FFmpeg binaries were not found with command where")
|
|
315
|
-
|
|
316
|
-
# Linux detection
|
|
317
|
-
else:
|
|
318
|
-
ffmpeg_path = shutil.which('ffmpeg')
|
|
319
|
-
ffprobe_path = shutil.which('ffprobe')
|
|
320
|
-
ffplay_path = shutil.which('ffplay')
|
|
321
|
-
|
|
322
|
-
if ffmpeg_path and ffprobe_path:
|
|
323
|
-
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
|
|
324
330
|
|
|
325
|
-
#
|
|
326
|
-
downloader = FFMPEGDownloader()
|
|
331
|
+
# STEP 3: Download if not found
|
|
327
332
|
return downloader.download()
|
|
328
333
|
|
|
329
334
|
except Exception as e:
|
StreamingCommunity/Util/os.py
CHANGED
|
@@ -2,17 +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
|
-
import struct
|
|
9
7
|
import logging
|
|
10
8
|
import socket
|
|
11
9
|
import platform
|
|
12
10
|
import inspect
|
|
13
|
-
import subprocess
|
|
14
11
|
import contextlib
|
|
15
|
-
import importlib.metadata
|
|
16
12
|
|
|
17
13
|
|
|
18
14
|
# External library
|
|
@@ -25,7 +21,7 @@ from pathvalidate import sanitize_filename, sanitize_filepath
|
|
|
25
21
|
# Internal utilities
|
|
26
22
|
from .installer.ffmpeg_install import check_ffmpeg
|
|
27
23
|
from .installer.bento4_install import check_mp4decrypt
|
|
28
|
-
from .installer.
|
|
24
|
+
from .installer.device_install import check_wvd_path
|
|
29
25
|
|
|
30
26
|
|
|
31
27
|
# Variable
|
|
@@ -245,7 +241,7 @@ class OsManager:
|
|
|
245
241
|
return False
|
|
246
242
|
|
|
247
243
|
|
|
248
|
-
class
|
|
244
|
+
class InternetManager():
|
|
249
245
|
def format_file_size(self, size_bytes: float) -> str:
|
|
250
246
|
"""
|
|
251
247
|
Formats a file size from bytes into a human-readable string representation.
|
|
@@ -314,83 +310,18 @@ class OsSummary:
|
|
|
314
310
|
self.ffprobe_path = None
|
|
315
311
|
self.ffplay_path = None
|
|
316
312
|
self.mp4decrypt_path = None
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
"""
|
|
320
|
-
Check if a specific executable (ffmpeg or ffprobe) is located using the given command.
|
|
321
|
-
Returns the path of the executable or None if not found.
|
|
322
|
-
"""
|
|
323
|
-
try:
|
|
324
|
-
result = subprocess.check_output(command, text=True).strip()
|
|
325
|
-
return result.split('\n')[0] if result else None
|
|
326
|
-
|
|
327
|
-
except subprocess.CalledProcessError:
|
|
328
|
-
return None
|
|
329
|
-
|
|
330
|
-
def get_library_version(self, lib_name: str):
|
|
331
|
-
"""
|
|
332
|
-
Retrieve the version of a Python library.
|
|
333
|
-
|
|
334
|
-
Args:
|
|
335
|
-
lib_name (str): The name of the Python library.
|
|
336
|
-
|
|
337
|
-
Returns:
|
|
338
|
-
str: The library name followed by its version, or `-not installed` if not found.
|
|
339
|
-
"""
|
|
340
|
-
try:
|
|
341
|
-
version = importlib.metadata.version(lib_name)
|
|
342
|
-
return f"{lib_name}-{version}"
|
|
343
|
-
|
|
344
|
-
except importlib.metadata.PackageNotFoundError:
|
|
345
|
-
return f"{lib_name}-not installed"
|
|
346
|
-
|
|
347
|
-
def install_library(self, lib_name: str):
|
|
348
|
-
"""
|
|
349
|
-
Install a Python library using pip.
|
|
350
|
-
|
|
351
|
-
Args:
|
|
352
|
-
lib_name (str): The name of the library to install.
|
|
353
|
-
"""
|
|
354
|
-
try:
|
|
355
|
-
console.print(f"Installing {lib_name}...", style="bold yellow")
|
|
356
|
-
subprocess.check_call([sys.executable, "-m", "pip", "install", lib_name])
|
|
357
|
-
console.print(f"{lib_name} installed successfully!", style="bold green")
|
|
358
|
-
|
|
359
|
-
except subprocess.CalledProcessError as e:
|
|
360
|
-
console.print(f"Failed to install {lib_name}: {e}", style="bold red")
|
|
361
|
-
sys.exit(1)
|
|
313
|
+
self.wvd_path = None
|
|
314
|
+
self.init()
|
|
362
315
|
|
|
363
316
|
def init(self):
|
|
364
317
|
|
|
365
|
-
#
|
|
366
|
-
|
|
367
|
-
arch = binary_paths.arch
|
|
368
|
-
|
|
369
|
-
# Check for existing FFmpeg binaries in binary directory
|
|
370
|
-
if os.path.exists(binary_dir):
|
|
371
|
-
ffmpeg_files = glob.glob(os.path.join(binary_dir, f'*ffmpeg*{arch}*'))
|
|
372
|
-
ffprobe_files = glob.glob(os.path.join(binary_dir, f'*ffprobe*{arch}*'))
|
|
373
|
-
|
|
374
|
-
if ffmpeg_files and ffprobe_files:
|
|
375
|
-
self.ffmpeg_path = ffmpeg_files[0]
|
|
376
|
-
self.ffprobe_path = ffprobe_files[0]
|
|
377
|
-
else:
|
|
378
|
-
self.ffmpeg_path, self.ffprobe_path, self.ffplay_path = check_ffmpeg()
|
|
379
|
-
else:
|
|
380
|
-
self.ffmpeg_path, self.ffprobe_path, self.ffplay_path = check_ffmpeg()
|
|
381
|
-
|
|
382
|
-
# Check mp4decrypt
|
|
318
|
+
# Check for binaries
|
|
319
|
+
self.ffmpeg_path, self.ffprobe_path, _ = check_ffmpeg()
|
|
383
320
|
self.mp4decrypt_path = check_mp4decrypt()
|
|
384
|
-
|
|
385
|
-
# Validate required binaries
|
|
386
|
-
if not self.ffmpeg_path or not self.ffprobe_path:
|
|
387
|
-
console.log("[red]Can't locate ffmpeg or ffprobe")
|
|
388
|
-
sys.exit(0)
|
|
389
|
-
|
|
390
|
-
if not self.mp4decrypt_path:
|
|
391
|
-
console.log("[yellow]Warning: mp4decrypt not found")
|
|
321
|
+
self.wvd_path = check_wvd_path()
|
|
392
322
|
|
|
393
323
|
self._display_binary_paths()
|
|
324
|
+
time.sleep(0.3)
|
|
394
325
|
|
|
395
326
|
def _display_binary_paths(self):
|
|
396
327
|
"""Display the paths of all detected binaries."""
|
|
@@ -398,7 +329,7 @@ class OsSummary:
|
|
|
398
329
|
'ffmpeg': self.ffmpeg_path,
|
|
399
330
|
'ffprobe': self.ffprobe_path,
|
|
400
331
|
'mp4decrypt': self.mp4decrypt_path,
|
|
401
|
-
'wvd':
|
|
332
|
+
'wvd': self.wvd_path
|
|
402
333
|
}
|
|
403
334
|
|
|
404
335
|
path_strings = []
|
|
@@ -409,8 +340,9 @@ class OsSummary:
|
|
|
409
340
|
console.print(f"[cyan]Path: {', [white]'.join(path_strings)}")
|
|
410
341
|
|
|
411
342
|
|
|
343
|
+
# Initialize the os_summary, internet_manager, and os_manager when the module is imported
|
|
412
344
|
os_manager = OsManager()
|
|
413
|
-
internet_manager =
|
|
345
|
+
internet_manager = InternetManager()
|
|
414
346
|
os_summary = OsSummary()
|
|
415
347
|
|
|
416
348
|
|
|
@@ -419,29 +351,6 @@ def suppress_output():
|
|
|
419
351
|
with contextlib.redirect_stdout(io.StringIO()):
|
|
420
352
|
yield
|
|
421
353
|
|
|
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
|
-
|
|
445
354
|
def get_call_stack():
|
|
446
355
|
"""Retrieves the current call stack with details about each call."""
|
|
447
356
|
stack = inspect.stack()
|
|
@@ -478,29 +387,5 @@ def get_mp4decrypt_path():
|
|
|
478
387
|
return os_summary.mp4decrypt_path
|
|
479
388
|
|
|
480
389
|
def get_wvd_path():
|
|
481
|
-
"""
|
|
482
|
-
|
|
483
|
-
Returns None if not found.
|
|
484
|
-
"""
|
|
485
|
-
binary_dir = binary_paths.get_binary_directory()
|
|
486
|
-
|
|
487
|
-
if not os.path.exists(binary_dir):
|
|
488
|
-
return None
|
|
489
|
-
|
|
490
|
-
for file in os.listdir(binary_dir):
|
|
491
|
-
if file.lower().endswith('wvd'):
|
|
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
|
-
|
|
506
|
-
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.init()
|
|
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()
|