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
|
@@ -1,10 +1,11 @@
|
|
|
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
|
|
7
|
+
import subprocess
|
|
8
|
+
from typing import Optional
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
# External library
|
|
@@ -13,12 +14,15 @@ from rich.console import Console
|
|
|
13
14
|
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn
|
|
14
15
|
|
|
15
16
|
|
|
17
|
+
# Internal utilities
|
|
18
|
+
from .binary_paths import binary_paths
|
|
19
|
+
|
|
20
|
+
|
|
16
21
|
# Variable
|
|
17
22
|
console = Console()
|
|
18
23
|
|
|
19
24
|
BENTO4_CONFIGURATION = {
|
|
20
25
|
'windows': {
|
|
21
|
-
'base_dir': lambda home: os.path.join(os.path.splitdrive(home)[0] + os.sep, 'binary'),
|
|
22
26
|
'download_url': 'https://www.bok.net/Bento4/binaries/Bento4-SDK-{version}.{platform}.zip',
|
|
23
27
|
'versions': {
|
|
24
28
|
'x64': 'x86_64-microsoft-win32',
|
|
@@ -27,7 +31,6 @@ BENTO4_CONFIGURATION = {
|
|
|
27
31
|
'executables': ['mp4decrypt.exe']
|
|
28
32
|
},
|
|
29
33
|
'darwin': {
|
|
30
|
-
'base_dir': lambda home: os.path.join(home, 'Applications', 'binary'),
|
|
31
34
|
'download_url': 'https://www.bok.net/Bento4/binaries/Bento4-SDK-{version}.{platform}.zip',
|
|
32
35
|
'versions': {
|
|
33
36
|
'x64': 'universal-apple-macosx',
|
|
@@ -36,7 +39,6 @@ BENTO4_CONFIGURATION = {
|
|
|
36
39
|
'executables': ['mp4decrypt']
|
|
37
40
|
},
|
|
38
41
|
'linux': {
|
|
39
|
-
'base_dir': lambda home: os.path.join(home, '.local', 'bin', 'binary'),
|
|
40
42
|
'download_url': 'https://www.bok.net/Bento4/binaries/Bento4-SDK-{version}.{platform}.zip',
|
|
41
43
|
'versions': {
|
|
42
44
|
'x64': 'x86_64-unknown-linux',
|
|
@@ -50,26 +52,11 @@ BENTO4_CONFIGURATION = {
|
|
|
50
52
|
|
|
51
53
|
class Bento4Downloader:
|
|
52
54
|
def __init__(self):
|
|
53
|
-
self.os_name =
|
|
54
|
-
self.arch =
|
|
55
|
-
self.home_dir =
|
|
56
|
-
self.base_dir =
|
|
55
|
+
self.os_name = binary_paths.system
|
|
56
|
+
self.arch = binary_paths.arch
|
|
57
|
+
self.home_dir = binary_paths.home_dir
|
|
58
|
+
self.base_dir = binary_paths.ensure_binary_directory()
|
|
57
59
|
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
60
|
|
|
74
61
|
def _download_file(self, url: str, destination: str) -> bool:
|
|
75
62
|
try:
|
|
@@ -150,7 +137,6 @@ class Bento4Downloader:
|
|
|
150
137
|
os.remove(zip_path)
|
|
151
138
|
|
|
152
139
|
if extracted_files:
|
|
153
|
-
console.print("[bold green]Bento4 successfully installed[/]")
|
|
154
140
|
return extracted_files
|
|
155
141
|
|
|
156
142
|
raise Exception("Failed to install Bento4")
|
|
@@ -160,32 +146,58 @@ class Bento4Downloader:
|
|
|
160
146
|
console.print(f"[bold red]Error downloading Bento4: {str(e)}[/]")
|
|
161
147
|
return []
|
|
162
148
|
|
|
163
|
-
|
|
164
|
-
|
|
149
|
+
|
|
150
|
+
def check_mp4decrypt() -> Optional[str]:
|
|
151
|
+
"""
|
|
152
|
+
Check for mp4decrypt in the system and download if not found.
|
|
153
|
+
Order: system PATH (where/which) -> binary directory -> download
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
Optional[str]: Path to mp4decrypt executable or None if not found/downloaded
|
|
157
|
+
"""
|
|
165
158
|
try:
|
|
166
|
-
|
|
167
|
-
mp4decrypt = "mp4decrypt.exe" if platform.system().lower() == "windows" else "mp4decrypt"
|
|
168
|
-
mp4decrypt_path = shutil.which(mp4decrypt)
|
|
159
|
+
system_platform = binary_paths.system
|
|
169
160
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
downloader = Bento4Downloader()
|
|
175
|
-
base_dir = downloader.base_dir
|
|
176
|
-
local_path = os.path.join(base_dir, mp4decrypt)
|
|
161
|
+
# STEP 1: Check system PATH
|
|
162
|
+
console.print("[cyan]Checking for mp4decrypt in system PATH...[/]")
|
|
163
|
+
mp4decrypt_name = "mp4decrypt.exe" if system_platform == "windows" else "mp4decrypt"
|
|
164
|
+
mp4decrypt_path = None
|
|
177
165
|
|
|
178
|
-
if
|
|
166
|
+
if system_platform == 'windows':
|
|
167
|
+
try:
|
|
168
|
+
mp4decrypt_path = subprocess.check_output(
|
|
169
|
+
['where', mp4decrypt_name], stderr=subprocess.DEVNULL, text=True
|
|
170
|
+
).strip().split('\n')[0]
|
|
171
|
+
|
|
172
|
+
if mp4decrypt_path:
|
|
173
|
+
logging.info("mp4decrypt found in Windows system PATH")
|
|
174
|
+
return mp4decrypt_path
|
|
175
|
+
|
|
176
|
+
except subprocess.CalledProcessError:
|
|
177
|
+
logging.info("mp4decrypt not found in Windows system PATH")
|
|
178
|
+
|
|
179
|
+
else:
|
|
180
|
+
mp4decrypt_path = shutil.which(mp4decrypt_name)
|
|
181
|
+
|
|
182
|
+
if mp4decrypt_path:
|
|
183
|
+
logging.info("mp4decrypt found in system PATH")
|
|
184
|
+
return mp4decrypt_path
|
|
185
|
+
|
|
186
|
+
# STEP 2: Check in binary directory
|
|
187
|
+
console.print("[cyan]Checking for mp4decrypt in binary directory...[/]")
|
|
188
|
+
binary_dir = binary_paths.get_binary_directory()
|
|
189
|
+
local_path = os.path.join(binary_dir, mp4decrypt_name)
|
|
190
|
+
|
|
191
|
+
if os.path.exists(local_path) and os.access(local_path, os.X_OK):
|
|
192
|
+
logging.info("mp4decrypt found in binary directory")
|
|
179
193
|
return local_path
|
|
180
194
|
|
|
181
|
-
# Download if not found
|
|
195
|
+
# STEP 3: Download if not found
|
|
196
|
+
console.print("[cyan]mp4decrypt not found. Downloading...[/]")
|
|
197
|
+
downloader = Bento4Downloader()
|
|
182
198
|
extracted_files = downloader.download()
|
|
183
199
|
return extracted_files[0] if extracted_files else None
|
|
184
200
|
|
|
185
201
|
except Exception as e:
|
|
186
202
|
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
|
|
203
|
+
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()
|
|
@@ -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
|