StreamingCommunity 2.0.0__py3-none-any.whl → 2.2.0__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/1337xx/__init__.py +1 -1
- StreamingCommunity/Api/Site/altadefinizione/__init__.py +1 -1
- StreamingCommunity/Api/Site/animeunity/__init__.py +1 -1
- StreamingCommunity/Api/Site/cb01new/__init__.py +1 -1
- StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +1 -3
- StreamingCommunity/Api/Site/ddlstreamitaly/series.py +1 -1
- StreamingCommunity/Api/Site/guardaserie/__init__.py +1 -3
- StreamingCommunity/Api/Site/guardaserie/series.py +2 -2
- StreamingCommunity/Api/Site/ilcorsaronero/__init__.py +1 -1
- StreamingCommunity/Api/Site/ilcorsaronero/util/ilCorsarScraper.py +14 -6
- StreamingCommunity/Api/Site/mostraguarda/__init__.py +1 -1
- StreamingCommunity/Api/Site/mostraguarda/film.py +1 -3
- StreamingCommunity/Api/Site/streamingcommunity/__init__.py +1 -1
- StreamingCommunity/Api/Site/streamingcommunity/film.py +1 -1
- StreamingCommunity/Api/Site/streamingcommunity/series.py +1 -1
- StreamingCommunity/Api/Template/Util/get_domain.py +12 -11
- StreamingCommunity/Lib/Downloader/HLS/downloader.py +42 -34
- StreamingCommunity/Lib/Downloader/HLS/segments.py +9 -11
- StreamingCommunity/Lib/FFmpeg/capture.py +1 -1
- StreamingCommunity/Lib/FFmpeg/util.py +1 -1
- StreamingCommunity/Lib/M3U8/decryptor.py +2 -2
- StreamingCommunity/Lib/M3U8/estimator.py +143 -90
- StreamingCommunity/Upload/update.py +2 -3
- StreamingCommunity/Upload/version.py +1 -1
- StreamingCommunity/Util/ffmpeg_installer.py +68 -28
- StreamingCommunity/Util/os.py +16 -25
- StreamingCommunity/run.py +0 -1
- {StreamingCommunity-2.0.0.dist-info → StreamingCommunity-2.2.0.dist-info}/METADATA +12 -9
- {StreamingCommunity-2.0.0.dist-info → StreamingCommunity-2.2.0.dist-info}/RECORD +33 -33
- {StreamingCommunity-2.0.0.dist-info → StreamingCommunity-2.2.0.dist-info}/WHEEL +1 -1
- {StreamingCommunity-2.0.0.dist-info → StreamingCommunity-2.2.0.dist-info}/entry_points.txt +1 -0
- {StreamingCommunity-2.0.0.dist-info → StreamingCommunity-2.2.0.dist-info}/LICENSE +0 -0
- {StreamingCommunity-2.0.0.dist-info → StreamingCommunity-2.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 21.04.25
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import time
|
|
@@ -26,7 +26,7 @@ class M3U8_Ts_Estimator:
|
|
|
26
26
|
def __init__(self, total_segments: int):
|
|
27
27
|
"""
|
|
28
28
|
Initialize the M3U8_Ts_Estimator object.
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
Parameters:
|
|
31
31
|
- total_segments (int): Length of total segments to download.
|
|
32
32
|
"""
|
|
@@ -34,73 +34,120 @@ class M3U8_Ts_Estimator:
|
|
|
34
34
|
self.now_downloaded_size = 0
|
|
35
35
|
self.total_segments = total_segments
|
|
36
36
|
self.lock = threading.Lock()
|
|
37
|
-
self.speed = {"upload": "N/A", "download": "N/A"}
|
|
38
|
-
self.
|
|
39
|
-
|
|
40
|
-
self.speed_thread.start()
|
|
37
|
+
self.speed = {"upload": "N/A", "download": "N/A"}
|
|
38
|
+
self.process_pid = os.getpid() # Get current process PID
|
|
39
|
+
logging.debug(f"Initializing M3U8_Ts_Estimator with PID: {self.process_pid}")
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
# Start the speed capture thread if TQDM_USE_LARGE_BAR is True
|
|
42
|
+
if TQDM_USE_LARGE_BAR:
|
|
43
|
+
logging.debug("TQDM_USE_LARGE_BAR is True, starting speed capture thread")
|
|
44
|
+
self.speed_thread = threading.Thread(target=self.capture_speed, args=(1, self.process_pid))
|
|
45
|
+
self.speed_thread.daemon = True
|
|
46
|
+
self.speed_thread.start()
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"""
|
|
48
|
+
else:
|
|
49
|
+
logging.debug("TQDM_USE_LARGE_BAR is False, speed capture thread not started")
|
|
50
|
+
|
|
51
|
+
def add_ts_file(self, size: int, size_download: int, duration: float):
|
|
52
|
+
"""Add a file size to the list of file sizes."""
|
|
53
|
+
logging.debug(f"Adding ts file - size: {size}, download size: {size_download}, duration: {duration}")
|
|
54
|
+
|
|
51
55
|
if size <= 0 or size_download <= 0 or duration <= 0:
|
|
52
|
-
logging.error("Invalid input values: size
|
|
56
|
+
logging.error(f"Invalid input values: size={size}, size_download={size_download}, duration={duration}")
|
|
53
57
|
return
|
|
54
58
|
|
|
55
|
-
# Add total size bytes
|
|
56
59
|
self.ts_file_sizes.append(size)
|
|
57
60
|
self.now_downloaded_size += size_download
|
|
61
|
+
logging.debug(f"Current total downloaded size: {self.now_downloaded_size}")
|
|
58
62
|
|
|
59
|
-
def capture_speed(self, interval: float = 1):
|
|
60
|
-
"""
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def get_network_io():
|
|
64
|
-
"""Get network I/O counters, handle missing psutil gracefully."""
|
|
63
|
+
def capture_speed(self, interval: float = 1, pid: int = None):
|
|
64
|
+
"""Capture the internet speed periodically."""
|
|
65
|
+
logging.debug(f"Starting speed capture with interval {interval}s for PID: {pid}")
|
|
66
|
+
|
|
67
|
+
def get_network_io(process=None):
|
|
65
68
|
try:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
if process:
|
|
70
|
+
|
|
71
|
+
# For process-specific monitoring
|
|
72
|
+
connections = process.connections(kind='inet')
|
|
73
|
+
if connections:
|
|
74
|
+
io_counters = process.io_counters()
|
|
75
|
+
logging.debug(f"Process IO counters: {io_counters}")
|
|
76
|
+
return io_counters
|
|
77
|
+
|
|
78
|
+
else:
|
|
79
|
+
logging.debug("No active internet connections found for process")
|
|
80
|
+
return None
|
|
81
|
+
else:
|
|
82
|
+
|
|
83
|
+
# For system-wide monitoring
|
|
84
|
+
io_counters = psutil.net_io_counters()
|
|
85
|
+
logging.debug(f"System IO counters: {io_counters}")
|
|
86
|
+
return io_counters
|
|
87
|
+
|
|
69
88
|
except Exception as e:
|
|
70
|
-
logging.
|
|
89
|
+
logging.error(f"Error getting network IO: {str(e)}")
|
|
71
90
|
return None
|
|
72
91
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if old_value is None: # If psutil is not available, continue with default values
|
|
77
|
-
time.sleep(interval)
|
|
78
|
-
continue
|
|
79
|
-
|
|
80
|
-
time.sleep(interval)
|
|
81
|
-
new_value = get_network_io()
|
|
82
|
-
|
|
83
|
-
if new_value is None: # Handle again if psutil fails in the next call
|
|
84
|
-
time.sleep(interval)
|
|
85
|
-
continue
|
|
92
|
+
try:
|
|
93
|
+
process = psutil.Process(pid) if pid else None
|
|
94
|
+
logging.debug(f"Monitoring process: {process}")
|
|
86
95
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
96
|
+
except Exception as e:
|
|
97
|
+
logging.error(f"Failed to get process with PID {pid}: {str(e)}")
|
|
98
|
+
process = None
|
|
90
99
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
100
|
+
last_upload = None
|
|
101
|
+
last_download = None
|
|
102
|
+
first_run = True
|
|
103
|
+
|
|
104
|
+
# Buffer circolare per le ultime N misurazioni
|
|
105
|
+
speed_buffer_size = 3
|
|
106
|
+
speed_buffer = deque(maxlen=speed_buffer_size)
|
|
95
107
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
108
|
+
while True:
|
|
109
|
+
try:
|
|
110
|
+
io_counters = get_network_io()
|
|
111
|
+
|
|
112
|
+
if io_counters:
|
|
113
|
+
current_upload = io_counters.bytes_sent
|
|
114
|
+
current_download = io_counters.bytes_recv
|
|
115
|
+
|
|
116
|
+
if not first_run and last_upload is not None and last_download is not None:
|
|
117
|
+
|
|
118
|
+
# Calcola la velocità istantanea
|
|
119
|
+
upload_speed = max(0, (current_upload - last_upload) / interval)
|
|
120
|
+
download_speed = max(0, (current_download - last_download) / interval)
|
|
121
|
+
|
|
122
|
+
# Aggiungi al buffer
|
|
123
|
+
speed_buffer.append(download_speed)
|
|
124
|
+
|
|
125
|
+
# Calcola la media mobile delle velocità
|
|
126
|
+
if len(speed_buffer) > 0:
|
|
127
|
+
avg_download_speed = sum(speed_buffer) / len(speed_buffer)
|
|
128
|
+
|
|
129
|
+
if avg_download_speed > 0:
|
|
130
|
+
with self.lock:
|
|
131
|
+
self.speed = {
|
|
132
|
+
"upload": internet_manager.format_transfer_speed(upload_speed),
|
|
133
|
+
"download": internet_manager.format_transfer_speed(avg_download_speed)
|
|
134
|
+
}
|
|
135
|
+
logging.debug(f"Updated speeds - Upload: {self.speed['upload']}, Download: {self.speed['download']}")
|
|
136
|
+
|
|
137
|
+
last_upload = current_upload
|
|
138
|
+
last_download = current_download
|
|
139
|
+
first_run = False
|
|
140
|
+
|
|
141
|
+
time.sleep(interval)
|
|
142
|
+
except Exception as e:
|
|
143
|
+
logging.error(f"Error in speed capture loop: {str(e)}")
|
|
144
|
+
logging.exception("Full traceback:")
|
|
145
|
+
logging.sleep(interval)
|
|
99
146
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
"""
|
|
147
|
+
def get_average_speed(self) -> list:
|
|
148
|
+
"""Calculate the average internet speed."""
|
|
103
149
|
with self.lock:
|
|
150
|
+
logging.debug(f"Current speed data: {self.speed}")
|
|
104
151
|
return self.speed['download'].split(" ")
|
|
105
152
|
|
|
106
153
|
def calculate_total_size(self) -> str:
|
|
@@ -127,7 +174,7 @@ class M3U8_Ts_Estimator:
|
|
|
127
174
|
except Exception as e:
|
|
128
175
|
logging.error("An unexpected error occurred: %s", e)
|
|
129
176
|
return "Error"
|
|
130
|
-
|
|
177
|
+
|
|
131
178
|
def get_downloaded_size(self) -> str:
|
|
132
179
|
"""
|
|
133
180
|
Get the total downloaded size formatted as a human-readable string.
|
|
@@ -136,41 +183,47 @@ class M3U8_Ts_Estimator:
|
|
|
136
183
|
str: The total downloaded size as a human-readable string.
|
|
137
184
|
"""
|
|
138
185
|
return internet_manager.format_file_size(self.now_downloaded_size)
|
|
139
|
-
|
|
186
|
+
|
|
140
187
|
def update_progress_bar(self, total_downloaded: int, duration: float, progress_counter: tqdm) -> None:
|
|
141
|
-
"""
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
188
|
+
"""Updates the progress bar with download information."""
|
|
189
|
+
try:
|
|
190
|
+
self.add_ts_file(total_downloaded * self.total_segments, total_downloaded, duration)
|
|
191
|
+
|
|
192
|
+
downloaded_file_size_str = self.get_downloaded_size()
|
|
193
|
+
file_total_size = self.calculate_total_size()
|
|
194
|
+
|
|
195
|
+
number_file_downloaded = downloaded_file_size_str.split(' ')[0]
|
|
196
|
+
number_file_total_size = file_total_size.split(' ')[0]
|
|
197
|
+
units_file_downloaded = downloaded_file_size_str.split(' ')[1]
|
|
198
|
+
units_file_total_size = file_total_size.split(' ')[1]
|
|
199
|
+
|
|
200
|
+
if TQDM_USE_LARGE_BAR:
|
|
201
|
+
speed_data = self.get_average_speed()
|
|
202
|
+
logging.debug(f"Speed data for progress bar: {speed_data}")
|
|
203
|
+
|
|
204
|
+
if len(speed_data) >= 2:
|
|
205
|
+
average_internet_speed = speed_data[0]
|
|
206
|
+
average_internet_unit = speed_data[1]
|
|
207
|
+
|
|
208
|
+
else:
|
|
209
|
+
logging.warning(f"Invalid speed data format: {speed_data}")
|
|
210
|
+
average_internet_speed = "N/A"
|
|
211
|
+
average_internet_unit = ""
|
|
212
|
+
|
|
213
|
+
progress_str = (
|
|
214
|
+
f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< "
|
|
215
|
+
f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size} "
|
|
216
|
+
f"{Colors.WHITE}| {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit}"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
else:
|
|
220
|
+
progress_str = (
|
|
221
|
+
f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< "
|
|
222
|
+
f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size}"
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
progress_counter.set_postfix_str(progress_str)
|
|
226
|
+
logging.debug(f"Updated progress bar: {progress_str}")
|
|
227
|
+
|
|
228
|
+
except Exception as e:
|
|
229
|
+
logging.error(f"Error updating progress bar: {str(e)}")
|
|
@@ -57,11 +57,10 @@ def update():
|
|
|
57
57
|
|
|
58
58
|
# Check installed version
|
|
59
59
|
if str(__version__).replace('v', '') != str(last_version).replace('v', '') :
|
|
60
|
-
console.print(f"[red]New version available: [yellow]{last_version}")
|
|
60
|
+
console.print(f"[red]New version available: [yellow]{last_version} \n")
|
|
61
61
|
else:
|
|
62
|
-
console.print(f"[
|
|
62
|
+
console.print(f" [yellow]Everything is up to date \n")
|
|
63
63
|
|
|
64
|
-
console.print("\n")
|
|
65
64
|
console.print(f"[red]{__title__} has been downloaded [yellow]{total_download_count} [red]times, but only [yellow]{percentual_stars}% [red]of users have starred it.\n\
|
|
66
65
|
[cyan]Help the repository grow today by leaving a [yellow]star [cyan]and [yellow]sharing [cyan]it with others online!")
|
|
67
66
|
|
|
@@ -116,21 +116,35 @@ class FFMPEGDownloader:
|
|
|
116
116
|
def _check_existing_binaries(self) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
117
117
|
"""
|
|
118
118
|
Check if FFmpeg binaries already exist in the base directory.
|
|
119
|
-
|
|
120
|
-
Returns:
|
|
121
|
-
Tuple[Optional[str], Optional[str], Optional[str]]: Paths to ffmpeg, ffprobe, and ffplay executables.
|
|
122
|
-
Returns None for each executable that is not found.
|
|
119
|
+
Enhanced to check both the binary directory and system paths on macOS.
|
|
123
120
|
"""
|
|
124
121
|
config = FFMPEG_CONFIGURATION[self.os_name]
|
|
125
122
|
executables = config['executables']
|
|
126
123
|
found_executables = []
|
|
127
124
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
125
|
+
# For macOS, check both binary directory and system paths
|
|
126
|
+
if self.os_name == 'darwin':
|
|
127
|
+
potential_paths = [
|
|
128
|
+
'/usr/local/bin',
|
|
129
|
+
'/opt/homebrew/bin',
|
|
130
|
+
'/usr/bin',
|
|
131
|
+
self.base_dir
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
for executable in executables:
|
|
135
|
+
found = None
|
|
136
|
+
for path in potential_paths:
|
|
137
|
+
full_path = os.path.join(path, executable)
|
|
138
|
+
if os.path.exists(full_path) and os.access(full_path, os.X_OK):
|
|
139
|
+
found = full_path
|
|
140
|
+
break
|
|
141
|
+
found_executables.append(found)
|
|
142
|
+
else:
|
|
143
|
+
|
|
144
|
+
# Original behavior for other operating systems
|
|
145
|
+
for executable in executables:
|
|
146
|
+
exe_paths = glob.glob(os.path.join(self.base_dir, executable))
|
|
147
|
+
found_executables.append(exe_paths[0] if exe_paths else None)
|
|
134
148
|
|
|
135
149
|
return tuple(found_executables) if len(found_executables) == 3 else (None, None, None)
|
|
136
150
|
|
|
@@ -275,37 +289,63 @@ class FFMPEGDownloader:
|
|
|
275
289
|
def check_ffmpeg() -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
276
290
|
"""
|
|
277
291
|
Check for FFmpeg executables in the system and download them if not found.
|
|
292
|
+
Enhanced detection for macOS systems.
|
|
278
293
|
|
|
279
294
|
Returns:
|
|
280
295
|
Tuple[Optional[str], Optional[str], Optional[str]]: Paths to ffmpeg, ffprobe, and ffplay executables.
|
|
281
|
-
Returns None for each executable that couldn't be found or downloaded.
|
|
282
|
-
|
|
283
|
-
The function first checks if FFmpeg executables are available in the system PATH:
|
|
284
|
-
- On Windows, uses the 'where' command
|
|
285
|
-
- On Unix-like systems, uses 'which'
|
|
286
|
-
|
|
287
|
-
If the executables are not found in PATH, it attempts to download and install them
|
|
288
|
-
using the FFMPEGDownloader class.
|
|
289
296
|
"""
|
|
290
297
|
try:
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
298
|
+
system_platform = platform.system().lower()
|
|
299
|
+
|
|
300
|
+
# Special handling for macOS
|
|
301
|
+
if system_platform == 'darwin':
|
|
302
|
+
|
|
303
|
+
# Common installation paths on macOS
|
|
304
|
+
potential_paths = [
|
|
305
|
+
'/usr/local/bin', # Homebrew default
|
|
306
|
+
'/opt/homebrew/bin', # Apple Silicon Homebrew
|
|
307
|
+
'/usr/bin', # System default
|
|
308
|
+
os.path.expanduser('~/Applications/binary'), # Custom installation
|
|
309
|
+
'/Applications/binary' # Custom installation
|
|
310
|
+
]
|
|
295
311
|
|
|
296
|
-
|
|
297
|
-
|
|
312
|
+
for path in potential_paths:
|
|
313
|
+
ffmpeg_path = os.path.join(path, 'ffmpeg')
|
|
314
|
+
ffprobe_path = os.path.join(path, 'ffprobe')
|
|
315
|
+
ffplay_path = os.path.join(path, 'ffplay')
|
|
316
|
+
|
|
317
|
+
if (os.path.exists(ffmpeg_path) and os.path.exists(ffprobe_path) and
|
|
318
|
+
os.access(ffmpeg_path, os.X_OK) and os.access(ffprobe_path, os.X_OK)):
|
|
319
|
+
|
|
320
|
+
# Return found executables, with ffplay being optional
|
|
321
|
+
ffplay_path = ffplay_path if os.path.exists(ffplay_path) else None
|
|
322
|
+
return ffmpeg_path, ffprobe_path, ffplay_path
|
|
323
|
+
|
|
324
|
+
# Windows detection
|
|
325
|
+
elif system_platform == 'windows':
|
|
326
|
+
try:
|
|
327
|
+
ffmpeg_path = subprocess.check_output(['where', 'ffmpeg'], text=True).strip().split('\n')[0]
|
|
328
|
+
ffprobe_path = subprocess.check_output(['where', 'ffprobe'], text=True).strip().split('\n')[0]
|
|
329
|
+
ffplay_path = subprocess.check_output(['where', 'ffplay'], text=True).strip().split('\n')[0]
|
|
330
|
+
|
|
331
|
+
if ffmpeg_path and ffprobe_path:
|
|
332
|
+
return ffmpeg_path, ffprobe_path, ffplay_path
|
|
333
|
+
except subprocess.CalledProcessError:
|
|
334
|
+
pass
|
|
335
|
+
|
|
336
|
+
# Linux detection
|
|
298
337
|
else:
|
|
299
338
|
ffmpeg_path = shutil.which('ffmpeg')
|
|
300
339
|
ffprobe_path = shutil.which('ffprobe')
|
|
301
340
|
ffplay_path = shutil.which('ffplay')
|
|
302
341
|
|
|
303
|
-
if
|
|
342
|
+
if ffmpeg_path and ffprobe_path:
|
|
304
343
|
return ffmpeg_path, ffprobe_path, ffplay_path
|
|
305
|
-
|
|
344
|
+
|
|
345
|
+
# If executables were not found, attempt to download FFmpeg
|
|
306
346
|
downloader = FFMPEGDownloader()
|
|
307
347
|
return downloader.download()
|
|
308
|
-
|
|
348
|
+
|
|
309
349
|
except Exception as e:
|
|
310
|
-
logging.error(f"Error checking FFmpeg: {e}")
|
|
350
|
+
logging.error(f"Error checking or downloading FFmpeg executables: {e}")
|
|
311
351
|
return None, None, None
|
StreamingCommunity/Util/os.py
CHANGED
|
@@ -317,16 +317,13 @@ class InternManager():
|
|
|
317
317
|
def check_internet():
|
|
318
318
|
while True:
|
|
319
319
|
try:
|
|
320
|
-
httpx.get("https://www.google.com")
|
|
321
|
-
#console.log("[bold green]Internet is available![/bold green]")
|
|
320
|
+
httpx.get("https://www.google.com", timeout=15)
|
|
322
321
|
break
|
|
323
322
|
|
|
324
323
|
except urllib.error.URLError:
|
|
325
324
|
console.log("[bold red]Internet is not available. Waiting...[/bold red]")
|
|
326
325
|
time.sleep(5)
|
|
327
326
|
|
|
328
|
-
print()
|
|
329
|
-
|
|
330
327
|
|
|
331
328
|
class OsSummary:
|
|
332
329
|
|
|
@@ -353,20 +350,17 @@ class OsSummary:
|
|
|
353
350
|
console.print(f"{command[0]} not found", style="bold red")
|
|
354
351
|
sys.exit(0)
|
|
355
352
|
|
|
356
|
-
def check_ffmpeg_location(self, command: list):
|
|
353
|
+
def check_ffmpeg_location(self, command: list) -> str:
|
|
357
354
|
"""
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
Returns:
|
|
361
|
-
str: Location of FFmpeg executable or None if not found
|
|
355
|
+
Check if a specific executable (ffmpeg or ffprobe) is located using the given command.
|
|
356
|
+
Returns the path of the executable or None if not found.
|
|
362
357
|
"""
|
|
363
358
|
try:
|
|
364
|
-
result = subprocess.check_output(command,
|
|
365
|
-
return result
|
|
359
|
+
result = subprocess.check_output(command, text=True).strip()
|
|
360
|
+
return result.split('\n')[0] if result else None
|
|
366
361
|
|
|
367
362
|
except subprocess.CalledProcessError:
|
|
368
|
-
|
|
369
|
-
sys.exit(0)
|
|
363
|
+
return None
|
|
370
364
|
|
|
371
365
|
def get_library_version(self, lib_name: str):
|
|
372
366
|
"""
|
|
@@ -467,27 +461,24 @@ class OsSummary:
|
|
|
467
461
|
console.print(f"[cyan]Python[white]: [bold red]{python_version} ({python_implementation} {arch}) - {os_info} ({glibc_version})[/bold red]")
|
|
468
462
|
logging.info(f"Python: {python_version} ({python_implementation} {arch}) - {os_info} ({glibc_version})")
|
|
469
463
|
|
|
470
|
-
#
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
# Usa il comando 'which' su Unix/Linux
|
|
475
|
-
else:
|
|
476
|
-
command = 'which'
|
|
464
|
+
# Use the 'where' command on Windows and 'which' command on Unix-like systems
|
|
465
|
+
system_platform = platform.system().lower()
|
|
466
|
+
command = 'where' if system_platform == 'windows' else 'which'
|
|
477
467
|
|
|
478
|
-
# Locate ffmpeg and ffprobe from
|
|
479
|
-
if self.ffmpeg_path
|
|
468
|
+
# Locate ffmpeg and ffprobe from the PATH environment
|
|
469
|
+
if self.ffmpeg_path is not None and "binary" not in self.ffmpeg_path:
|
|
480
470
|
self.ffmpeg_path = self.check_ffmpeg_location([command, 'ffmpeg'])
|
|
481
471
|
|
|
482
|
-
if self.ffprobe_path
|
|
472
|
+
if self.ffprobe_path is not None and "binary" not in self.ffprobe_path:
|
|
483
473
|
self.ffprobe_path = self.check_ffmpeg_location([command, 'ffprobe'])
|
|
484
474
|
|
|
485
|
-
#
|
|
475
|
+
# If ffmpeg or ffprobe is not located, fall back to using the check_ffmpeg function
|
|
486
476
|
if self.ffmpeg_path is None or self.ffprobe_path is None:
|
|
487
477
|
self.ffmpeg_path, self.ffprobe_path, self.ffplay_path = check_ffmpeg()
|
|
488
478
|
|
|
479
|
+
# If still not found, print error and exit
|
|
489
480
|
if self.ffmpeg_path is None or self.ffprobe_path is None:
|
|
490
|
-
console.log("[red]
|
|
481
|
+
console.log("[red]Can't locate ffmpeg or ffprobe")
|
|
491
482
|
sys.exit(0)
|
|
492
483
|
|
|
493
484
|
ffmpeg_version = self.get_executable_version([self.ffprobe_path, '-version'])
|
StreamingCommunity/run.py
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: StreamingCommunity
|
|
3
|
-
Version: 2.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 2.2.0
|
|
4
|
+
Summary: UNKNOWN
|
|
5
5
|
Home-page: https://github.com/Lovi-0/StreamingCommunity
|
|
6
6
|
Author: Lovi-0
|
|
7
|
+
License: UNKNOWN
|
|
7
8
|
Project-URL: Bug Reports, https://github.com/Lovi-0/StreamingCommunity/issues
|
|
8
9
|
Project-URL: Source, https://github.com/Lovi-0/StreamingCommunity
|
|
9
|
-
Keywords:
|
|
10
|
+
Keywords: streaming community
|
|
11
|
+
Platform: UNKNOWN
|
|
10
12
|
Requires-Python: >=3.8
|
|
11
13
|
Description-Content-Type: text/markdown
|
|
12
|
-
License-File: LICENSE
|
|
13
14
|
Requires-Dist: httpx
|
|
14
|
-
Requires-Dist: cffi
|
|
15
15
|
Requires-Dist: bs4
|
|
16
16
|
Requires-Dist: rich
|
|
17
17
|
Requires-Dist: tqdm
|
|
@@ -255,7 +255,7 @@ The configuration file is divided into several main sections:
|
|
|
255
255
|
* `%(episode)` : Is the number of the episode
|
|
256
256
|
* `%(episode_name)` : Is the name of the episode
|
|
257
257
|
`<br/><br/>`
|
|
258
|
-
|
|
258
|
+
|
|
259
259
|
- `not_close`: If true, continues running after downloading
|
|
260
260
|
|
|
261
261
|
### qBittorrent Configuration
|
|
@@ -307,6 +307,9 @@ The configuration file is divided into several main sections:
|
|
|
307
307
|
- `default_audio_workser`: Number of threads for audio download
|
|
308
308
|
- `cleanup_tmp_folder`: Remove temporary .ts files after download
|
|
309
309
|
|
|
310
|
+
> [!IMPORTANT]
|
|
311
|
+
> Set `tqdm_use_large_bar` to `false` when using Termux or terminals with limited width to prevent display issues
|
|
312
|
+
|
|
310
313
|
|
|
311
314
|
<br>
|
|
312
315
|
|
|
@@ -359,10 +362,8 @@ forced-ita hin - Hindi pol - Polish tur - Turkish
|
|
|
359
362
|
|
|
360
363
|
<br>
|
|
361
364
|
|
|
362
|
-
|
|
363
365
|
# COMMAND
|
|
364
366
|
|
|
365
|
-
|
|
366
367
|
- Download a specific season by entering its number.
|
|
367
368
|
* **Example:** `1` will download *Season 1* only.
|
|
368
369
|
|
|
@@ -433,7 +434,7 @@ The `run-container` command mounts also the `config.json` file, so any change to
|
|
|
433
434
|
|
|
434
435
|
# To Do
|
|
435
436
|
|
|
436
|
-
-
|
|
437
|
+
- Finish [website API](https://github.com/Lovi-0/StreamingCommunity/tree/test_gui_1)
|
|
437
438
|
|
|
438
439
|
# Contributing
|
|
439
440
|
|
|
@@ -454,3 +455,5 @@ This software is provided "as is", without warranty of any kind, express or impl
|
|
|
454
455
|
<a href="https://github.com/Lovi-0/StreamingCommunity/graphs/contributors" alt="View Contributors">
|
|
455
456
|
<img src="https://contrib.rocks/image?repo=Lovi-0/StreamingCommunity&max=1000&columns=10" alt="Contributors" />
|
|
456
457
|
</a>
|
|
458
|
+
|
|
459
|
+
|