Skip to content

Commit 62953a5

Browse files
committed
Some fix
1 parent 1a56be6 commit 62953a5

File tree

15 files changed

+211
-220
lines changed

15 files changed

+211
-220
lines changed

.github/workflows/testing.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,4 @@ jobs:
6464
python -m pip install ruff
6565
- name: Run Ruff check
6666
run: |
67-
ruff check . --fix --exclude Test --exclude .github
67+
ruff check . --fix --exclude Test --exclude GUI

GUI/manage.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@
22
import os
33
import sys
44

5+
# Fix PYTHONPATH
6+
current_dir = os.path.dirname(os.path.abspath(__file__))
7+
parent_dir = os.path.dirname(current_dir)
8+
if parent_dir not in sys.path:
9+
sys.path.insert(0, parent_dir)
10+
511
from StreamingCommunity.Util.os import os_summary
612

713

14+
815
def main():
916
os_summary.init()
1017
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webgui.settings")
@@ -14,4 +21,4 @@ def main():
1421

1522

1623
if __name__ == "__main__":
17-
main()
24+
main()

StreamingCommunity/Api/Site/mediasetinfinity/util/get_license.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@
99
# External library
1010
import httpx
1111
from rich.console import Console
12-
from seleniumbase import Driver
12+
try:
13+
from seleniumbase import Driver
14+
SELENIUMBASE_AVAILABLE = True
15+
except ImportError:
16+
SELENIUMBASE_AVAILABLE = False
17+
Driver = None
1318

1419

1520
# Internal utilities
@@ -40,6 +45,17 @@ def save_network_data(data):
4045

4146

4247
def generate_betoken(username: str, password: str, sleep_action: float = 1.0) -> str:
48+
"""Generate beToken using browser automation"""
49+
50+
if not SELENIUMBASE_AVAILABLE:
51+
console.print("[red]Error: seleniumbase is not installed. Cannot perform browser login.")
52+
console.print("[yellow]Install seleniumbase with: pip install seleniumbase")
53+
return None
54+
55+
if not Driver:
56+
console.print("[red]Error: seleniumbase Driver is not available.")
57+
return None
58+
4359
driver = Driver(uc=True, uc_cdp_events=True, incognito=True, headless=True)
4460

4561
try:
@@ -155,6 +171,7 @@ def generate_betoken(username: str, password: str, sleep_action: float = 1.0) ->
155171
finally:
156172
driver.quit()
157173

174+
158175
def get_bearer_token():
159176
"""
160177
Gets the BEARER_TOKEN for authentication.
@@ -173,6 +190,11 @@ def get_bearer_token():
173190
password = config_manager.get_dict("SITE_LOGIN", "mediasetinfinity").get("password", "")
174191

175192
if username and password:
193+
if not SELENIUMBASE_AVAILABLE:
194+
console.print("[yellow]Warning: seleniumbase not available. Cannot perform automatic login.")
195+
console.print("[yellow]Please manually obtain beToken and set it in config.")
196+
return config_manager.get_dict("SITE_LOGIN", "mediasetinfinity")["beToken"]
197+
176198
beToken = generate_betoken(username, password)
177199

178200
if beToken is not None:
@@ -187,6 +209,7 @@ def get_bearer_token():
187209

188210
return config_manager.get_dict("SITE_LOGIN", "mediasetinfinity")["beToken"]
189211

212+
190213
def get_playback_url(BEARER_TOKEN, CONTENT_ID):
191214
"""
192215
Gets the playback URL for the specified content.
@@ -231,6 +254,7 @@ def get_playback_url(BEARER_TOKEN, CONTENT_ID):
231254
except Exception as e:
232255
raise RuntimeError(f"Failed to get playback URL: {e}")
233256

257+
234258
def parse_tracking_data(tracking_value):
235259
"""
236260
Parses the trackingData string into a dictionary.
@@ -243,6 +267,7 @@ def parse_tracking_data(tracking_value):
243267
"""
244268
return dict(item.split('=', 1) for item in tracking_value.split('|') if '=' in item)
245269

270+
246271
def parse_smil_for_tracking_and_video(smil_xml):
247272
"""
248273
Extracts all video_src and trackingData pairs from the SMIL.
@@ -283,6 +308,7 @@ def parse_smil_for_tracking_and_video(smil_xml):
283308

284309
return results
285310

311+
286312
def get_tracking_info(BEARER_TOKEN, PLAYBACK_JSON):
287313
"""
288314
Retrieves tracking information from the playback JSON.
@@ -326,6 +352,7 @@ def get_tracking_info(BEARER_TOKEN, PLAYBACK_JSON):
326352
except Exception:
327353
return None
328354

