StreamingCommunity 1.8.0__py3-none-any.whl → 1.9.1__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/{Src/Api → Api}/Player/ddl.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Player/maxstream.py +2 -2
- StreamingCommunity/{Src/Api → Api}/Player/supervideo.py +2 -2
- StreamingCommunity/{Src/Api → Api}/Player/vixcloud.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/1337xx/__init__.py +1 -1
- StreamingCommunity/{Src/Api → Api}/Site/1337xx/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/1337xx/site.py +7 -7
- StreamingCommunity/{Src/Api → Api}/Site/1337xx/title.py +6 -6
- StreamingCommunity/{Src/Api → Api}/Site/altadefinizione/__init__.py +1 -1
- StreamingCommunity/{Src/Api/Site/mostraguarda → Api/Site/altadefinizione}/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/altadefinizione/film.py +8 -8
- StreamingCommunity/{Src/Api → Api}/Site/altadefinizione/site.py +7 -7
- StreamingCommunity/{Src/Api → Api}/Site/animeunity/__init__.py +1 -1
- StreamingCommunity/{Src/Api/Site/altadefinizione → Api/Site/animeunity}/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/animeunity/film_serie.py +18 -19
- StreamingCommunity/{Src/Api → Api}/Site/animeunity/site.py +6 -6
- StreamingCommunity/{Src/Api → Api}/Site/animeunity/util/ScrapeSerie.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/bitsearch/__init__.py +1 -1
- StreamingCommunity/{Src/Api → Api}/Site/bitsearch/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/bitsearch/site.py +7 -7
- StreamingCommunity/{Src/Api → Api}/Site/bitsearch/title.py +5 -5
- StreamingCommunity/{Src/Api → Api}/Site/cb01new/__init__.py +1 -1
- StreamingCommunity/{Src/Api → Api}/Site/cb01new/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/cb01new/film.py +8 -8
- StreamingCommunity/{Src/Api → Api}/Site/cb01new/site.py +6 -6
- StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/__init__.py +1 -1
- StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/series.py +11 -12
- StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/site.py +7 -7
- StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/util/ScrapeSerie.py +5 -3
- StreamingCommunity/{Src/Api → Api}/Site/guardaserie/__init__.py +1 -1
- StreamingCommunity/{Src/Api/Site/piratebays → Api/Site/guardaserie}/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/guardaserie/series.py +11 -11
- StreamingCommunity/{Src/Api → Api}/Site/guardaserie/site.py +7 -7
- StreamingCommunity/{Src/Api/Site/guardaserie/Player → Api/Site/guardaserie/util}/ScrapeSerie.py +2 -2
- StreamingCommunity/{Src/Api → Api}/Site/mostraguarda/__init__.py +2 -2
- StreamingCommunity/{Src/Api/Site/animeunity → Api/Site/mostraguarda}/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/mostraguarda/film.py +9 -9
- StreamingCommunity/{Src/Api → Api}/Site/piratebays/__init__.py +1 -1
- StreamingCommunity/{Src/Api/Site/guardaserie → Api/Site/piratebays}/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/piratebays/site.py +6 -6
- StreamingCommunity/{Src/Api → Api}/Site/piratebays/title.py +5 -5
- StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/__init__.py +1 -1
- StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/costant.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/film.py +10 -10
- StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/series.py +20 -18
- StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/site.py +7 -7
- StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/util/ScrapeSerie.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Template/Util/get_domain.py +3 -3
- StreamingCommunity/{Src/Api → Api}/Template/Util/manage_ep.py +2 -2
- StreamingCommunity/{Src/Api → Api}/Template/Util/recall_search.py +1 -1
- StreamingCommunity/{Src/Api → Api}/Template/site.py +1 -1
- StreamingCommunity/{Src/Lib → Lib}/Downloader/HLS/downloader.py +4 -4
- StreamingCommunity/{Src/Lib → Lib}/Downloader/HLS/proxyes.py +3 -3
- StreamingCommunity/{Src/Lib → Lib}/Downloader/HLS/segments.py +42 -44
- StreamingCommunity/{Src/Lib → Lib}/Downloader/MP4/downloader.py +5 -5
- StreamingCommunity/{Src/Lib → Lib}/Downloader/TOR/downloader.py +3 -3
- StreamingCommunity/{Src/Lib → Lib}/Driver/driver_1.py +1 -1
- StreamingCommunity/{Src/Lib → Lib}/FFmpeg/capture.py +2 -2
- StreamingCommunity/{Src/Lib → Lib}/FFmpeg/command.py +3 -3
- StreamingCommunity/{Src/Lib → Lib}/FFmpeg/util.py +1 -1
- StreamingCommunity/{Src/Lib → Lib}/M3U8/decryptor.py +59 -24
- StreamingCommunity/{Src/Lib → Lib}/M3U8/estimator.py +6 -3
- StreamingCommunity/{Src/Lib → Lib}/M3U8/parser.py +1 -1
- StreamingCommunity/{Src/Lib → Lib}/TMBD/tmdb.py +1 -1
- StreamingCommunity/{Src/Upload → Upload}/update.py +6 -2
- StreamingCommunity/{Src/Upload → Upload}/version.py +1 -1
- StreamingCommunity/Util/ffmpeg_installer.py +275 -0
- StreamingCommunity/{Src/Util → Util}/headers.py +1 -1
- StreamingCommunity/{Src/Util → Util}/logger.py +1 -1
- StreamingCommunity/{Src/Util → Util}/message.py +2 -2
- StreamingCommunity/{Src/Util → Util}/os.py +118 -21
- StreamingCommunity/run.py +18 -12
- {StreamingCommunity-1.8.0.dist-info → StreamingCommunity-1.9.1.dist-info}/METADATA +126 -60
- StreamingCommunity-1.9.1.dist-info/RECORD +95 -0
- {StreamingCommunity-1.8.0.dist-info → StreamingCommunity-1.9.1.dist-info}/WHEEL +1 -1
- StreamingCommunity/Src/Api/Site/animeunity/anime.py +0 -126
- StreamingCommunity/Src/Api/Site/ddlstreamitaly/Player/ScrapeSerie.py +0 -83
- StreamingCommunity/Src/Api/Site/guardaserie/util/ScrapeSerie.py +0 -110
- StreamingCommunity-1.8.0.dist-info/RECORD +0 -97
- /StreamingCommunity/{Src/Api → Api}/Player/Helper/Vixcloud/js_parser.py +0 -0
- /StreamingCommunity/{Src/Api → Api}/Player/Helper/Vixcloud/util.py +0 -0
- /StreamingCommunity/{Src/Api → Api}/Template/Class/SearchType.py +0 -0
- /StreamingCommunity/{Src/Api → Api}/Template/Util/__init__.py +0 -0
- /StreamingCommunity/{Src/Api → Api}/Template/__init__.py +0 -0
- /StreamingCommunity/{Src/Lib → Lib}/Downloader/__init__.py +0 -0
- /StreamingCommunity/{Src/Lib → Lib}/FFmpeg/__init__.py +0 -0
- /StreamingCommunity/{Src/Lib → Lib}/M3U8/__init__.py +0 -0
- /StreamingCommunity/{Src/Lib → Lib}/M3U8/url_fixer.py +0 -0
- /StreamingCommunity/{Src/Lib → Lib}/TMBD/__init__.py +0 -0
- /StreamingCommunity/{Src/Lib → Lib}/TMBD/obj_tmbd.py +0 -0
- /StreamingCommunity/{Src/Util → Util}/_jsonConfig.py +0 -0
- /StreamingCommunity/{Src/Util → Util}/call_stack.py +0 -0
- /StreamingCommunity/{Src/Util → Util}/color.py +0 -0
- /StreamingCommunity/{Src/Util → Util}/console.py +0 -0
- /StreamingCommunity/{Src/Util → Util}/table.py +0 -0
- {StreamingCommunity-1.8.0.dist-info → StreamingCommunity-1.9.1.dist-info}/LICENSE +0 -0
- {StreamingCommunity-1.8.0.dist-info → StreamingCommunity-1.9.1.dist-info}/entry_points.txt +0 -0
- {StreamingCommunity-1.8.0.dist-info → StreamingCommunity-1.9.1.dist-info}/top_level.txt +0 -0
|
@@ -19,12 +19,12 @@ from tqdm import tqdm
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
# Internal utilities
|
|
22
|
-
from StreamingCommunity.
|
|
23
|
-
from StreamingCommunity.
|
|
24
|
-
from StreamingCommunity.
|
|
25
|
-
from StreamingCommunity.
|
|
26
|
-
from StreamingCommunity.
|
|
27
|
-
from StreamingCommunity.
|
|
22
|
+
from StreamingCommunity.Util.console import console
|
|
23
|
+
from StreamingCommunity.Util.headers import get_headers, random_headers
|
|
24
|
+
from StreamingCommunity.Util.color import Colors
|
|
25
|
+
from StreamingCommunity.Util._jsonConfig import config_manager
|
|
26
|
+
from StreamingCommunity.Util.os import os_manager
|
|
27
|
+
from StreamingCommunity.Util.call_stack import get_call_stack
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
# Logic class
|
|
@@ -39,15 +39,19 @@ from .proxyes import main_test_proxy
|
|
|
39
39
|
# Config
|
|
40
40
|
TQDM_DELAY_WORKER = config_manager.get_float('M3U8_DOWNLOAD', 'tqdm_delay')
|
|
41
41
|
TQDM_USE_LARGE_BAR = config_manager.get_int('M3U8_DOWNLOAD', 'tqdm_use_large_bar')
|
|
42
|
+
|
|
42
43
|
REQUEST_MAX_RETRY = config_manager.get_int('REQUESTS', 'max_retry')
|
|
43
44
|
REQUEST_VERIFY = config_manager.get_bool('REQUESTS', 'verify_ssl')
|
|
45
|
+
|
|
44
46
|
THERE_IS_PROXY_LIST = os_manager.check_file("list_proxy.txt")
|
|
45
47
|
PROXY_START_MIN = config_manager.get_float('REQUESTS', 'proxy_start_min')
|
|
46
48
|
PROXY_START_MAX = config_manager.get_float('REQUESTS', 'proxy_start_max')
|
|
49
|
+
|
|
47
50
|
DEFAULT_VIDEO_WORKERS = config_manager.get_int('M3U8_DOWNLOAD', 'default_video_workser')
|
|
48
51
|
DEFAULT_AUDIO_WORKERS = config_manager.get_int('M3U8_DOWNLOAD', 'default_audio_workser')
|
|
49
52
|
|
|
50
53
|
|
|
54
|
+
|
|
51
55
|
# Variable
|
|
52
56
|
headers_index = config_manager.get_dict('REQUESTS', 'user-agent')
|
|
53
57
|
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
@@ -67,7 +71,8 @@ class M3U8_Segments:
|
|
|
67
71
|
self.url = url
|
|
68
72
|
self.tmp_folder = tmp_folder
|
|
69
73
|
self.is_index_url = is_index_url
|
|
70
|
-
self.expected_real_time = None
|
|
74
|
+
self.expected_real_time = None
|
|
75
|
+
self.max_timeout = max_timeout
|
|
71
76
|
|
|
72
77
|
self.tmp_file_path = os.path.join(self.tmp_folder, "0.ts")
|
|
73
78
|
os.makedirs(self.tmp_folder, exist_ok=True)
|
|
@@ -81,6 +86,8 @@ class M3U8_Segments:
|
|
|
81
86
|
self.queue = PriorityQueue()
|
|
82
87
|
self.stop_event = threading.Event()
|
|
83
88
|
self.downloaded_segments = set()
|
|
89
|
+
self.base_timeout = 1.0
|
|
90
|
+
self.current_timeout = 5.0
|
|
84
91
|
|
|
85
92
|
# Stopping
|
|
86
93
|
self.interrupt_flag = threading.Event()
|
|
@@ -214,9 +221,12 @@ class M3U8_Segments:
|
|
|
214
221
|
self.download_interrupted = True
|
|
215
222
|
self.stop_event.set()
|
|
216
223
|
|
|
217
|
-
|
|
224
|
+
if threading.current_thread() is threading.main_thread():
|
|
225
|
+
signal.signal(signal.SIGINT, interrupt_handler)
|
|
226
|
+
else:
|
|
227
|
+
print("Signal handler must be set in the main thread")
|
|
218
228
|
|
|
219
|
-
def make_requests_stream(self, ts_url: str, index: int, progress_bar: tqdm,
|
|
229
|
+
def make_requests_stream(self, ts_url: str, index: int, progress_bar: tqdm, backoff_factor: float = 1.5) -> None:
|
|
220
230
|
"""
|
|
221
231
|
Downloads a TS segment and adds it to the segment queue with retry logic.
|
|
222
232
|
|
|
@@ -226,14 +236,8 @@ class M3U8_Segments:
|
|
|
226
236
|
- progress_bar (tqdm): Progress counter for tracking download progress.
|
|
227
237
|
- retries (int): The number of times to retry on failure (default is 3).
|
|
228
238
|
- backoff_factor (float): The backoff factor for exponential backoff (default is 1.5 seconds).
|
|
229
|
-
"""
|
|
230
|
-
|
|
231
|
-
return
|
|
232
|
-
|
|
233
|
-
need_verify = REQUEST_VERIFY
|
|
234
|
-
min_segment_size = 100 # Minimum acceptable size for a TS segment in bytes
|
|
235
|
-
|
|
236
|
-
for attempt in range(retries):
|
|
239
|
+
"""
|
|
240
|
+
for attempt in range(REQUEST_MAX_RETRY):
|
|
237
241
|
if self.interrupt_flag.is_set():
|
|
238
242
|
return
|
|
239
243
|
|
|
@@ -247,7 +251,7 @@ class M3U8_Segments:
|
|
|
247
251
|
proxy = self.valid_proxy[index % len(self.valid_proxy)]
|
|
248
252
|
logging.info(f"Use proxy: {proxy}")
|
|
249
253
|
|
|
250
|
-
with httpx.Client(proxies=proxy, verify=
|
|
254
|
+
with httpx.Client(proxies=proxy, verify=REQUEST_VERIFY) as client:
|
|
251
255
|
if 'key_base_url' in self.__dict__:
|
|
252
256
|
response = client.get(
|
|
253
257
|
url=ts_url,
|
|
@@ -265,7 +269,7 @@ class M3U8_Segments:
|
|
|
265
269
|
)
|
|
266
270
|
|
|
267
271
|
else:
|
|
268
|
-
with httpx.Client(verify=
|
|
272
|
+
with httpx.Client(verify=REQUEST_VERIFY) as client_2:
|
|
269
273
|
if 'key_base_url' in self.__dict__:
|
|
270
274
|
response = client_2.get(
|
|
271
275
|
url=ts_url,
|
|
@@ -286,19 +290,12 @@ class M3U8_Segments:
|
|
|
286
290
|
response.raise_for_status()
|
|
287
291
|
segment_content = response.content
|
|
288
292
|
content_size = len(segment_content)
|
|
289
|
-
|
|
290
|
-
# Check if segment is too small (possibly corrupted or empty)
|
|
291
|
-
if content_size < min_segment_size:
|
|
292
|
-
raise httpx.RequestError(f"Segment {index} too small ({content_size} bytes)")
|
|
293
|
-
|
|
294
293
|
duration = time.time() - start_time
|
|
295
294
|
|
|
296
295
|
# Decrypt if needed and verify decrypted content
|
|
297
296
|
if self.decryption is not None:
|
|
298
297
|
try:
|
|
299
298
|
segment_content = self.decryption.decrypt(segment_content)
|
|
300
|
-
if len(segment_content) < min_segment_size:
|
|
301
|
-
raise Exception(f"Decrypted segment {index} too small ({len(segment_content)} bytes)")
|
|
302
299
|
|
|
303
300
|
except Exception as e:
|
|
304
301
|
logging.error(f"Decryption failed for segment {index}: {str(e)}")
|
|
@@ -318,10 +315,10 @@ class M3U8_Segments:
|
|
|
318
315
|
return
|
|
319
316
|
|
|
320
317
|
except Exception as e:
|
|
321
|
-
|
|
318
|
+
logging.info(f"Attempt {attempt + 1} failed for segment {index} - '{ts_url}': {e}")
|
|
322
319
|
|
|
323
|
-
if attempt + 1 ==
|
|
324
|
-
|
|
320
|
+
if attempt + 1 == REQUEST_MAX_RETRY:
|
|
321
|
+
console.log(f"[red]Final retry failed for segment: {index}")
|
|
325
322
|
self.queue.put((index, None)) # Marker for failed segment
|
|
326
323
|
progress_bar.update(1)
|
|
327
324
|
break
|
|
@@ -334,19 +331,20 @@ class M3U8_Segments:
|
|
|
334
331
|
"""
|
|
335
332
|
Writes segments to file with additional verification.
|
|
336
333
|
"""
|
|
334
|
+
buffer = {}
|
|
335
|
+
expected_index = 0
|
|
336
|
+
segments_written = set()
|
|
337
|
+
|
|
337
338
|
with open(self.tmp_file_path, 'wb') as f:
|
|
338
|
-
expected_index = 0
|
|
339
|
-
buffer = {}
|
|
340
|
-
total_written = 0
|
|
341
|
-
segments_written = set()
|
|
342
|
-
|
|
343
339
|
while not self.stop_event.is_set() or not self.queue.empty():
|
|
344
|
-
|
|
345
340
|
if self.interrupt_flag.is_set():
|
|
346
341
|
break
|
|
347
342
|
|
|
348
343
|
try:
|
|
349
|
-
index, segment_content = self.queue.get(timeout=
|
|
344
|
+
index, segment_content = self.queue.get(timeout=self.current_timeout)
|
|
345
|
+
|
|
346
|
+
# Successful queue retrieval: reduce timeout
|
|
347
|
+
self.current_timeout = max(self.base_timeout, self.current_timeout / 2)
|
|
350
348
|
|
|
351
349
|
# Handle failed segments
|
|
352
350
|
if segment_content is None:
|
|
@@ -357,7 +355,6 @@ class M3U8_Segments:
|
|
|
357
355
|
# Write segment if it's the next expected one
|
|
358
356
|
if index == expected_index:
|
|
359
357
|
f.write(segment_content)
|
|
360
|
-
total_written += len(segment_content)
|
|
361
358
|
segments_written.add(index)
|
|
362
359
|
f.flush()
|
|
363
360
|
expected_index += 1
|
|
@@ -365,26 +362,25 @@ class M3U8_Segments:
|
|
|
365
362
|
# Write any buffered segments that are now in order
|
|
366
363
|
while expected_index in buffer:
|
|
367
364
|
next_segment = buffer.pop(expected_index)
|
|
365
|
+
|
|
368
366
|
if next_segment is not None:
|
|
369
367
|
f.write(next_segment)
|
|
370
|
-
total_written += len(next_segment)
|
|
371
368
|
segments_written.add(expected_index)
|
|
372
369
|
f.flush()
|
|
370
|
+
|
|
373
371
|
expected_index += 1
|
|
372
|
+
|
|
374
373
|
else:
|
|
375
374
|
buffer[index] = segment_content
|
|
376
375
|
|
|
377
376
|
except queue.Empty:
|
|
377
|
+
self.current_timeout = min(self.max_timeout, self.current_timeout * 1.5)
|
|
378
|
+
|
|
378
379
|
if self.stop_event.is_set():
|
|
379
380
|
break
|
|
380
|
-
|
|
381
|
+
|
|
381
382
|
except Exception as e:
|
|
382
383
|
logging.error(f"Error writing segment {index}: {str(e)}")
|
|
383
|
-
continue
|
|
384
|
-
|
|
385
|
-
# Final verification
|
|
386
|
-
if total_written == 0:
|
|
387
|
-
raise Exception("No data written to file")
|
|
388
384
|
|
|
389
385
|
def download_streams(self, add_desc):
|
|
390
386
|
"""
|
|
@@ -493,8 +489,10 @@ class M3U8_Segments:
|
|
|
493
489
|
for index in missing_segments:
|
|
494
490
|
if self.interrupt_flag.is_set():
|
|
495
491
|
break
|
|
492
|
+
|
|
496
493
|
try:
|
|
497
494
|
self.make_requests_stream(self.segments[index], index, progress_bar)
|
|
495
|
+
|
|
498
496
|
except Exception as e:
|
|
499
497
|
logging.error(f"Failed to retry segment {index}: {str(e)}")
|
|
500
498
|
|
|
@@ -13,11 +13,11 @@ from tqdm import tqdm
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
# Internal utilities
|
|
16
|
-
from StreamingCommunity.
|
|
17
|
-
from StreamingCommunity.
|
|
18
|
-
from StreamingCommunity.
|
|
19
|
-
from StreamingCommunity.
|
|
20
|
-
from StreamingCommunity.
|
|
16
|
+
from StreamingCommunity.Util.headers import get_headers
|
|
17
|
+
from StreamingCommunity.Util.color import Colors
|
|
18
|
+
from StreamingCommunity.Util.console import console, Panel
|
|
19
|
+
from StreamingCommunity.Util._jsonConfig import config_manager
|
|
20
|
+
from StreamingCommunity.Util.os import internet_manager
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
# Logic class
|
|
@@ -8,9 +8,9 @@ import logging
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
# Internal utilities
|
|
11
|
-
from StreamingCommunity.
|
|
12
|
-
from StreamingCommunity.
|
|
13
|
-
from StreamingCommunity.
|
|
11
|
+
from StreamingCommunity.Util.color import Colors
|
|
12
|
+
from StreamingCommunity.Util.os import internet_manager
|
|
13
|
+
from StreamingCommunity.Util._jsonConfig import config_manager
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
# External libraries
|
|
@@ -7,8 +7,8 @@ import subprocess
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
# Internal utilities
|
|
10
|
-
from StreamingCommunity.
|
|
11
|
-
from StreamingCommunity.
|
|
10
|
+
from StreamingCommunity.Util.console import console
|
|
11
|
+
from StreamingCommunity.Util.os import internet_manager
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
# Variable
|
|
@@ -8,9 +8,9 @@ from typing import List, Dict
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
# Internal utilities
|
|
11
|
-
from StreamingCommunity.
|
|
12
|
-
from StreamingCommunity.
|
|
13
|
-
from StreamingCommunity.
|
|
11
|
+
from StreamingCommunity.Util._jsonConfig import config_manager
|
|
12
|
+
from StreamingCommunity.Util.os import os_manager, suppress_output
|
|
13
|
+
from StreamingCommunity.Util.console import console
|
|
14
14
|
from .util import need_to_force_to_ts, check_duration_v_a
|
|
15
15
|
from .capture import capture_ffmpeg_real_time
|
|
16
16
|
from ..M3U8 import M3U8_Codec
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# 03.04.24
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
|
+
import time
|
|
4
5
|
import logging
|
|
5
6
|
import subprocess
|
|
6
7
|
import importlib.util
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
# Internal utilities
|
|
10
|
-
from StreamingCommunity.
|
|
11
|
+
from StreamingCommunity.Util.console import console
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
# Check if Crypto module is installed
|
|
@@ -17,8 +18,8 @@ crypto_installed = crypto_spec is not None
|
|
|
17
18
|
|
|
18
19
|
if crypto_installed:
|
|
19
20
|
logging.info("Decrypy use: Crypto")
|
|
20
|
-
from Crypto.Cipher import AES
|
|
21
|
-
from Crypto.Util.Padding import unpad
|
|
21
|
+
from Crypto.Cipher import AES # type: ignore
|
|
22
|
+
from Crypto.Util.Padding import unpad # type: ignore
|
|
22
23
|
|
|
23
24
|
class M3U8_Decryption:
|
|
24
25
|
"""
|
|
@@ -34,12 +35,20 @@ if crypto_installed:
|
|
|
34
35
|
- method (str): The encryption method.
|
|
35
36
|
"""
|
|
36
37
|
self.key = key
|
|
37
|
-
|
|
38
|
+
self.iv = iv
|
|
39
|
+
if "0x" in str(iv):
|
|
38
40
|
self.iv = bytes.fromhex(iv.replace("0x", ""))
|
|
39
|
-
else:
|
|
40
|
-
self.iv = iv
|
|
41
41
|
self.method = method
|
|
42
|
-
|
|
42
|
+
|
|
43
|
+
# Precreate cipher based on encryption method
|
|
44
|
+
if self.method == "AES":
|
|
45
|
+
self.cipher = AES.new(self.key, AES.MODE_ECB)
|
|
46
|
+
elif self.method == "AES-128":
|
|
47
|
+
self.cipher = AES.new(self.key[:16], AES.MODE_CBC, iv=self.iv)
|
|
48
|
+
elif self.method == "AES-128-CTR":
|
|
49
|
+
self.cipher = AES.new(self.key[:16], AES.MODE_CTR, nonce=self.iv)
|
|
50
|
+
else:
|
|
51
|
+
raise ValueError("Invalid or unsupported method")
|
|
43
52
|
|
|
44
53
|
def decrypt(self, ciphertext: bytes) -> bytes:
|
|
45
54
|
"""
|
|
@@ -51,23 +60,34 @@ if crypto_installed:
|
|
|
51
60
|
Returns:
|
|
52
61
|
bytes: The decrypted content.
|
|
53
62
|
"""
|
|
54
|
-
|
|
55
|
-
cipher = AES.new(self.key, AES.MODE_ECB)
|
|
56
|
-
decrypted_data = cipher.decrypt(ciphertext)
|
|
57
|
-
return unpad(decrypted_data, AES.block_size)
|
|
58
|
-
|
|
59
|
-
elif self.method == "AES-128":
|
|
60
|
-
cipher = AES.new(self.key[:16], AES.MODE_CBC, iv=self.iv)
|
|
61
|
-
decrypted_data = cipher.decrypt(ciphertext)
|
|
62
|
-
return unpad(decrypted_data, AES.block_size)
|
|
63
|
+
start = time.perf_counter_ns()
|
|
63
64
|
|
|
65
|
+
# Decrypt based on encryption method
|
|
66
|
+
if self.method in {"AES", "AES-128"}:
|
|
67
|
+
decrypted_data = self.cipher.decrypt(ciphertext)
|
|
68
|
+
decrypted_content = unpad(decrypted_data, AES.block_size)
|
|
69
|
+
|
|
64
70
|
elif self.method == "AES-128-CTR":
|
|
65
|
-
|
|
66
|
-
return cipher.decrypt(ciphertext)
|
|
67
|
-
|
|
71
|
+
decrypted_content = self.cipher.decrypt(ciphertext)
|
|
68
72
|
else:
|
|
69
73
|
raise ValueError("Invalid or unsupported method")
|
|
70
74
|
|
|
75
|
+
end = time.perf_counter_ns()
|
|
76
|
+
|
|
77
|
+
# Calculate elapsed time with high precision
|
|
78
|
+
elapsed_nanoseconds = end - start
|
|
79
|
+
elapsed_milliseconds = elapsed_nanoseconds / 1_000_000
|
|
80
|
+
elapsed_seconds = elapsed_nanoseconds / 1_000_000_000
|
|
81
|
+
|
|
82
|
+
# Print performance metrics
|
|
83
|
+
logging.info(f"[Crypto Decryption Performance]")
|
|
84
|
+
logging.info(f"Method: {self.method}")
|
|
85
|
+
logging.info(f"Decryption Time: {elapsed_milliseconds:.4f} ms ({elapsed_seconds:.6f} s)")
|
|
86
|
+
logging.info(f"Decrypted Content Length: {len(decrypted_content)} bytes")
|
|
87
|
+
|
|
88
|
+
return decrypted_content
|
|
89
|
+
|
|
90
|
+
|
|
71
91
|
else:
|
|
72
92
|
|
|
73
93
|
# Check if openssl command is available
|
|
@@ -95,10 +115,9 @@ else:
|
|
|
95
115
|
- method (str): The encryption method.
|
|
96
116
|
"""
|
|
97
117
|
self.key = key
|
|
98
|
-
|
|
118
|
+
self.iv = iv
|
|
119
|
+
if "0x" in str(iv):
|
|
99
120
|
self.iv = bytes.fromhex(iv.replace("0x", ""))
|
|
100
|
-
else:
|
|
101
|
-
self.iv = iv
|
|
102
121
|
self.method = method
|
|
103
122
|
logging.info(f"Decrypt add: ('key': {self.key}, 'iv': {self.iv}, 'method': {self.method})")
|
|
104
123
|
|
|
@@ -112,6 +131,9 @@ else:
|
|
|
112
131
|
Returns:
|
|
113
132
|
bytes: The decrypted content.
|
|
114
133
|
"""
|
|
134
|
+
start = time.perf_counter_ns()
|
|
135
|
+
|
|
136
|
+
# Construct OpenSSL command based on encryption method
|
|
115
137
|
if self.method == "AES":
|
|
116
138
|
openssl_cmd = f'openssl enc -d -aes-256-ecb -K {self.key.hex()} -nosalt'
|
|
117
139
|
elif self.method == "AES-128":
|
|
@@ -122,8 +144,21 @@ else:
|
|
|
122
144
|
raise ValueError("Invalid or unsupported method")
|
|
123
145
|
|
|
124
146
|
try:
|
|
125
|
-
|
|
147
|
+
decrypted_content = subprocess.check_output(openssl_cmd.split(), input=ciphertext, stderr=subprocess.STDOUT)
|
|
126
148
|
except subprocess.CalledProcessError as e:
|
|
127
149
|
raise ValueError(f"Decryption failed: {e.output.decode()}")
|
|
128
150
|
|
|
129
|
-
|
|
151
|
+
end = time.perf_counter_ns()
|
|
152
|
+
|
|
153
|
+
# Calculate elapsed time with high precision
|
|
154
|
+
elapsed_nanoseconds = end - start
|
|
155
|
+
elapsed_milliseconds = elapsed_nanoseconds / 1_000_000
|
|
156
|
+
elapsed_seconds = elapsed_nanoseconds / 1_000_000_000
|
|
157
|
+
|
|
158
|
+
# Print performance metrics
|
|
159
|
+
logging.info(f"[OpenSSL Decryption Performance]")
|
|
160
|
+
logging.info(f"Method: {self.method}")
|
|
161
|
+
logging.info(f"Decryption Time: {elapsed_milliseconds:.4f} ms ({elapsed_seconds:.6f} s)")
|
|
162
|
+
logging.info(f"Decrypted Content Length: {len(decrypted_content)} bytes")
|
|
163
|
+
|
|
164
|
+
return decrypted_content
|
|
@@ -13,9 +13,9 @@ from tqdm import tqdm
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
# Internal utilities
|
|
16
|
-
from StreamingCommunity.
|
|
17
|
-
from StreamingCommunity.
|
|
18
|
-
from StreamingCommunity.
|
|
16
|
+
from StreamingCommunity.Util.color import Colors
|
|
17
|
+
from StreamingCommunity.Util.os import internet_manager
|
|
18
|
+
from StreamingCommunity.Util._jsonConfig import config_manager
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
# Variable
|
|
@@ -65,18 +65,21 @@ class M3U8_Ts_Estimator:
|
|
|
65
65
|
try:
|
|
66
66
|
io_counters = psutil.net_io_counters()
|
|
67
67
|
return io_counters
|
|
68
|
+
|
|
68
69
|
except Exception as e:
|
|
69
70
|
logging.warning(f"Unable to access network I/O counters: {e}")
|
|
70
71
|
return None
|
|
71
72
|
|
|
72
73
|
while True:
|
|
73
74
|
old_value = get_network_io()
|
|
75
|
+
|
|
74
76
|
if old_value is None: # If psutil is not available, continue with default values
|
|
75
77
|
time.sleep(interval)
|
|
76
78
|
continue
|
|
77
79
|
|
|
78
80
|
time.sleep(interval)
|
|
79
81
|
new_value = get_network_io()
|
|
82
|
+
|
|
80
83
|
if new_value is None: # Handle again if psutil fails in the next call
|
|
81
84
|
time.sleep(interval)
|
|
82
85
|
continue
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# 01.03.2023
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
import sys
|
|
4
5
|
import time
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
# Internal utilities
|
|
8
9
|
from .version import __version__, __author__, __title__
|
|
9
|
-
from StreamingCommunity.
|
|
10
|
+
from StreamingCommunity.Util.console import console
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
# External library
|
|
@@ -14,7 +15,10 @@ import httpx
|
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
# Variable
|
|
17
|
-
|
|
18
|
+
if getattr(sys, 'frozen', False): # Modalità PyInstaller
|
|
19
|
+
base_path = os.path.join(sys._MEIPASS, "StreamingCommunity")
|
|
20
|
+
else:
|
|
21
|
+
base_path = os.path.dirname(__file__)
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
def update():
|