StreamingCommunity 3.3.0__py3-none-any.whl → 3.3.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/Api/Site/altadefinizione/__init__.py +37 -17
- StreamingCommunity/Api/Site/animeunity/__init__.py +36 -16
- StreamingCommunity/Api/Site/animeworld/__init__.py +50 -6
- StreamingCommunity/Api/Site/crunchyroll/__init__.py +42 -16
- StreamingCommunity/Api/Site/crunchyroll/site.py +1 -1
- StreamingCommunity/Api/Site/guardaserie/__init__.py +50 -6
- StreamingCommunity/Api/Site/mediasetinfinity/__init__.py +43 -5
- StreamingCommunity/Api/Site/mediasetinfinity/film.py +1 -1
- StreamingCommunity/Api/Site/mediasetinfinity/site.py +6 -3
- StreamingCommunity/Api/Site/mediasetinfinity/util/ScrapeSerie.py +6 -7
- StreamingCommunity/Api/Site/mediasetinfinity/util/get_license.py +162 -0
- StreamingCommunity/Api/Site/raiplay/__init__.py +45 -14
- StreamingCommunity/Api/Site/raiplay/series.py +9 -5
- StreamingCommunity/Api/Site/streamingcommunity/__init__.py +7 -2
- StreamingCommunity/Api/Site/streamingwatch/__init__.py +44 -14
- StreamingCommunity/Lib/Downloader/DASH/downloader.py +87 -55
- StreamingCommunity/Lib/Downloader/HLS/downloader.py +18 -6
- StreamingCommunity/Lib/Downloader/HLS/segments.py +1 -1
- StreamingCommunity/Lib/FFmpeg/command.py +66 -7
- StreamingCommunity/Lib/FFmpeg/util.py +16 -13
- StreamingCommunity/Upload/version.py +1 -1
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/METADATA +2 -11
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/RECORD +27 -27
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/WHEEL +0 -0
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/entry_points.txt +0 -0
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/licenses/LICENSE +0 -0
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/top_level.txt +0 -0
|
@@ -6,11 +6,13 @@ import shutil
|
|
|
6
6
|
|
|
7
7
|
# External libraries
|
|
8
8
|
from rich.console import Console
|
|
9
|
+
from rich.panel import Panel
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
# Internal utilities
|
|
12
13
|
from StreamingCommunity.Util.config_json import config_manager
|
|
13
|
-
from StreamingCommunity.
|
|
14
|
+
from StreamingCommunity.Util.os import internet_manager
|
|
15
|
+
from ...FFmpeg import print_duration_table
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
# Logic class
|
|
@@ -20,6 +22,7 @@ from .decrypt import decrypt_with_mp4decrypt
|
|
|
20
22
|
from .cdm_helpher import get_widevine_keys
|
|
21
23
|
|
|
22
24
|
|
|
25
|
+
|
|
23
26
|
# Config
|
|
24
27
|
DOWNLOAD_SPECIFIC_AUDIO = config_manager.get_list('M3U8_DOWNLOAD', 'specific_list_audio')
|
|
25
28
|
FILTER_CUSTOM_REOLUTION = str(config_manager.get('M3U8_CONVERSION', 'force_resolution')).strip().lower()
|
|
@@ -35,8 +38,8 @@ class DASH_Downloader:
|
|
|
35
38
|
self.cdm_device = cdm_device
|
|
36
39
|
self.license_url = license_url
|
|
37
40
|
self.mpd_url = mpd_url
|
|
38
|
-
self.
|
|
39
|
-
self.
|
|
41
|
+
self.out_path = os.path.splitext(os.path.abspath(str(output_path)))[0]
|
|
42
|
+
self.original_output_path = output_path
|
|
40
43
|
self.parser = None
|
|
41
44
|
self._setup_temp_dirs()
|
|
42
45
|
|
|
@@ -94,12 +97,35 @@ class DASH_Downloader:
|
|
|
94
97
|
self.error = None
|
|
95
98
|
self.stopped = False
|
|
96
99
|
|
|
100
|
+
# Fetch keys immediately after obtaining PSSH
|
|
101
|
+
if not self.parser.pssh:
|
|
102
|
+
console.print("[red]No PSSH found: segments are not encrypted, skipping decryption.")
|
|
103
|
+
self.download_segments(clear=True)
|
|
104
|
+
return True
|
|
105
|
+
|
|
106
|
+
keys = get_widevine_keys(
|
|
107
|
+
pssh=self.parser.pssh,
|
|
108
|
+
license_url=self.license_url,
|
|
109
|
+
cdm_device_path=self.cdm_device,
|
|
110
|
+
headers=custom_headers,
|
|
111
|
+
payload=custom_payload
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
if not keys:
|
|
115
|
+
console.print("[red]No keys found, cannot proceed with download.[/red]")
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
# Extract the first key for decryption
|
|
119
|
+
key = keys[0]
|
|
120
|
+
KID = key['kid']
|
|
121
|
+
KEY = key['key']
|
|
122
|
+
|
|
97
123
|
for typ in ["video", "audio"]:
|
|
98
124
|
rep = self.get_representation_by_type(typ)
|
|
99
125
|
if rep:
|
|
100
126
|
encrypted_path = os.path.join(self.encrypted_dir, f"{rep['id']}_encrypted.m4s")
|
|
101
127
|
|
|
102
|
-
# If m4s file doesn't exist start downloading
|
|
128
|
+
# If m4s file doesn't exist, start downloading
|
|
103
129
|
if not os.path.exists(encrypted_path):
|
|
104
130
|
downloader = MPD_Segments(
|
|
105
131
|
tmp_folder=self.encrypted_dir,
|
|
@@ -124,28 +150,6 @@ class DASH_Downloader:
|
|
|
124
150
|
self.error = str(ex)
|
|
125
151
|
return False
|
|
126
152
|
|
|
127
|
-
if not self.parser.pssh:
|
|
128
|
-
print("No PSSH found: segments are not encrypted, skipping decryption.")
|
|
129
|
-
self.download_segments(clear=True)
|
|
130
|
-
return True
|
|
131
|
-
|
|
132
|
-
keys = get_widevine_keys(
|
|
133
|
-
pssh=self.parser.pssh,
|
|
134
|
-
license_url=self.license_url,
|
|
135
|
-
cdm_device_path=self.cdm_device,
|
|
136
|
-
headers=custom_headers,
|
|
137
|
-
payload=custom_payload
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
if not keys:
|
|
141
|
-
self.error = f"No key found, cannot decrypt {typ}"
|
|
142
|
-
print(self.error)
|
|
143
|
-
return False
|
|
144
|
-
|
|
145
|
-
key = keys[0]
|
|
146
|
-
KID = key['kid']
|
|
147
|
-
KEY = key['key']
|
|
148
|
-
|
|
149
153
|
decrypted_path = os.path.join(self.decrypted_dir, f"{typ}.mp4")
|
|
150
154
|
result_path = decrypt_with_mp4decrypt(
|
|
151
155
|
encrypted_path, KID, KEY, output_path=decrypted_path
|
|
@@ -169,46 +173,74 @@ class DASH_Downloader:
|
|
|
169
173
|
pass
|
|
170
174
|
|
|
171
175
|
def finalize_output(self):
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
# fallback: if one of the two is missing, look in encrypted
|
|
176
|
-
if not os.path.exists(video_file):
|
|
177
|
-
for f in os.listdir(self.encrypted_dir):
|
|
178
|
-
if f.endswith("_encrypted.m4s") and ("video" in f or f.startswith("1_")):
|
|
179
|
-
video_file = os.path.join(self.encrypted_dir, f)
|
|
180
|
-
break
|
|
181
|
-
if not os.path.exists(audio_file):
|
|
182
|
-
for f in os.listdir(self.encrypted_dir):
|
|
183
|
-
if f.endswith("_encrypted.m4s") and ("audio" in f or f.startswith("0_")):
|
|
184
|
-
audio_file = os.path.join(self.encrypted_dir, f)
|
|
185
|
-
break
|
|
186
|
-
|
|
187
|
-
# Usa il nome file originale per il file finale
|
|
176
|
+
|
|
177
|
+
# Use the original output path for the final file
|
|
188
178
|
output_file = self.original_output_path
|
|
179
|
+
|
|
180
|
+
# Set the output file path for status tracking
|
|
181
|
+
self.output_file = output_file
|
|
182
|
+
use_shortest = False
|
|
189
183
|
|
|
190
|
-
if os.path.exists(video_file) and os.path.exists(audio_file):
|
|
184
|
+
"""if os.path.exists(video_file) and os.path.exists(audio_file):
|
|
191
185
|
audio_tracks = [{"path": audio_file}]
|
|
192
|
-
join_audios(video_file, audio_tracks, output_file)
|
|
186
|
+
out_audio_path, use_shortest = join_audios(video_file, audio_tracks, output_file)
|
|
187
|
+
|
|
193
188
|
elif os.path.exists(video_file):
|
|
194
|
-
join_video(video_file, output_file, codec=None)
|
|
189
|
+
out_video_path = join_video(video_file, output_file, codec=None)
|
|
190
|
+
|
|
195
191
|
else:
|
|
196
192
|
print("Video file missing, cannot export")
|
|
193
|
+
return None
|
|
194
|
+
"""
|
|
197
195
|
|
|
198
|
-
#
|
|
196
|
+
# Handle failed sync case
|
|
197
|
+
if use_shortest:
|
|
198
|
+
new_filename = output_file.replace(".mp4", "_failed_sync.mp4")
|
|
199
|
+
os.rename(output_file, new_filename)
|
|
200
|
+
output_file = new_filename
|
|
201
|
+
self.output_file = new_filename
|
|
202
|
+
|
|
203
|
+
# Display file information
|
|
204
|
+
if os.path.exists(output_file):
|
|
205
|
+
file_size = internet_manager.format_file_size(os.path.getsize(output_file))
|
|
206
|
+
duration = print_duration_table(output_file, description=False, return_string=True)
|
|
207
|
+
panel_content = (
|
|
208
|
+
f"[cyan]File size: [bold red]{file_size}[/bold red]\n"
|
|
209
|
+
f"[cyan]Duration: [bold]{duration}[/bold]\n"
|
|
210
|
+
f"[cyan]Output: [bold]{os.path.abspath(output_file)}[/bold]"
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
console.print(Panel(
|
|
214
|
+
panel_content,
|
|
215
|
+
title=f"{os.path.basename(output_file.replace('.mp4', ''))}",
|
|
216
|
+
border_style="green"
|
|
217
|
+
))
|
|
218
|
+
|
|
219
|
+
# Clean up: delete only the tmp directory, not the main directory
|
|
199
220
|
if os.path.exists(self.tmp_dir):
|
|
200
221
|
shutil.rmtree(self.tmp_dir, ignore_errors=True)
|
|
201
222
|
|
|
202
|
-
#
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
os.rmdir(self.out_path)
|
|
206
|
-
except Exception as e:
|
|
207
|
-
print(f"[WARN] Impossibile eliminare la cartella {self.out_path}: {e}")
|
|
223
|
+
# Only remove the temp base directory if it was created specifically for this download
|
|
224
|
+
# and if the final output is NOT inside this directory
|
|
225
|
+
output_dir = os.path.dirname(self.original_output_path)
|
|
208
226
|
|
|
227
|
+
# Check if out_path is different from the actual output directory
|
|
228
|
+
# and if it's empty, then it's safe to remove
|
|
229
|
+
if (self.out_path != output_dir and
|
|
230
|
+
os.path.exists(self.out_path) and
|
|
231
|
+
not os.listdir(self.out_path)):
|
|
232
|
+
try:
|
|
233
|
+
os.rmdir(self.out_path)
|
|
234
|
+
except Exception as e:
|
|
235
|
+
print(f"[WARN] Cannot remove directory {self.out_path}: {e}")
|
|
209
236
|
|
|
210
|
-
|
|
211
|
-
|
|
237
|
+
# Verify the final file exists before returning
|
|
238
|
+
if os.path.exists(output_file):
|
|
239
|
+
return output_file
|
|
240
|
+
else:
|
|
241
|
+
self.error = "Final output file was not created successfully"
|
|
242
|
+
return None
|
|
243
|
+
|
|
212
244
|
def get_status(self):
|
|
213
245
|
"""
|
|
214
246
|
Returns a dict with 'path', 'error', and 'stopped' for external use.
|
|
@@ -217,4 +249,4 @@ class DASH_Downloader:
|
|
|
217
249
|
"path": self.output_file,
|
|
218
250
|
"error": self.error,
|
|
219
251
|
"stopped": self.stopped
|
|
220
|
-
}
|
|
252
|
+
}
|
|
@@ -360,6 +360,7 @@ class MergeManager:
|
|
|
360
360
|
"""
|
|
361
361
|
video_file = os.path.join(self.temp_dir, 'video', '0.ts')
|
|
362
362
|
merged_file = video_file
|
|
363
|
+
use_shortest = False
|
|
363
364
|
|
|
364
365
|
if not self.audio_streams and not self.sub_streams:
|
|
365
366
|
merged_file = join_video(
|
|
@@ -376,7 +377,7 @@ class MergeManager:
|
|
|
376
377
|
} for a in self.audio_streams]
|
|
377
378
|
|
|
378
379
|
merged_audio_path = os.path.join(self.temp_dir, 'merged_audio.mp4')
|
|
379
|
-
merged_file = join_audios(
|
|
380
|
+
merged_file, use_shortest = join_audios(
|
|
380
381
|
video_path=video_file,
|
|
381
382
|
audio_tracks=audio_tracks,
|
|
382
383
|
out_path=merged_audio_path,
|
|
@@ -396,7 +397,7 @@ class MergeManager:
|
|
|
396
397
|
out_path=merged_subs_path
|
|
397
398
|
)
|
|
398
399
|
|
|
399
|
-
return merged_file
|
|
400
|
+
return merged_file, use_shortest
|
|
400
401
|
|
|
401
402
|
|
|
402
403
|
class HLS_Downloader:
|
|
@@ -467,9 +468,9 @@ class HLS_Downloader:
|
|
|
467
468
|
sub_streams=self.m3u8_manager.sub_streams
|
|
468
469
|
)
|
|
469
470
|
|
|
470
|
-
final_file = self.merge_manager.merge()
|
|
471
|
+
final_file, use_shortest = self.merge_manager.merge()
|
|
471
472
|
self.path_manager.move_final_file(final_file)
|
|
472
|
-
self._print_summary()
|
|
473
|
+
self._print_summary(use_shortest)
|
|
473
474
|
self.path_manager.cleanup()
|
|
474
475
|
|
|
475
476
|
return {
|
|
@@ -495,7 +496,7 @@ class HLS_Downloader:
|
|
|
495
496
|
'stopped': False
|
|
496
497
|
}
|
|
497
498
|
|
|
498
|
-
def _print_summary(self):
|
|
499
|
+
def _print_summary(self, use_shortest):
|
|
499
500
|
"""Prints download summary including file size, duration, and any missing segments."""
|
|
500
501
|
if TELEGRAM_BOT:
|
|
501
502
|
bot = get_bot_instance()
|
|
@@ -523,7 +524,18 @@ class HLS_Downloader:
|
|
|
523
524
|
|
|
524
525
|
if missing_ts:
|
|
525
526
|
panel_content += f"\n{missing_info}"
|
|
526
|
-
|
|
527
|
+
|
|
528
|
+
new_filename = self.path_manager.output_path
|
|
529
|
+
if missing_ts and use_shortest:
|
|
530
|
+
new_filename = new_filename.replace(".mp4", "_failed_sync_ts.mp4")
|
|
531
|
+
elif missing_ts:
|
|
532
|
+
new_filename = new_filename.replace(".mp4", "_failed_ts.mp4")
|
|
533
|
+
elif use_shortest:
|
|
534
|
+
new_filename = new_filename.replace(".mp4", "_failed_sync.mp4")
|
|
535
|
+
|
|
536
|
+
if missing_ts or use_shortest:
|
|
537
|
+
os.rename(self.path_manager.output_path, new_filename)
|
|
538
|
+
self.path_manager.output_path = new_filename
|
|
527
539
|
|
|
528
540
|
console.print(Panel(
|
|
529
541
|
panel_content,
|
|
@@ -249,7 +249,7 @@ class M3U8_Segments:
|
|
|
249
249
|
self.info_nRetry += 1
|
|
250
250
|
|
|
251
251
|
if attempt + 1 == REQUEST_MAX_RETRY:
|
|
252
|
-
console.
|
|
252
|
+
console.print(f"[red]Final retry failed for segment: {index}")
|
|
253
253
|
self.queue.put((index, None)) # Marker for failed segment
|
|
254
254
|
progress_bar.update(1)
|
|
255
255
|
self.info_nFailed += 1
|
|
@@ -15,7 +15,7 @@ from StreamingCommunity.Util.os import os_manager, suppress_output, get_ffmpeg_p
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
# Logic class
|
|
18
|
-
from .util import need_to_force_to_ts, check_duration_v_a
|
|
18
|
+
from .util import need_to_force_to_ts, check_duration_v_a, get_video_duration
|
|
19
19
|
from .capture import capture_ffmpeg_real_time
|
|
20
20
|
from ..M3U8 import M3U8_Codec
|
|
21
21
|
|
|
@@ -99,6 +99,14 @@ def join_video(video_path: str, out_path: str, codec: M3U8_Codec = None):
|
|
|
99
99
|
- out_path (str): The path to save the output file.
|
|
100
100
|
- codec (M3U8_Codec): The video codec to use. Defaults to 'copy'.
|
|
101
101
|
"""
|
|
102
|
+
if video_path is None:
|
|
103
|
+
console.log("[red]No video path provided for joining.")
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
if out_path is None:
|
|
107
|
+
console.log("[red]No output path provided for joining.")
|
|
108
|
+
return None
|
|
109
|
+
|
|
102
110
|
ffmpeg_cmd = [get_ffmpeg_path()]
|
|
103
111
|
|
|
104
112
|
# Enabled the use of gpu
|
|
@@ -175,10 +183,50 @@ def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: s
|
|
|
175
183
|
Parameters:
|
|
176
184
|
- video_path (str): The path to the video file.
|
|
177
185
|
- audio_tracks (list[dict[str, str]]): A list of dictionaries containing information about audio tracks.
|
|
178
|
-
Each dictionary should contain the 'path'
|
|
186
|
+
Each dictionary should contain the 'path' and 'name' keys.
|
|
179
187
|
- out_path (str): The path to save the output file.
|
|
180
188
|
"""
|
|
181
|
-
|
|
189
|
+
if video_path is None:
|
|
190
|
+
console.log("[red]No video path provided for joining audios.")
|
|
191
|
+
return None, False
|
|
192
|
+
|
|
193
|
+
if audio_tracks is None or len(audio_tracks) == 0:
|
|
194
|
+
console.log("[red]No audio tracks provided for joining.")
|
|
195
|
+
return None, False
|
|
196
|
+
|
|
197
|
+
if out_path is None:
|
|
198
|
+
console.log("[red]No output path provided for joining audios.")
|
|
199
|
+
return None, False
|
|
200
|
+
|
|
201
|
+
use_shortest = False
|
|
202
|
+
duration_diffs = []
|
|
203
|
+
|
|
204
|
+
# Get video duration first
|
|
205
|
+
video_duration = get_video_duration(video_path, None)
|
|
206
|
+
|
|
207
|
+
for audio_track in audio_tracks:
|
|
208
|
+
audio_path = audio_track.get('path')
|
|
209
|
+
audio_lang = audio_track.get('name', 'unknown')
|
|
210
|
+
audio_duration, diff = check_duration_v_a(video_path, audio_path)
|
|
211
|
+
|
|
212
|
+
duration_diffs.append({
|
|
213
|
+
'language': audio_lang,
|
|
214
|
+
'difference': diff,
|
|
215
|
+
'has_error': diff > 0.5,
|
|
216
|
+
'video_duration': video_duration,
|
|
217
|
+
'audio_duration': audio_duration
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
if diff > 0.5:
|
|
221
|
+
use_shortest = True
|
|
222
|
+
console.log("[red]Warning: Some audio tracks have duration differences (>0.5s)")
|
|
223
|
+
|
|
224
|
+
# Print duration differences for each track
|
|
225
|
+
if use_shortest:
|
|
226
|
+
for track in duration_diffs:
|
|
227
|
+
color = "red" if track['has_error'] else "green"
|
|
228
|
+
console.print(f"[{color}]Audio {track['language']}: Video duration: {track['video_duration']:.2f}s, Audio duration: {track['audio_duration']:.2f}s, Difference: {track['difference']:.2f}s[/{color}]")
|
|
229
|
+
|
|
182
230
|
|
|
183
231
|
# Start command with locate ffmpeg
|
|
184
232
|
ffmpeg_cmd = [get_ffmpeg_path()]
|
|
@@ -238,9 +286,8 @@ def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: s
|
|
|
238
286
|
else:
|
|
239
287
|
ffmpeg_cmd.extend(['-preset', 'fast'])
|
|
240
288
|
|
|
241
|
-
# Use shortest input path
|
|
242
|
-
if
|
|
243
|
-
console.log(f"[red]Use shortest input (Duration difference: {duration_diff:.2f} seconds)...")
|
|
289
|
+
# Use shortest input path if any audio track has significant difference
|
|
290
|
+
if use_shortest:
|
|
244
291
|
ffmpeg_cmd.extend(['-shortest', '-strict', 'experimental'])
|
|
245
292
|
|
|
246
293
|
# Overwrite
|
|
@@ -261,7 +308,7 @@ def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: s
|
|
|
261
308
|
capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join audio")
|
|
262
309
|
print()
|
|
263
310
|
|
|
264
|
-
return out_path
|
|
311
|
+
return out_path, use_shortest
|
|
265
312
|
|
|
266
313
|
|
|
267
314
|
def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_path: str):
|
|
@@ -274,6 +321,18 @@ def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_pat
|
|
|
274
321
|
Each dictionary should contain the 'path' key with the path to the subtitle file and the 'name' key with the name of the subtitle.
|
|
275
322
|
- out_path (str): The path to save the output file.
|
|
276
323
|
"""
|
|
324
|
+
if video_path is None:
|
|
325
|
+
console.log("[red]No video path provided for joining subtitles.")
|
|
326
|
+
return None
|
|
327
|
+
|
|
328
|
+
if subtitles_list is None or len(subtitles_list) == 0:
|
|
329
|
+
console.log("[red]No subtitles provided for joining.")
|
|
330
|
+
return None
|
|
331
|
+
|
|
332
|
+
if out_path is None:
|
|
333
|
+
console.log("[red]No output path provided for joining subtitles.")
|
|
334
|
+
return None
|
|
335
|
+
|
|
277
336
|
ffmpeg_cmd = [get_ffmpeg_path(), "-i", video_path]
|
|
278
337
|
|
|
279
338
|
# Add subtitle input files first
|
|
@@ -47,15 +47,16 @@ def has_audio_stream(video_path: str) -> bool:
|
|
|
47
47
|
return False
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
def get_video_duration(file_path: str) -> float:
|
|
50
|
+
def get_video_duration(file_path: str, file_type: str = "file") -> float:
|
|
51
51
|
"""
|
|
52
|
-
Get the duration of a video
|
|
52
|
+
Get the duration of a media file (video or audio).
|
|
53
53
|
|
|
54
54
|
Parameters:
|
|
55
|
-
- file_path (str): The path to the
|
|
55
|
+
- file_path (str): The path to the media file.
|
|
56
|
+
- file_type (str): The type of the file ('video' or 'audio'). Defaults to 'file'.
|
|
56
57
|
|
|
57
58
|
Returns:
|
|
58
|
-
(float): The duration of the
|
|
59
|
+
(float): The duration of the media file in seconds if successful, None if there's an error.
|
|
59
60
|
"""
|
|
60
61
|
try:
|
|
61
62
|
ffprobe_cmd = [get_ffprobe_path(), '-v', 'error', '-show_format', '-print_format', 'json', file_path]
|
|
@@ -64,23 +65,23 @@ def get_video_duration(file_path: str) -> float:
|
|
|
64
65
|
# Use a with statement to ensure the subprocess is cleaned up properly
|
|
65
66
|
with subprocess.Popen(ffprobe_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) as proc:
|
|
66
67
|
stdout, stderr = proc.communicate()
|
|
67
|
-
|
|
68
|
+
|
|
68
69
|
if proc.returncode != 0:
|
|
69
70
|
logging.error(f"Error: {stderr}")
|
|
70
71
|
return None
|
|
71
|
-
|
|
72
|
+
|
|
72
73
|
# Parse JSON output
|
|
73
74
|
probe_result = json.loads(stdout)
|
|
74
75
|
|
|
75
|
-
# Extract duration from the
|
|
76
|
+
# Extract duration from the media information
|
|
76
77
|
try:
|
|
77
78
|
return float(probe_result['format']['duration'])
|
|
78
|
-
|
|
79
|
+
|
|
79
80
|
except Exception:
|
|
80
81
|
return 1
|
|
81
82
|
|
|
82
83
|
except Exception as e:
|
|
83
|
-
logging.error(f"Get
|
|
84
|
+
logging.error(f"Get {file_type} duration error: {e}, ffprobe path: {get_ffprobe_path()}, file path: {file_path}")
|
|
84
85
|
sys.exit(0)
|
|
85
86
|
|
|
86
87
|
|
|
@@ -242,20 +243,22 @@ def check_duration_v_a(video_path, audio_path, tolerance=1.0):
|
|
|
242
243
|
Returns:
|
|
243
244
|
- tuple: (bool, float) -> True if the duration of the video and audio matches, False otherwise, along with the difference in duration.
|
|
244
245
|
"""
|
|
245
|
-
video_duration = get_video_duration(video_path)
|
|
246
|
-
audio_duration = get_video_duration(audio_path)
|
|
246
|
+
video_duration = get_video_duration(video_path, file_type="video")
|
|
247
|
+
audio_duration = get_video_duration(audio_path, file_type="audio")
|
|
247
248
|
|
|
248
249
|
# Check if either duration is None and specify which one is None
|
|
249
250
|
if video_duration is None and audio_duration is None:
|
|
250
251
|
console.print("[yellow]Warning: Both video and audio durations are None. Returning 0 as duration difference.[/yellow]")
|
|
251
252
|
return False, 0.0
|
|
253
|
+
|
|
252
254
|
elif video_duration is None:
|
|
253
255
|
console.print("[yellow]Warning: Video duration is None. Returning 0 as duration difference.[/yellow]")
|
|
254
256
|
return False, 0.0
|
|
257
|
+
|
|
255
258
|
elif audio_duration is None:
|
|
256
259
|
console.print("[yellow]Warning: Audio duration is None. Returning 0 as duration difference.[/yellow]")
|
|
257
260
|
return False, 0.0
|
|
258
|
-
|
|
261
|
+
|
|
259
262
|
# Calculate the duration difference
|
|
260
263
|
duration_difference = abs(video_duration - audio_duration)
|
|
261
264
|
|
|
@@ -263,4 +266,4 @@ def check_duration_v_a(video_path, audio_path, tolerance=1.0):
|
|
|
263
266
|
if duration_difference <= tolerance:
|
|
264
267
|
return True, duration_difference
|
|
265
268
|
else:
|
|
266
|
-
return False, duration_difference
|
|
269
|
+
return False, duration_difference
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: StreamingCommunity
|
|
3
|
-
Version: 3.3.
|
|
3
|
+
Version: 3.3.1
|
|
4
4
|
Home-page: https://github.com/Lovi-0/StreamingCommunity
|
|
5
5
|
Author: Lovi-0
|
|
6
6
|
Project-URL: Bug Reports, https://github.com/Lovi-0/StreamingCommunity/issues
|
|
@@ -25,6 +25,7 @@ Requires-Dist: ua-generator
|
|
|
25
25
|
Requires-Dist: qbittorrent-api
|
|
26
26
|
Requires-Dist: pyTelegramBotAPI
|
|
27
27
|
Requires-Dist: pywidevine
|
|
28
|
+
Requires-Dist: seleniumbase
|
|
28
29
|
Dynamic: author
|
|
29
30
|
Dynamic: description
|
|
30
31
|
Dynamic: description-content-type
|
|
@@ -906,10 +907,6 @@ python3 telegram_bot.py
|
|
|
906
907
|
```
|
|
907
908
|
</details>
|
|
908
909
|
|
|
909
|
-
# SITE_LOGIN key
|
|
910
|
-
To use some site, you may need a SITE_LOGIN key.
|
|
911
|
-
See [guide.md](.github/.site/guide.md) for instructions on how to obtain it.
|
|
912
|
-
|
|
913
910
|
# Tutorials
|
|
914
911
|
|
|
915
912
|
- [Windows](https://www.youtube.com/watch?v=mZGqK4wdN-k)
|
|
@@ -917,17 +914,11 @@ See [guide.md](.github/.site/guide.md) for instructions on how to obtain it.
|
|
|
917
914
|
- [Pypy](https://www.youtube.com/watch?v=C6m9ZKOK0p4)
|
|
918
915
|
- [Compiled](https://www.youtube.com/watch?v=pm4lqsxkTVo)
|
|
919
916
|
|
|
920
|
-
# To Do
|
|
921
|
-
|
|
922
|
-
- To Finish [website API](https://github.com/Arrowar/StreamingCommunity/tree/test_gui_1)
|
|
923
|
-
- To finish [website API 2](https://github.com/hydrosh/StreamingCommunity/tree/test_gui_1)
|
|
924
|
-
|
|
925
917
|
## Useful Project
|
|
926
918
|
|
|
927
919
|
### 🎯 [Unit3Dup](https://github.com/31December99/Unit3Dup)
|
|
928
920
|
Bot in Python per la generazione e l'upload automatico di torrent su tracker basati su Unit3D.
|
|
929
921
|
|
|
930
|
-
|
|
931
922
|
### 🇮🇹 [MammaMia](https://github.com/UrloMythus/MammaMia)
|
|
932
923
|
Addon per Stremio che consente lo streaming HTTPS di film, serie, anime e TV in diretta in lingua italiana.
|
|
933
924
|
|
|
@@ -11,50 +11,50 @@ StreamingCommunity/Api/Player/sweetpixel.py,sha256=AAdLSD7ASLuZNiBx6btr8S44Wkxtj
|
|
|
11
11
|
StreamingCommunity/Api/Player/vixcloud.py,sha256=0DnukAIBqGqTmL9I6JkpAkHLMf1UG1D_J8c7zt6KDmU,6609
|
|
12
12
|
StreamingCommunity/Api/Player/Helper/Vixcloud/js_parser.py,sha256=U-8QlD5kGzIk3-4t4D6QyYmiDe8UBrSuVi1YHRQb7AU,4295
|
|
13
13
|
StreamingCommunity/Api/Player/Helper/Vixcloud/util.py,sha256=WjpNA-ohE6AIwYOVzacYqy7CR3fXfdf7PfIp69vk8js,5343
|
|
14
|
-
StreamingCommunity/Api/Site/altadefinizione/__init__.py,sha256=
|
|
14
|
+
StreamingCommunity/Api/Site/altadefinizione/__init__.py,sha256=KCzeJZZ7rFu-64Zm7ik20XGFi8KK1wIeF6r7C3SHSkM,5553
|
|
15
15
|
StreamingCommunity/Api/Site/altadefinizione/film.py,sha256=CQG1C9RGbPm_x89xgyTS8IqcP-lihNqCDY9ANghOlhE,3798
|
|
16
16
|
StreamingCommunity/Api/Site/altadefinizione/series.py,sha256=--XPvUzL4K3KKaS41jZ9WToT3wxt2rUwblNoJOp8hs0,8280
|
|
17
17
|
StreamingCommunity/Api/Site/altadefinizione/site.py,sha256=nrDmENnvWbW7iNO7OIGpQWJttzFGipZg0dsC8GiSEBU,2864
|
|
18
18
|
StreamingCommunity/Api/Site/altadefinizione/util/ScrapeSerie.py,sha256=9iulNlnNAhTfI5iKNW3I6pZqYeYwovAswa13L3LPGDM,4251
|
|
19
|
-
StreamingCommunity/Api/Site/animeunity/__init__.py,sha256=
|
|
19
|
+
StreamingCommunity/Api/Site/animeunity/__init__.py,sha256=pmFGKSwpgfCGAXRK-thm4xZGG-J3Yeu-wl83rBoSaBI,5426
|
|
20
20
|
StreamingCommunity/Api/Site/animeunity/film.py,sha256=Vqg6yag2siR-Y3ougBsV8mzdQXChxg6ghz_KVXFQ3pE,998
|
|
21
21
|
StreamingCommunity/Api/Site/animeunity/serie.py,sha256=2PfpXblRpIrHC6gURfKYo16Vx_ZDcQX4CX03wAo8SCQ,5694
|
|
22
22
|
StreamingCommunity/Api/Site/animeunity/site.py,sha256=GLULPQATMHcXiH99d772v1ICH-PnnZgSM3q5__eN-gs,4977
|
|
23
23
|
StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py,sha256=UladSvOlTEVLiV0-rAz45zrET5qRHMuTGuKEpeQoumU,3872
|
|
24
|
-
StreamingCommunity/Api/Site/animeworld/__init__.py,sha256=
|
|
24
|
+
StreamingCommunity/Api/Site/animeworld/__init__.py,sha256=v5ZuJVJCZINxG6_nZ_EQxRAyXmD6V35AlRokgx4kS2U,5139
|
|
25
25
|
StreamingCommunity/Api/Site/animeworld/film.py,sha256=Ysp0k5AlrOsl19S9LV4nwVg8cjFf5w0FZRO9CiO6NxA,1748
|
|
26
26
|
StreamingCommunity/Api/Site/animeworld/serie.py,sha256=b1yuBnLNqJ-IWEVqLnQuYv6VbUQ60a5YFpZbJrS3lnI,3486
|
|
27
27
|
StreamingCommunity/Api/Site/animeworld/site.py,sha256=Zdp6ayA1L6hS1t0q4fclHs7J1eiD16Ta9isTc13Zye8,3746
|
|
28
28
|
StreamingCommunity/Api/Site/animeworld/util/ScrapeSerie.py,sha256=S6sLtQt4Wvp4IVDho1uAXL8_-bbdj-RTRA1D7x9V8n8,3591
|
|
29
|
-
StreamingCommunity/Api/Site/crunchyroll/__init__.py,sha256=
|
|
29
|
+
StreamingCommunity/Api/Site/crunchyroll/__init__.py,sha256=8CuiONjE2G9q1UFrvTvToI7DVKw7RrCGUT2aACDoy2s,5555
|
|
30
30
|
StreamingCommunity/Api/Site/crunchyroll/film.py,sha256=MgA8bwJEf6unT_uBBsLpMqRdbctkLYLGeBB-_fX1wvk,2615
|
|
31
31
|
StreamingCommunity/Api/Site/crunchyroll/series.py,sha256=0JnPc9Bw02Ys-wmdAvcIbAlkyGHbw7ELemWVizWPl1k,7265
|
|
32
|
-
StreamingCommunity/Api/Site/crunchyroll/site.py,sha256=
|
|
32
|
+
StreamingCommunity/Api/Site/crunchyroll/site.py,sha256=udMWlEi4fVG-yVcrsEP2JXdRZeYB_xwETTpPDzyBOno,4072
|
|
33
33
|
StreamingCommunity/Api/Site/crunchyroll/util/ScrapeSerie.py,sha256=ZY6z4pT5q7igiNZnJAvE9x0WiORTvFSH-U_CpqOzusA,7855
|
|
34
34
|
StreamingCommunity/Api/Site/crunchyroll/util/get_license.py,sha256=KpVvJbGUTLeVD-tt8r7ajBPWVT7Tm6GrKwzpW8CS-tg,6881
|
|
35
|
-
StreamingCommunity/Api/Site/guardaserie/__init__.py,sha256=
|
|
35
|
+
StreamingCommunity/Api/Site/guardaserie/__init__.py,sha256=_VTPh3wBh3v73pSOIBxNqRV-DyhxD9iKj7DC6gpgm3I,5179
|
|
36
36
|
StreamingCommunity/Api/Site/guardaserie/series.py,sha256=Q5i_nCAy8zAnublmWuujxrDwvP9XP2D207vrtYOdl_I,6566
|
|
37
37
|
StreamingCommunity/Api/Site/guardaserie/site.py,sha256=_AtrkOb5xUnIfqp212y3p7kWb7BKJYwQHleSRgDOSYA,2239
|
|
38
38
|
StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py,sha256=4wk2TVb4YDoPrhR2uuNijYuOpSt9mhcxwcXhmwTHPUY,4396
|
|
39
|
-
StreamingCommunity/Api/Site/mediasetinfinity/__init__.py,sha256=
|
|
40
|
-
StreamingCommunity/Api/Site/mediasetinfinity/film.py,sha256=
|
|
39
|
+
StreamingCommunity/Api/Site/mediasetinfinity/__init__.py,sha256=ZmyJRjzdfnvlSsVG05IYCocvJsCyzfBiGPxjN1RAHmY,5428
|
|
40
|
+
StreamingCommunity/Api/Site/mediasetinfinity/film.py,sha256=ZfLbc3631UwmBThtA_X0nQew__hSl1l5ASOr9IdS71w,2334
|
|
41
41
|
StreamingCommunity/Api/Site/mediasetinfinity/series.py,sha256=BtZaZmpJ7-tbMPmosRLbZG4-oaIMK3Q1CHV29A5xmng,6898
|
|
42
|
-
StreamingCommunity/Api/Site/mediasetinfinity/site.py,sha256=
|
|
43
|
-
StreamingCommunity/Api/Site/mediasetinfinity/util/ScrapeSerie.py,sha256
|
|
42
|
+
StreamingCommunity/Api/Site/mediasetinfinity/site.py,sha256=qCpc02RaEzIJll9m3IJOgDLQdU58MnGIXlBY5_GqU84,4374
|
|
43
|
+
StreamingCommunity/Api/Site/mediasetinfinity/util/ScrapeSerie.py,sha256=-zzBI2ugkIXaqlwTfFIMcmzpIXIbQiimRk_j9UVxZ5E,11154
|
|
44
44
|
StreamingCommunity/Api/Site/mediasetinfinity/util/fix_mpd.py,sha256=B7uZfQ8X4p8KsiPVangFSs5rKKKpA3tavjPCdNrqyCc,1712
|
|
45
|
-
StreamingCommunity/Api/Site/mediasetinfinity/util/get_license.py,sha256=
|
|
46
|
-
StreamingCommunity/Api/Site/raiplay/__init__.py,sha256=
|
|
45
|
+
StreamingCommunity/Api/Site/mediasetinfinity/util/get_license.py,sha256=hwHOsdLznJ_KIS5F6PnfFNxqP4nxfoassATxoLHN60E,11872
|
|
46
|
+
StreamingCommunity/Api/Site/raiplay/__init__.py,sha256=ExPwFYzTxyF8giuUb_kNaYgLqsiKdJ2faYhYDmsH6CM,5433
|
|
47
47
|
StreamingCommunity/Api/Site/raiplay/film.py,sha256=Ep1Kcbvm2nZoHvaxW5Qbg4FBl9QZORM-NEi2obhQyos,3029
|
|
48
|
-
StreamingCommunity/Api/Site/raiplay/series.py,sha256=
|
|
48
|
+
StreamingCommunity/Api/Site/raiplay/series.py,sha256=AojsVWqMk_o0i2zGVvXNVDeCUuNaPcyG-QUTtGquYkE,7829
|
|
49
49
|
StreamingCommunity/Api/Site/raiplay/site.py,sha256=e0DoTIcSNLbk8uEBGgYCG6gbZ6yv-yo0Z-rT7PLNTlo,3182
|
|
50
50
|
StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py,sha256=KsEL8XSr_c1vvaPo-_AY9RGE7_eHZ8oN2iyZtB0ocrw,5880
|
|
51
51
|
StreamingCommunity/Api/Site/raiplay/util/get_license.py,sha256=96Q5aSWhtxtmQl2yzylL-1x3jY24UpLLZlEE6YrO_gs,978
|
|
52
|
-
StreamingCommunity/Api/Site/streamingcommunity/__init__.py,sha256=
|
|
52
|
+
StreamingCommunity/Api/Site/streamingcommunity/__init__.py,sha256=lLQKaDrHyxehXmjpyx7Zf2rLYsBG22xo45rC_4oJhKc,5618
|
|
53
53
|
StreamingCommunity/Api/Site/streamingcommunity/film.py,sha256=aA0tsDc5GCFFkrn_s2QKNcnyBYhK2S5OnDx29P1_rhA,2779
|
|
54
54
|
StreamingCommunity/Api/Site/streamingcommunity/series.py,sha256=eLCeVjur2w2J1PgYzhe8dIJYGZus6YEyQr5H0YXazH4,9055
|
|
55
55
|
StreamingCommunity/Api/Site/streamingcommunity/site.py,sha256=o1jdgF1nnUCnPnvybfc6a-0esHSYcRGjtUEOAXk6YAE,4131
|
|
56
56
|
StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py,sha256=b-PWO1fP68yfptLb5X3i4M1PFZHKkqJh32yHgKsoIGs,5684
|
|
57
|
-
StreamingCommunity/Api/Site/streamingwatch/__init__.py,sha256=
|
|
57
|
+
StreamingCommunity/Api/Site/streamingwatch/__init__.py,sha256=Nx8fedFHn4Ewy4iFDutyMiTzdYQKhfkJYJ8KlZjBOhg,5583
|
|
58
58
|
StreamingCommunity/Api/Site/streamingwatch/film.py,sha256=B_9z9xzfuqEbzv-eVHuadvOKTcEiHhRU92Gk_f2u7Zs,1693
|
|
59
59
|
StreamingCommunity/Api/Site/streamingwatch/series.py,sha256=nxGef_djMUZLAEDbHG-hbbly0eWagxPm8xu5aFIlSrI,6245
|
|
60
60
|
StreamingCommunity/Api/Site/streamingwatch/site.py,sha256=KEtm-TeTuryAODXdvJAGPHauaq8cVl8QxHV6CDT1mzo,3474
|
|
@@ -68,17 +68,17 @@ StreamingCommunity/Api/Template/Util/manage_ep.py,sha256=JMGLj_SJY-K5YuvKISurEYy
|
|
|
68
68
|
StreamingCommunity/Lib/Downloader/__init__.py,sha256=Zh92xTvBIEIjNQN22iXItG7_VqiDGbOpO5gOZDBdGxc,288
|
|
69
69
|
StreamingCommunity/Lib/Downloader/DASH/cdm_helpher.py,sha256=-odu7Hc5dvzRXldQRlFalt6IVfvqi6XG_DCSyyqBGJ4,4767
|
|
70
70
|
StreamingCommunity/Lib/Downloader/DASH/decrypt.py,sha256=y3F5zrED-FSQzwE9e5rTl07aWkg76qwFgxtziPpPVuQ,2352
|
|
71
|
-
StreamingCommunity/Lib/Downloader/DASH/downloader.py,sha256=
|
|
71
|
+
StreamingCommunity/Lib/Downloader/DASH/downloader.py,sha256=ioOWGidwsdU3WqayYZZ2N6sOPkrWxCgbZC0aoxCBL40,9341
|
|
72
72
|
StreamingCommunity/Lib/Downloader/DASH/parser.py,sha256=QUyYhmu-zI2GieiNZaoOoDVPTclSKXsrdeToAtbC9yI,9858
|
|
73
73
|
StreamingCommunity/Lib/Downloader/DASH/segments.py,sha256=oiip_q3QOY_c17_t66D0m974hyzsQXivhHY5-uE9dlQ,12747
|
|
74
|
-
StreamingCommunity/Lib/Downloader/HLS/downloader.py,sha256=
|
|
75
|
-
StreamingCommunity/Lib/Downloader/HLS/segments.py,sha256=
|
|
74
|
+
StreamingCommunity/Lib/Downloader/HLS/downloader.py,sha256=RqiLBZnKujbYqkmEAgqKnxmjJF2ar0nA__UEWFIDn68,21839
|
|
75
|
+
StreamingCommunity/Lib/Downloader/HLS/segments.py,sha256=SH1GCl4SObqqQoLiDwLDf3NTrjJoIgFby9Bqlt4s2Vo,17916
|
|
76
76
|
StreamingCommunity/Lib/Downloader/MP4/downloader.py,sha256=_z61uQVnlfh-ktWYTisfbg_a4C3tpK7MO8HUdgLhTpI,8060
|
|
77
77
|
StreamingCommunity/Lib/Downloader/TOR/downloader.py,sha256=tYOCuKkKDcTIJ-2bGIeplovRkLTdp89i8lUvJs_N9jc,19133
|
|
78
78
|
StreamingCommunity/Lib/FFmpeg/__init__.py,sha256=NZmOXpnc5jvdvjNnHPxvj5bzp0GUuNMXd00Fcy4rQPI,258
|
|
79
79
|
StreamingCommunity/Lib/FFmpeg/capture.py,sha256=UUBaFK8WLkvqxsElqyEj-JvDvd9zUFZ2bRwRoUcGjVk,5039
|
|
80
|
-
StreamingCommunity/Lib/FFmpeg/command.py,sha256=
|
|
81
|
-
StreamingCommunity/Lib/FFmpeg/util.py,sha256=
|
|
80
|
+
StreamingCommunity/Lib/FFmpeg/command.py,sha256=WNPBgEy9I2JRGbO_c4wZzmu0Knx2tx08V2WCrUgAwpA,12654
|
|
81
|
+
StreamingCommunity/Lib/FFmpeg/util.py,sha256=enx5Z29A4aHkq09iaP80NVMttQn9PZ8ruGe6GvURjGk,9515
|
|
82
82
|
StreamingCommunity/Lib/M3U8/__init__.py,sha256=Zxij4WFCxjwyfswUfBv0oys_o0vQpAL5PoK5TGG_StY,288
|
|
83
83
|
StreamingCommunity/Lib/M3U8/decryptor.py,sha256=1IutV-rZm3XLWGw1sEkApa3VkdwBc7BcC8AfyJ_zrrg,2965
|
|
84
84
|
StreamingCommunity/Lib/M3U8/estimator.py,sha256=S4pa4hBgtJsh11mmQcpQK_1_xhkb9fHrIZdfWBxEbAg,5592
|
|
@@ -91,7 +91,7 @@ StreamingCommunity/TelegramHelp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
|
|
|
91
91
|
StreamingCommunity/TelegramHelp/config.json,sha256=v7FjA4smyLWZxChATewmvxDjJcclOCRZ_BIFPJd8Jvc,1374
|
|
92
92
|
StreamingCommunity/TelegramHelp/telegram_bot.py,sha256=KiurpTrgiUNyMcLfceseaxV4xGegCUSJ8YttSeGXhJM,26429
|
|
93
93
|
StreamingCommunity/Upload/update.py,sha256=9DStkCA_GGHjf47eDjUe-YeQ2mXhjmY-6E5C-HAwPlo,3829
|
|
94
|
-
StreamingCommunity/Upload/version.py,sha256=
|
|
94
|
+
StreamingCommunity/Upload/version.py,sha256=8GQwLbyQwIIaG_jRKVooDEaiM4x6UJJF-eo9SHw0G3A,171
|
|
95
95
|
StreamingCommunity/Util/bento4_installer.py,sha256=P5ipziMCvezxan8GUh9vm8B1LXGyHusFVDf842LSwis,6966
|
|
96
96
|
StreamingCommunity/Util/color.py,sha256=NvD0Eni-25oOOkY-szCEoc0lGvzQxyL7xhM0RE4EvUM,458
|
|
97
97
|
StreamingCommunity/Util/config_json.py,sha256=vB6o6J5V874_bp9Y87VCCxp2wig0P3vwz4--zVokH4o,28576
|
|
@@ -102,9 +102,9 @@ StreamingCommunity/Util/logger.py,sha256=9kGD6GmWj2pM8ADpJc85o7jm8DD0c5Aguqnq-9k
|
|
|
102
102
|
StreamingCommunity/Util/message.py,sha256=tstYQ9NxlfWupYkqPrOqOLqrKEb1iG5NsvIMNTUXXvA,1332
|
|
103
103
|
StreamingCommunity/Util/os.py,sha256=VgKPcQxFTU_aCDpIw-T-z61xl9sCCAWZZdc0UZBieu8,17135
|
|
104
104
|
StreamingCommunity/Util/table.py,sha256=xXfgd09oIYSxpJwnnkPZdWJ0DcCihSqxfvYYAEo-YMA,9806
|
|
105
|
-
streamingcommunity-3.3.
|
|
106
|
-
streamingcommunity-3.3.
|
|
107
|
-
streamingcommunity-3.3.
|
|
108
|
-
streamingcommunity-3.3.
|
|
109
|
-
streamingcommunity-3.3.
|
|
110
|
-
streamingcommunity-3.3.
|
|
105
|
+
streamingcommunity-3.3.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
106
|
+
streamingcommunity-3.3.1.dist-info/METADATA,sha256=JXI1Q9AzFDz0tbOn7gZd43PXslZDWM8sjj_fBoobMSU,27157
|
|
107
|
+
streamingcommunity-3.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
108
|
+
streamingcommunity-3.3.1.dist-info/entry_points.txt,sha256=Qph9XYfDC8n4LfDLOSl6gJGlkb9eFb5f-JOr_Wb_5rk,67
|
|
109
|
+
streamingcommunity-3.3.1.dist-info/top_level.txt,sha256=YsOcxKP-WOhWpIWgBlh0coll9XUx7aqmRPT7kmt3fH0,19
|
|
110
|
+
streamingcommunity-3.3.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|