StreamingCommunity 2.5.6__py3-none-any.whl → 2.5.8__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/Player/ddl.py +2 -3
- StreamingCommunity/Api/Site/1337xx/__init__.py +5 -6
- StreamingCommunity/Api/Site/1337xx/site.py +7 -14
- StreamingCommunity/Api/Site/1337xx/title.py +3 -5
- StreamingCommunity/Api/Site/altadefinizionegratis/__init__.py +7 -6
- StreamingCommunity/Api/Site/altadefinizionegratis/film.py +14 -19
- StreamingCommunity/Api/Site/altadefinizionegratis/site.py +6 -14
- StreamingCommunity/Api/Site/animeunity/__init__.py +7 -7
- StreamingCommunity/Api/Site/animeunity/film_serie.py +29 -31
- StreamingCommunity/Api/Site/animeunity/site.py +14 -22
- StreamingCommunity/Api/Site/cb01new/__init__.py +5 -4
- StreamingCommunity/Api/Site/cb01new/film.py +2 -5
- StreamingCommunity/Api/Site/cb01new/site.py +5 -13
- StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +5 -4
- StreamingCommunity/Api/Site/ddlstreamitaly/series.py +12 -49
- StreamingCommunity/Api/Site/ddlstreamitaly/site.py +6 -16
- StreamingCommunity/Api/Site/ddlstreamitaly/util/ScrapeSerie.py +2 -3
- StreamingCommunity/Api/Site/guardaserie/__init__.py +5 -4
- StreamingCommunity/Api/Site/guardaserie/series.py +12 -46
- StreamingCommunity/Api/Site/guardaserie/site.py +5 -13
- StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +10 -14
- StreamingCommunity/Api/Site/ilcorsaronero/__init__.py +5 -4
- StreamingCommunity/Api/Site/ilcorsaronero/site.py +5 -13
- StreamingCommunity/Api/Site/ilcorsaronero/title.py +3 -5
- StreamingCommunity/Api/Site/mostraguarda/__init__.py +2 -2
- StreamingCommunity/Api/Site/mostraguarda/film.py +4 -8
- StreamingCommunity/Api/Site/streamingcommunity/__init__.py +8 -7
- StreamingCommunity/Api/Site/streamingcommunity/film.py +14 -18
- StreamingCommunity/Api/Site/streamingcommunity/series.py +25 -76
- StreamingCommunity/Api/Site/streamingcommunity/site.py +11 -23
- StreamingCommunity/Api/Template/Util/__init__.py +8 -1
- StreamingCommunity/Api/Template/Util/manage_ep.py +46 -2
- StreamingCommunity/Api/Template/config_loader.py +71 -0
- StreamingCommunity/Lib/Downloader/HLS/downloader.py +60 -60
- StreamingCommunity/Lib/Downloader/HLS/segments.py +40 -15
- StreamingCommunity/Lib/Downloader/MP4/downloader.py +47 -40
- StreamingCommunity/Lib/FFmpeg/command.py +59 -3
- StreamingCommunity/Lib/M3U8/estimator.py +10 -12
- StreamingCommunity/Lib/M3U8/parser.py +12 -51
- StreamingCommunity/Lib/TMBD/tmdb.py +66 -99
- StreamingCommunity/TelegramHelp/telegram_bot.py +222 -68
- StreamingCommunity/Util/_jsonConfig.py +14 -13
- StreamingCommunity/Util/ffmpeg_installer.py +70 -64
- StreamingCommunity/Util/headers.py +11 -122
- StreamingCommunity/Util/os.py +65 -56
- StreamingCommunity/Util/table.py +62 -108
- StreamingCommunity/run.py +16 -11
- {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/METADATA +57 -23
- StreamingCommunity-2.5.8.dist-info/RECORD +86 -0
- StreamingCommunity/Api/Site/1337xx/costant.py +0 -15
- StreamingCommunity/Api/Site/altadefinizionegratis/costant.py +0 -21
- StreamingCommunity/Api/Site/animeunity/costant.py +0 -21
- StreamingCommunity/Api/Site/cb01new/costant.py +0 -19
- StreamingCommunity/Api/Site/ddlstreamitaly/costant.py +0 -20
- StreamingCommunity/Api/Site/guardaserie/costant.py +0 -19
- StreamingCommunity/Api/Site/ilcorsaronero/costant.py +0 -19
- StreamingCommunity/Api/Site/mostraguarda/costant.py +0 -19
- StreamingCommunity/Api/Site/streamingcommunity/costant.py +0 -21
- StreamingCommunity/TelegramHelp/request_manager.py +0 -82
- StreamingCommunity/TelegramHelp/session.py +0 -56
- StreamingCommunity-2.5.6.dist-info/RECORD +0 -96
- {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/LICENSE +0 -0
- {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/WHEEL +0 -0
- {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/entry_points.txt +0 -0
- {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/top_level.txt +0 -0
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import os
|
|
4
4
|
import re
|
|
5
5
|
import sys
|
|
6
|
+
import time
|
|
6
7
|
import signal
|
|
7
8
|
import logging
|
|
8
9
|
from functools import partial
|
|
@@ -39,21 +40,38 @@ TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
|
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
class InterruptHandler:
|
|
44
|
+
def __init__(self):
|
|
45
|
+
self.interrupt_count = 0
|
|
46
|
+
self.last_interrupt_time = 0
|
|
47
|
+
self.kill_download = False
|
|
48
|
+
self.force_quit = False
|
|
46
49
|
|
|
50
|
+
def signal_handler(signum, frame, interrupt_handler, original_handler):
|
|
51
|
+
"""Enhanced signal handler for multiple interrupt scenarios"""
|
|
52
|
+
current_time = time.time()
|
|
53
|
+
|
|
54
|
+
# Reset counter if more than 2 seconds have passed since last interrupt
|
|
55
|
+
if current_time - interrupt_handler.last_interrupt_time > 2:
|
|
56
|
+
interrupt_handler.interrupt_count = 0
|
|
57
|
+
|
|
58
|
+
interrupt_handler.interrupt_count += 1
|
|
59
|
+
interrupt_handler.last_interrupt_time = current_time
|
|
60
|
+
|
|
61
|
+
if interrupt_handler.interrupt_count == 1:
|
|
62
|
+
interrupt_handler.kill_download = True
|
|
63
|
+
console.print("\n[bold yellow]First interrupt received. Download will complete and save. Press Ctrl+C three times quickly to force quit.[/bold yellow]")
|
|
64
|
+
|
|
65
|
+
elif interrupt_handler.interrupt_count >= 3:
|
|
66
|
+
interrupt_handler.force_quit = True
|
|
67
|
+
console.print("\n[bold red]Force quit activated. Saving partial download...[/bold red]")
|
|
68
|
+
signal.signal(signum, original_handler)
|
|
47
69
|
|
|
48
70
|
def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = None):
|
|
49
71
|
"""
|
|
50
|
-
Downloads an MP4 video
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
- url (str): The URL of the MP4 video to download.
|
|
54
|
-
- path (str): The local path where the downloaded MP4 file will be saved.
|
|
55
|
-
- referer (str, optional): The referer header value.
|
|
56
|
-
- headers_ (dict, optional): Custom headers for the request.
|
|
72
|
+
Downloads an MP4 video with enhanced interrupt handling.
|
|
73
|
+
- Single Ctrl+C: Completes download gracefully
|
|
74
|
+
- Triple Ctrl+C: Saves partial download and exits
|
|
57
75
|
"""
|
|
58
76
|
if TELEGRAM_BOT:
|
|
59
77
|
bot = get_bot_instance()
|
|
@@ -65,23 +83,19 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
|
|
|
65
83
|
bot.send_message(f"Contenuto già scaricato!", None)
|
|
66
84
|
return 400
|
|
67
85
|
|
|
68
|
-
# Early return for link-only mode
|
|
69
86
|
if GET_ONLY_LINK:
|
|
70
87
|
return {'path': path, 'url': url}
|
|
71
88
|
|
|
72
|
-
# Validate URL
|
|
73
89
|
if not (url.lower().startswith('http://') or url.lower().startswith('https://')):
|
|
74
90
|
logging.error(f"Invalid URL: {url}")
|
|
75
91
|
console.print(f"[bold red]Invalid URL: {url}[/bold red]")
|
|
76
92
|
return None
|
|
77
93
|
|
|
78
|
-
# Prepare headers
|
|
79
94
|
try:
|
|
80
95
|
headers = {}
|
|
81
96
|
if referer:
|
|
82
97
|
headers['Referer'] = referer
|
|
83
98
|
|
|
84
|
-
# Use custom headers if provided, otherwise use default user agent
|
|
85
99
|
if headers_:
|
|
86
100
|
headers.update(headers_)
|
|
87
101
|
else:
|
|
@@ -93,17 +107,12 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
|
|
|
93
107
|
return None
|
|
94
108
|
|
|
95
109
|
temp_path = f"{path}.temp"
|
|
96
|
-
|
|
97
|
-
original_handler = signal.signal(signal.SIGINT, partial(signal_handler,
|
|
110
|
+
interrupt_handler = InterruptHandler()
|
|
111
|
+
original_handler = signal.signal(signal.SIGINT, partial(signal_handler, interrupt_handler=interrupt_handler, original_handler=signal.getsignal(signal.SIGINT)))
|
|
98
112
|
|
|
99
113
|
try:
|
|
100
|
-
|
|
101
|
-
transport = httpx.HTTPTransport(
|
|
102
|
-
verify=False,
|
|
103
|
-
http2=True
|
|
104
|
-
)
|
|
114
|
+
transport = httpx.HTTPTransport(verify=False, http2=True)
|
|
105
115
|
|
|
106
|
-
# Download with streaming and progress tracking
|
|
107
116
|
with httpx.Client(transport=transport, timeout=httpx.Timeout(60)) as client:
|
|
108
117
|
with client.stream("GET", url, headers=headers, timeout=REQUEST_TIMEOUT) as response:
|
|
109
118
|
response.raise_for_status()
|
|
@@ -119,21 +128,22 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
|
|
|
119
128
|
bar_format=f"{Colors.YELLOW}[MP4]{Colors.WHITE}: "
|
|
120
129
|
f"{Colors.RED}{{percentage:.2f}}% {Colors.MAGENTA}{{bar}} {Colors.WHITE}[ "
|
|
121
130
|
f"{Colors.YELLOW}{{n_fmt}}{Colors.WHITE} / {Colors.RED}{{total_fmt}} {Colors.WHITE}] "
|
|
122
|
-
f"{Colors.YELLOW}{{elapsed}} {Colors.WHITE}< {Colors.CYAN}{{remaining}}
|
|
123
|
-
f"{Colors.YELLOW}{{rate_fmt}}{{postfix}}
|
|
131
|
+
f"{Colors.YELLOW}{{elapsed}} {Colors.WHITE}< {Colors.CYAN}{{remaining}}{Colors.WHITE}, "
|
|
132
|
+
f"{Colors.YELLOW}{{rate_fmt}}{{postfix}} ",
|
|
124
133
|
unit='iB',
|
|
125
134
|
unit_scale=True,
|
|
126
135
|
desc='Downloading',
|
|
127
|
-
mininterval=0.05
|
|
136
|
+
mininterval=0.05,
|
|
137
|
+
file=sys.stdout # Using file=sys.stdout to force in-place updates because sys.stderr may not support carriage returns in this environment.
|
|
128
138
|
)
|
|
129
139
|
|
|
130
140
|
downloaded = 0
|
|
131
141
|
with open(temp_path, 'wb') as file, progress_bar as bar:
|
|
132
142
|
try:
|
|
133
143
|
for chunk in response.iter_bytes(chunk_size=1024):
|
|
134
|
-
if
|
|
135
|
-
console.print("\n[bold
|
|
136
|
-
|
|
144
|
+
if interrupt_handler.force_quit:
|
|
145
|
+
console.print("\n[bold red]Force quitting... Saving partial download.[/bold red]")
|
|
146
|
+
break
|
|
137
147
|
|
|
138
148
|
if chunk:
|
|
139
149
|
size = file.write(chunk)
|
|
@@ -141,18 +151,15 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
|
|
|
141
151
|
bar.update(size)
|
|
142
152
|
|
|
143
153
|
except KeyboardInterrupt:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
os.remove(temp_path)
|
|
147
|
-
return None, True
|
|
154
|
+
if not interrupt_handler.force_quit:
|
|
155
|
+
interrupt_handler.kill_download = True
|
|
148
156
|
|
|
149
|
-
# Rename temp file to final file
|
|
150
157
|
if os.path.exists(temp_path):
|
|
151
158
|
os.rename(temp_path, path)
|
|
152
159
|
|
|
153
160
|
if os.path.exists(path):
|
|
154
161
|
console.print(Panel(
|
|
155
|
-
f"[bold green]Download completed![/bold green]\n"
|
|
162
|
+
f"[bold green]Download completed{' (Partial)' if interrupt_handler.force_quit else ''}![/bold green]\n"
|
|
156
163
|
f"[cyan]File size: [bold red]{internet_manager.format_file_size(os.path.getsize(path))}[/bold red]\n"
|
|
157
164
|
f"[cyan]Duration: [bold]{print_duration_table(path, description=False, return_string=True)}[/bold]",
|
|
158
165
|
title=f"{os.path.basename(path.replace('.mp4', ''))}",
|
|
@@ -160,22 +167,22 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
|
|
|
160
167
|
))
|
|
161
168
|
|
|
162
169
|
if TELEGRAM_BOT:
|
|
163
|
-
message = f"Download completato\nDimensione: {internet_manager.format_file_size(os.path.getsize(path))}\nDurata: {print_duration_table(path, description=False, return_string=True)}\nTitolo: {os.path.basename(path.replace('.mp4', ''))}"
|
|
170
|
+
message = f"Download completato{'(Parziale)' if interrupt_handler.force_quit else ''}\nDimensione: {internet_manager.format_file_size(os.path.getsize(path))}\nDurata: {print_duration_table(path, description=False, return_string=True)}\nTitolo: {os.path.basename(path.replace('.mp4', ''))}"
|
|
164
171
|
clean_message = re.sub(r'\[[a-zA-Z]+\]', '', message)
|
|
165
172
|
bot.send_message(clean_message, None)
|
|
166
173
|
|
|
167
|
-
return path,
|
|
174
|
+
return path, interrupt_handler.kill_download
|
|
175
|
+
|
|
168
176
|
else:
|
|
169
177
|
console.print("[bold red]Download failed or file is empty.[/bold red]")
|
|
170
|
-
return None,
|
|
178
|
+
return None, interrupt_handler.kill_download
|
|
171
179
|
|
|
172
180
|
except Exception as e:
|
|
173
181
|
logging.error(f"Unexpected error: {e}")
|
|
174
182
|
console.print(f"[bold red]Unexpected Error: {e}[/bold red]")
|
|
175
183
|
if os.path.exists(temp_path):
|
|
176
184
|
os.remove(temp_path)
|
|
177
|
-
return None,
|
|
185
|
+
return None, interrupt_handler.kill_download
|
|
178
186
|
|
|
179
187
|
finally:
|
|
180
|
-
# Restore original signal handler
|
|
181
188
|
signal.signal(signal.SIGINT, original_handler)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import sys
|
|
4
4
|
import logging
|
|
5
5
|
import subprocess
|
|
6
|
-
from typing import List, Dict
|
|
6
|
+
from typing import List, Dict, Tuple, Optional
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
# Internal utilities
|
|
@@ -34,6 +34,62 @@ USE_LARGE_BAR = not ("android" in sys.platform or "ios" in sys.platform)
|
|
|
34
34
|
FFMPEG_PATH = os_summary.ffmpeg_path
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
|
|
38
|
+
def check_subtitle_encoders() -> Tuple[Optional[bool], Optional[bool]]:
|
|
39
|
+
"""
|
|
40
|
+
Executes 'ffmpeg -encoders' and checks if 'mov_text' and 'webvtt' encoders are available.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Tuple[Optional[bool], Optional[bool]]: A tuple containing (mov_text_supported, webvtt_supported)
|
|
44
|
+
Returns (None, None) if the FFmpeg command fails
|
|
45
|
+
"""
|
|
46
|
+
try:
|
|
47
|
+
result = subprocess.run(
|
|
48
|
+
[FFMPEG_PATH, '-encoders'],
|
|
49
|
+
capture_output=True,
|
|
50
|
+
text=True,
|
|
51
|
+
check=True
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Check for encoder presence in output
|
|
55
|
+
output = result.stdout
|
|
56
|
+
mov_text_supported = "mov_text" in output
|
|
57
|
+
webvtt_supported = "webvtt" in output
|
|
58
|
+
|
|
59
|
+
return mov_text_supported, webvtt_supported
|
|
60
|
+
|
|
61
|
+
except subprocess.CalledProcessError as e:
|
|
62
|
+
print(f"Error executing 'ffmpeg -encoders': {e}")
|
|
63
|
+
return None, None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def select_subtitle_encoder() -> Optional[str]:
|
|
67
|
+
"""
|
|
68
|
+
Determines the best available subtitle encoder to use.
|
|
69
|
+
Prefers mov_text over webvtt if both are available.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Optional[str]: Name of the best available encoder ('mov_text' or 'webvtt')
|
|
73
|
+
or None if no supported encoder is found
|
|
74
|
+
"""
|
|
75
|
+
mov_text_supported, webvtt_supported = check_subtitle_encoders()
|
|
76
|
+
|
|
77
|
+
# Return early if check failed
|
|
78
|
+
if mov_text_supported is None:
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
# Prioritize mov_text over webvtt
|
|
82
|
+
if mov_text_supported:
|
|
83
|
+
logging.info("Using 'mov_text' as the subtitle encoder.")
|
|
84
|
+
return "mov_text"
|
|
85
|
+
elif webvtt_supported:
|
|
86
|
+
logging.info("Using 'webvtt' as the subtitle encoder.")
|
|
87
|
+
return "webvtt"
|
|
88
|
+
|
|
89
|
+
logging.error("No supported subtitle encoder found.")
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
|
|
37
93
|
def join_video(video_path: str, out_path: str, codec: M3U8_Codec = None):
|
|
38
94
|
"""
|
|
39
95
|
Joins single ts video file to mp4
|
|
@@ -238,9 +294,9 @@ def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_pat
|
|
|
238
294
|
|
|
239
295
|
# Add output Parameters
|
|
240
296
|
if USE_CODEC:
|
|
241
|
-
ffmpeg_cmd.extend(['-c:v', 'copy', '-c:a', 'copy', '-c:s',
|
|
297
|
+
ffmpeg_cmd.extend(['-c:v', 'copy', '-c:a', 'copy', '-c:s', select_subtitle_encoder()])
|
|
242
298
|
else:
|
|
243
|
-
ffmpeg_cmd.extend(['-c', 'copy', '-c:s',
|
|
299
|
+
ffmpeg_cmd.extend(['-c', 'copy', '-c:s', select_subtitle_encoder()])
|
|
244
300
|
|
|
245
301
|
# Overwrite
|
|
246
302
|
ffmpeg_cmd += [out_path, "-y"]
|
|
@@ -106,12 +106,12 @@ class M3U8_Ts_Estimator:
|
|
|
106
106
|
try:
|
|
107
107
|
self.add_ts_file(total_downloaded * self.total_segments, total_downloaded, duration)
|
|
108
108
|
|
|
109
|
-
downloaded_file_size_str = internet_manager.format_file_size(self.now_downloaded_size)
|
|
109
|
+
#downloaded_file_size_str = internet_manager.format_file_size(self.now_downloaded_size)
|
|
110
110
|
file_total_size = self.calculate_total_size()
|
|
111
111
|
|
|
112
|
-
number_file_downloaded = downloaded_file_size_str.split(' ')[0]
|
|
112
|
+
#number_file_downloaded = downloaded_file_size_str.split(' ')[0]
|
|
113
113
|
number_file_total_size = file_total_size.split(' ')[0]
|
|
114
|
-
units_file_downloaded = downloaded_file_size_str.split(' ')[1]
|
|
114
|
+
#units_file_downloaded = downloaded_file_size_str.split(' ')[1]
|
|
115
115
|
units_file_total_size = file_total_size.split(' ')[1]
|
|
116
116
|
|
|
117
117
|
if USE_LARGE_BAR:
|
|
@@ -124,21 +124,19 @@ class M3U8_Ts_Estimator:
|
|
|
124
124
|
average_internet_speed = "N/A"
|
|
125
125
|
average_internet_unit = ""
|
|
126
126
|
|
|
127
|
-
# Retrieve retry count from segments_instance
|
|
128
127
|
retry_count = self.segments_instance.active_retries if self.segments_instance else 0
|
|
129
128
|
progress_str = (
|
|
130
|
-
f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< "
|
|
131
|
-
f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size}
|
|
132
|
-
f"{Colors.WHITE}
|
|
133
|
-
f"{Colors.WHITE}
|
|
129
|
+
#f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< "
|
|
130
|
+
f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size}"
|
|
131
|
+
f"{Colors.WHITE} {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit}"
|
|
132
|
+
f"{Colors.WHITE} {Colors.GREEN}CRR {Colors.RED}{retry_count} "
|
|
134
133
|
)
|
|
135
134
|
else:
|
|
136
|
-
# Retrieve retry count from segments_instance
|
|
137
135
|
retry_count = self.segments_instance.active_retries if self.segments_instance else 0
|
|
138
136
|
progress_str = (
|
|
139
|
-
f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< "
|
|
140
|
-
f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size}
|
|
141
|
-
f"{Colors.WHITE}
|
|
137
|
+
#f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< "
|
|
138
|
+
f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size}"
|
|
139
|
+
f"{Colors.WHITE} {Colors.GREEN}CRR {Colors.RED}{retry_count} "
|
|
142
140
|
)
|
|
143
141
|
|
|
144
142
|
progress_counter.set_postfix_str(progress_str)
|
|
@@ -39,12 +39,17 @@ CODEC_MAPPINGS = {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
RESOLUTIONS = [
|
|
42
|
-
(7680, 4320),
|
|
43
|
-
(3840, 2160),
|
|
44
|
-
(2560, 1440),
|
|
45
|
-
(1920, 1080),
|
|
46
|
-
(1280, 720),
|
|
47
|
-
(640, 480)
|
|
42
|
+
(7680, 4320), # 8K
|
|
43
|
+
(3840, 2160), # 4K
|
|
44
|
+
(2560, 1440), # 1440p
|
|
45
|
+
(1920, 1080), # 1080p
|
|
46
|
+
(1280, 720), # 720p
|
|
47
|
+
(640, 480), # VGA
|
|
48
|
+
(640, 360), # 360p
|
|
49
|
+
(480, 320), # HVGA
|
|
50
|
+
(426, 240), # 240p
|
|
51
|
+
(320, 240), # QVGA
|
|
52
|
+
(256, 144), # 144p
|
|
48
53
|
]
|
|
49
54
|
|
|
50
55
|
|
|
@@ -377,47 +382,6 @@ class M3U8_Subtitle:
|
|
|
377
382
|
return subtitle
|
|
378
383
|
return None
|
|
379
384
|
|
|
380
|
-
def download_all(self, custom_subtitle):
|
|
381
|
-
"""
|
|
382
|
-
Download all subtitles listed in the object's attributes, filtering based on a provided list of custom subtitles.
|
|
383
|
-
|
|
384
|
-
Parameters:
|
|
385
|
-
- custom_subtitle (list): A list of custom subtitles to download.
|
|
386
|
-
|
|
387
|
-
Returns:
|
|
388
|
-
list: A list containing dictionaries with subtitle information including name, language, and URI.
|
|
389
|
-
"""
|
|
390
|
-
|
|
391
|
-
output = [] # Initialize an empty list to store subtitle information
|
|
392
|
-
|
|
393
|
-
# Iterate through all available subtitles
|
|
394
|
-
for obj_subtitle in self.subtitle_get_all_uris_and_names():
|
|
395
|
-
|
|
396
|
-
# Check if the subtitle name is not in the list of custom subtitles, and skip if not found
|
|
397
|
-
if obj_subtitle.get('name') not in custom_subtitle:
|
|
398
|
-
continue
|
|
399
|
-
|
|
400
|
-
# Send a request to retrieve the subtitle content
|
|
401
|
-
logging.info(f"Download subtitle: {obj_subtitle.get('name')}")
|
|
402
|
-
response_subitle = httpx.get(obj_subtitle.get('uri'))
|
|
403
|
-
|
|
404
|
-
try:
|
|
405
|
-
# Try to extract the VTT URL from the subtitle content
|
|
406
|
-
sub_parse = M3U8_Parser()
|
|
407
|
-
sub_parse.parse_data(obj_subtitle.get('uri'), response_subitle.text)
|
|
408
|
-
url_subititle = sub_parse.subtitle[0]
|
|
409
|
-
|
|
410
|
-
output.append({
|
|
411
|
-
'name': obj_subtitle.get('name'),
|
|
412
|
-
'language': obj_subtitle.get('language'),
|
|
413
|
-
'uri': url_subititle
|
|
414
|
-
})
|
|
415
|
-
|
|
416
|
-
except Exception as e:
|
|
417
|
-
logging.error(f"Cant download: {obj_subtitle.get('name')}, error: {e}")
|
|
418
|
-
|
|
419
|
-
return output
|
|
420
|
-
|
|
421
385
|
|
|
422
386
|
class M3U8_Parser:
|
|
423
387
|
def __init__(self):
|
|
@@ -555,7 +519,6 @@ class M3U8_Parser:
|
|
|
555
519
|
- m3u8_obj: The M3U8 object containing encryption keys.
|
|
556
520
|
"""
|
|
557
521
|
try:
|
|
558
|
-
|
|
559
522
|
if m3u8_obj.key is not None:
|
|
560
523
|
if self.keys is None:
|
|
561
524
|
self.keys = {
|
|
@@ -564,7 +527,6 @@ class M3U8_Parser:
|
|
|
564
527
|
'uri': m3u8_obj.key.uri
|
|
565
528
|
}
|
|
566
529
|
|
|
567
|
-
|
|
568
530
|
except Exception as e:
|
|
569
531
|
logging.error(f"Error parsing encryption keys: {e}")
|
|
570
532
|
sys.exit(0)
|
|
@@ -629,7 +591,6 @@ class M3U8_Parser:
|
|
|
629
591
|
"""
|
|
630
592
|
Initialize variables for video, audio, and subtitle playlists.
|
|
631
593
|
"""
|
|
632
|
-
|
|
633
594
|
self._video = M3U8_Video(self.video_playlist)
|
|
634
595
|
self._audio = M3U8_Audio(self.audio_playlist)
|
|
635
596
|
self._subtitle = M3U8_Subtitle(self.subtitle_playlist)
|
|
@@ -663,4 +624,4 @@ class M3U8_Parser:
|
|
|
663
624
|
if return_string:
|
|
664
625
|
return f"[yellow]{int(hours)}[red]h [yellow]{int(minutes)}[red]m [yellow]{int(seconds)}[red]s"
|
|
665
626
|
else:
|
|
666
|
-
return {'h': int(hours), 'm': int(minutes), 's': int(seconds)}
|
|
627
|
+
return {'h': int(hours), 'm': int(minutes), 's': int(seconds)}
|