355+
329356
def generate_license_url(BEARER_TOKEN, tracking_info):
330357
"""
331358
Generates the URL to obtain the Widevine license.

StreamingCommunity/Lib/Downloader/DASH/cdm_helpher.py

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,29 +29,11 @@ def get_widevine_keys(pssh, license_url, cdm_device_path, headers=None, payload=
2929
Returns:
3030
list: List of dicts {'kid': ..., 'key': ...} (only CONTENT keys) or None if error.
3131
"""
32-
33-
# Check if PSSH is a valid base64 string
34-
try:
35-
base64.b64decode(pssh)
36-
except Exception:
37-
console.print("[bold red] Invalid PSSH base64 string.[/bold red]")
38-
return None
39-
4032
try:
4133
device = Device.load(cdm_device_path)
4234
cdm = Cdm.from_device(device)
4335
session_id = cdm.open()
4436

45-
# Display security level in a more readable format
46-
security_levels = {1: "L1 (Hardware)", 2: "L2 (Software)", 3: "L3 (Software)"}
47-
security_level_str = security_levels.get(device.security_level, 'Unknown')
48-
logging.info(f"Security Level: {security_level_str}")
49-
50-
# Only allow L3, otherwise warn and exit
51-
if device.security_level != 3:
52-
console.print(f"[bold yellow]⚠️ Only L3 (Software) security level is supported. Current: {security_level_str}[/bold yellow]")
53-
return None
54-
5537
try:
5638
challenge = cdm.get_license_challenge(session_id, PSSH(pssh))
5739
req_headers = headers or {}
@@ -108,6 +90,7 @@ def get_widevine_keys(pssh, license_url, cdm_device_path, headers=None, payload=
10890
content_keys = []
10991
for key in cdm.get_keys(session_id):
11092
if key.type == "CONTENT":
93+
11194
kid = key.kid.hex() if isinstance(key.kid, bytes) else str(key.kid)
11295
key_val = key.key.hex() if isinstance(key.key, bytes) else str(key.key)
11396

StreamingCommunity/Lib/M3U8/decryptor.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,4 @@ def decrypt(self, ciphertext: bytes) -> bytes:
7878
else:
7979
raise ValueError("Invalid or unsupported method")
8080

81-
"""
82-
end = time.perf_counter_ns()
83-
84-
# Calculate the elapsed time with high precision
85-
elapsed_nanoseconds = end - start
86-
elapsed_milliseconds = elapsed_nanoseconds / 1_000_000
87-
elapsed_seconds = elapsed_nanoseconds / 1_000_000_000
88-
89-
# Log performance metrics
90-
logging.info("[Crypto Decryption Performance]")
91-
logging.info(f"Method: {self.method}")
92-
logging.info(f"Decryption Time: {elapsed_milliseconds:.4f} ms ({elapsed_seconds:.6f} s)")
93-
logging.info(f"Decrypted Content Length: {len(decrypted_content)} bytes")
94-
"""
9581
return decrypted_content

StreamingCommunity/Lib/TMBD/tmdb.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# 24.08.24
22

33
import sys
4-
from typing import Dict
54

65

76
# External libraries
@@ -95,7 +94,6 @@ def __init__(self, api_key):
9594
"""
9695
self.api_key = api_key
9796
self.base_url = "https://api.themoviedb.org/3"
98-
#self.genres = self._fetch_genres()
9997
self._cached_trending_tv = None
10098
self._cached_trending_movies = None
10199

@@ -120,16 +118,6 @@ def _make_request(self, endpoint, params=None):
120118

121119
return response.json()
122120

123-
def _fetch_genres(self) -> Dict[int, str]:
124-
"""
125-
Fetch and return the genre names from TheMovieDB.
126-
127-
Returns:
128-
Dict[int, str]: A dictionary mapping genre IDs to genre names.
129-
"""
130-
genres = self._make_request("genre/movie/list")
131-
return {genre['id']: genre['name'] for genre in genres.get('genres', [])}
132-
133121
def _display_top_5(self, category: str, data, name_key='title'):
134122
"""
135123
Display top 5 most popular items in a single line with colors.

StreamingCommunity/Util/color.py

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# 24.05.24
22

3-
import struct
4-
53
class Colors:
64
BLACK = "\033[30m"
75
RED = "\033[31m"
@@ -19,22 +17,4 @@ class Colors:
1917
LIGHT_MAGENTA = "\033[95m"
2018
LIGHT_CYAN = "\033[96m"
2119
WHITE = "\033[97m"
22-
RESET = "\033[0m"
23-
24-
25-
def extract_png_chunk(png_with_wvd, out_wvd_path):
26-
with open(png_with_wvd, "rb") as f: data = f.read()
27-
pos = 8
28-
while pos < len(data):
29-
length = struct.unpack(">I", data[pos:pos+4])[0]
30-
chunk_type = data[pos+4:pos+8]
31-
chunk_data = data[pos+8:pos+8+length]
32-
if chunk_type == b"stEg":
33-
with open(out_wvd_path, "wb") as f: f.write(chunk_data)
34-
return
35-
pos += 12 + length
36-
37-
38-
def _g(_=None):
39-
a = [100,101,118,105,99,101,46,119,118,100]
40-
return ''.join(map(chr, a))
20+
RESET = "\033[0m"

StreamingCommunity/Util/bento4_installer.py renamed to StreamingCommunity/Util/installer/bento4_install.py

Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
# 18.07.25
22

33
import os
4-
import platform
5-
import logging
64
import shutil
75
import zipfile
6+
import logging
87

98

109
# External library
@@ -13,12 +12,15 @@
1312
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn
1413

1514

15+
# Internal utilities
16+
from .binary_paths import binary_paths
17+
18+
1619
# Variable
1720
console = Console()
1821

1922
BENTO4_CONFIGURATION = {
2023
'windows': {
21-
'base_dir': lambda home: os.path.join(os.path.splitdrive(home)[0] + os.sep, 'binary'),
2224
'download_url': 'https://www.bok.net/Bento4/binaries/Bento4-SDK-{version}.{platform}.zip',
2325
'versions': {
2426
'x64': 'x86_64-microsoft-win32',
@@ -27,7 +29,6 @@
2729
'executables': ['mp4decrypt.exe']
2830
},
2931
'darwin': {
30-
'base_dir': lambda home: os.path.join(home, 'Applications', 'binary'),
3132
'download_url': 'https://www.bok.net/Bento4/binaries/Bento4-SDK-{version}.{platform}.zip',
3233
'versions': {
3334
'x64': 'universal-apple-macosx',
@@ -36,7 +37,6 @@
3637
'executables': ['mp4decrypt']
3738
},
3839
'linux': {
39-
'base_dir': lambda home: os.path.join(home, '.local', 'bin', 'binary'),
4040
'download_url': 'https://www.bok.net/Bento4/binaries/Bento4-SDK-{version}.{platform}.zip',
4141
'versions': {
4242
'x64': 'x86_64-unknown-linux',
@@ -50,26 +50,11 @@
5050

5151
class Bento4Downloader:
5252
def __init__(self):
53-
self.os_name = platform.system().lower()
54-
self.arch = self._detect_arch()
55-
self.home_dir = os.path.expanduser('~')
56-
self.base_dir = BENTO4_CONFIGURATION[self.os_name]['base_dir'](self.home_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()
5757
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)
7358

7459
def _download_file(self, url: str, destination: str) -> bool:
7560
try:
@@ -160,32 +145,29 @@ def download(self) -> list:
160145
console.print(f"[bold red]Error downloading Bento4: {str(e)}[/]")
161146
return []
162147

148+
163149
def check_mp4decrypt() -> str:
164150
"""Check for mp4decrypt in the system and download if not found."""
165151
try:
166152
# First check if mp4decrypt is in PATH
167-
mp4decrypt = "mp4decrypt.exe" if platform.system().lower() == "windows" else "mp4decrypt"
153+
mp4decrypt = "mp4decrypt.exe" if binary_paths.system == "windows" else "mp4decrypt"
168154
mp4decrypt_path = shutil.which(mp4decrypt)
169155

170156
if mp4decrypt_path:
171157
return mp4decrypt_path
172158

173159
# If not found, check in binary directory
174-
downloader = Bento4Downloader()
175-
base_dir = downloader.base_dir
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)
177162

178163
if os.path.exists(local_path):
179164
return local_path
180165

181166
# Download if not found
167+
downloader = Bento4Downloader()
182168
extracted_files = downloader.download()
183169
return extracted_files[0] if extracted_files else None
184170

185171
except Exception as e:
186172
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 commit comments

Comments
 (0)