StreamingCommunity 2.9.1__py3-none-any.whl → 2.9.3__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/Helper/Vixcloud/util.py +1 -6
- StreamingCommunity/Api/Player/maxstream.py +3 -0
- StreamingCommunity/Api/Player/supervideo.py +4 -0
- StreamingCommunity/Api/Site/1337xx/__init__.py +1 -1
- StreamingCommunity/Api/Site/animeunity/film_serie.py +1 -1
- StreamingCommunity/Api/Site/animeunity/site.py +14 -32
- StreamingCommunity/Api/Site/cb01new/__init__.py +1 -1
- StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +1 -1
- StreamingCommunity/Api/Site/guardaserie/__init__.py +1 -1
- StreamingCommunity/Api/Site/guardaserie/series.py +1 -1
- StreamingCommunity/Api/Site/mostraguarda/__init__.py +1 -1
- StreamingCommunity/Api/Site/streamingcommunity/series.py +1 -1
- StreamingCommunity/Api/Template/Util/manage_ep.py +42 -16
- StreamingCommunity/Api/Template/site.py +1 -1
- StreamingCommunity/Lib/Downloader/HLS/downloader.py +13 -2
- StreamingCommunity/Lib/Downloader/HLS/segments.py +37 -11
- StreamingCommunity/Lib/Downloader/MP4/downloader.py +2 -1
- StreamingCommunity/Lib/FFmpeg/command.py +2 -2
- StreamingCommunity/Lib/FFmpeg/util.py +11 -15
- StreamingCommunity/Lib/M3U8/estimator.py +1 -1
- StreamingCommunity/Lib/TMBD/tmdb.py +1 -1
- StreamingCommunity/Upload/version.py +1 -1
- {StreamingCommunity-2.9.1.dist-info → StreamingCommunity-2.9.3.dist-info}/METADATA +2 -1
- {StreamingCommunity-2.9.1.dist-info → StreamingCommunity-2.9.3.dist-info}/RECORD +28 -28
- {StreamingCommunity-2.9.1.dist-info → StreamingCommunity-2.9.3.dist-info}/LICENSE +0 -0
- {StreamingCommunity-2.9.1.dist-info → StreamingCommunity-2.9.3.dist-info}/WHEEL +0 -0
- {StreamingCommunity-2.9.1.dist-info → StreamingCommunity-2.9.3.dist-info}/entry_points.txt +0 -0
- {StreamingCommunity-2.9.1.dist-info → StreamingCommunity-2.9.3.dist-info}/top_level.txt +0 -0
|
@@ -8,14 +8,12 @@ class Episode:
|
|
|
8
8
|
self.data = data
|
|
9
9
|
|
|
10
10
|
self.id: int = data.get('id', 0)
|
|
11
|
-
self.scws_id: int = data.get('scws_id', 0)
|
|
12
11
|
self.number: int = data.get('number', 1)
|
|
13
12
|
self.name: str = data.get('name', '')
|
|
14
|
-
self.plot: str = data.get('plot', '')
|
|
15
13
|
self.duration: int = data.get('duration', 0)
|
|
16
14
|
|
|
17
15
|
def __str__(self):
|
|
18
|
-
return f"Episode(id={self.id}, number={self.number}, name='{self.name}',
|
|
16
|
+
return f"Episode(id={self.id}, number={self.number}, name='{self.name}', duration={self.duration} sec)"
|
|
19
17
|
|
|
20
18
|
class EpisodeManager:
|
|
21
19
|
def __init__(self):
|
|
@@ -89,12 +87,9 @@ class Season:
|
|
|
89
87
|
self.season_data = season_data
|
|
90
88
|
|
|
91
89
|
self.id: int = season_data.get('id', 0)
|
|
92
|
-
self.scws_id: int = season_data.get('scws_id', 0)
|
|
93
|
-
self.imdb_id: int = season_data.get('imdb_id', 0)
|
|
94
90
|
self.number: int = season_data.get('number', 0)
|
|
95
91
|
self.name: str = season_data.get('name', '')
|
|
96
92
|
self.slug: str = season_data.get('slug', '')
|
|
97
|
-
self.plot: str = season_data.get('plot', '')
|
|
98
93
|
self.type: str = season_data.get('type', '')
|
|
99
94
|
self.seasons_count: int = season_data.get('seasons_count', 0)
|
|
100
95
|
|
|
@@ -119,6 +119,8 @@ class VideoSource:
|
|
|
119
119
|
|
|
120
120
|
if match:
|
|
121
121
|
return match.group(1)
|
|
122
|
+
else:
|
|
123
|
+
logging.error("Failed to find M3U8 URL: No match found")
|
|
122
124
|
|
|
123
125
|
else:
|
|
124
126
|
|
|
@@ -151,6 +153,8 @@ class VideoSource:
|
|
|
151
153
|
|
|
152
154
|
if match:
|
|
153
155
|
return match.group(1)
|
|
156
|
+
else:
|
|
157
|
+
logging.error("Failed to find M3U8 URL: No match found")
|
|
154
158
|
|
|
155
159
|
return None
|
|
156
160
|
|
|
@@ -70,7 +70,7 @@ def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_so
|
|
|
70
70
|
video_source.get_embed(obj_episode.id)
|
|
71
71
|
|
|
72
72
|
# Create output path
|
|
73
|
-
title_name = f"{scrape_serie.series_name}_EP_{dynamic_format_number(
|
|
73
|
+
title_name = f"{scrape_serie.series_name}_EP_{dynamic_format_number(str(obj_episode.number))}.mp4"
|
|
74
74
|
|
|
75
75
|
if scrape_serie.is_series:
|
|
76
76
|
mp4_path = os_manager.get_sanitize_path(os.path.join(site_constant.ANIME_FOLDER, scrape_serie.series_name))
|
|
@@ -12,6 +12,7 @@ from rich.console import Console
|
|
|
12
12
|
|
|
13
13
|
# Internal utilities
|
|
14
14
|
from StreamingCommunity.Util.config_json import config_manager
|
|
15
|
+
from StreamingCommunity.Util.headers import get_userAgent
|
|
15
16
|
from StreamingCommunity.Util.table import TVShowManager
|
|
16
17
|
from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
|
|
17
18
|
|
|
@@ -29,7 +30,7 @@ table_show_manager = TVShowManager()
|
|
|
29
30
|
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
30
31
|
|
|
31
32
|
|
|
32
|
-
def get_token(
|
|
33
|
+
def get_token() -> dict:
|
|
33
34
|
"""
|
|
34
35
|
Function to retrieve session tokens from a specified website.
|
|
35
36
|
|
|
@@ -40,8 +41,6 @@ def get_token(site_name: str, domain: str) -> dict:
|
|
|
40
41
|
Returns:
|
|
41
42
|
- dict: A dictionary containing session tokens. The keys are 'XSRF_TOKEN', 'animeunity_session', and 'csrf_token'.
|
|
42
43
|
"""
|
|
43
|
-
|
|
44
|
-
# Send a GET request to the specified URL composed of the site name and domain
|
|
45
44
|
response = httpx.get(
|
|
46
45
|
url=site_constant.FULL_URL,
|
|
47
46
|
timeout=max_timeout
|
|
@@ -50,17 +49,11 @@ def get_token(site_name: str, domain: str) -> dict:
|
|
|
50
49
|
|
|
51
50
|
# Initialize variables to store CSRF token
|
|
52
51
|
find_csrf_token = None
|
|
53
|
-
|
|
54
|
-
# Parse the HTML response using BeautifulSoup
|
|
55
52
|
soup = BeautifulSoup(response.text, "html.parser")
|
|
56
53
|
|
|
57
|
-
# Loop through all meta tags in the HTML response
|
|
58
54
|
for html_meta in soup.find_all("meta"):
|
|
59
|
-
|
|
60
|
-
# Check if the meta tag has a 'name' attribute equal to "csrf-token"
|
|
61
55
|
if html_meta.get('name') == "csrf-token":
|
|
62
56
|
|
|
63
|
-
# If found, retrieve the content of the meta tag, which is the CSRF token
|
|
64
57
|
find_csrf_token = html_meta.get('content')
|
|
65
58
|
|
|
66
59
|
logging.info(f"Extract: ('animeunity_session': {response.cookies['animeunity_session']}, 'csrf_token': {find_csrf_token})")
|
|
@@ -83,13 +76,12 @@ def get_real_title(record):
|
|
|
83
76
|
Returns:
|
|
84
77
|
- str: The title found in the record. If no title is found, returns None.
|
|
85
78
|
"""
|
|
86
|
-
|
|
87
|
-
if record['title'] is not None:
|
|
88
|
-
return record['title']
|
|
89
|
-
|
|
90
|
-
elif record['title_eng'] is not None:
|
|
79
|
+
if record['title_eng'] is not None:
|
|
91
80
|
return record['title_eng']
|
|
92
81
|
|
|
82
|
+
elif record['title'] is not None:
|
|
83
|
+
return record['title']
|
|
84
|
+
|
|
93
85
|
else:
|
|
94
86
|
return record['title_it']
|
|
95
87
|
|
|
@@ -117,26 +109,15 @@ def title_search(title: str) -> int:
|
|
|
117
109
|
console.print("[bold red]Error: Unable to determine valid domain or base URL.[/bold red]")
|
|
118
110
|
console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]")
|
|
119
111
|
sys.exit(1)
|
|
120
|
-
|
|
121
|
-
data = get_token(site_constant.SITE_NAME, domain_to_use)
|
|
122
|
-
|
|
123
|
-
# Prepare cookies to be used in the request
|
|
124
|
-
cookies = {
|
|
125
|
-
'animeunity_session': data.get('animeunity_session')
|
|
126
|
-
}
|
|
127
112
|
|
|
128
|
-
#
|
|
113
|
+
# Create parameter for request
|
|
114
|
+
data = get_token()
|
|
115
|
+
cookies = {'animeunity_session': data.get('animeunity_session')}
|
|
129
116
|
headers = {
|
|
130
|
-
'
|
|
131
|
-
'accept-language': 'it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7',
|
|
132
|
-
'content-type': 'application/json;charset=UTF-8',
|
|
117
|
+
'user-agent': get_userAgent(),
|
|
133
118
|
'x-csrf-token': data.get('csrf_token')
|
|
134
119
|
}
|
|
135
|
-
|
|
136
|
-
# Prepare JSON data to be sent in the request
|
|
137
|
-
json_data = {
|
|
138
|
-
'title': title
|
|
139
|
-
}
|
|
120
|
+
json_data = {'title': title}
|
|
140
121
|
|
|
141
122
|
# Send a POST request to the API endpoint for live search
|
|
142
123
|
try:
|
|
@@ -167,8 +148,9 @@ def title_search(title: str) -> int:
|
|
|
167
148
|
'slug': dict_title.get('slug'),
|
|
168
149
|
'name': dict_title.get('name'),
|
|
169
150
|
'type': dict_title.get('type'),
|
|
170
|
-
'
|
|
171
|
-
'episodes_count': dict_title.get('episodes_count')
|
|
151
|
+
'status': dict_title.get('status'),
|
|
152
|
+
'episodes_count': dict_title.get('episodes_count'),
|
|
153
|
+
'plot': ' '.join((words := str(dict_title.get('plot', '')).split())[:10]) + ('...' if len(words) > 10 else '')
|
|
172
154
|
})
|
|
173
155
|
|
|
174
156
|
if site_constant.TELEGRAM_BOT:
|
|
@@ -51,7 +51,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scap
|
|
|
51
51
|
- bool: kill handler status
|
|
52
52
|
"""
|
|
53
53
|
start_message()
|
|
54
|
-
index_season_selected = dynamic_format_number(index_season_selected)
|
|
54
|
+
index_season_selected = dynamic_format_number(str(index_season_selected))
|
|
55
55
|
|
|
56
56
|
# Get info about episode
|
|
57
57
|
obj_episode = scape_info_serie.list_episodes[index_episode_selected - 1]
|
|
@@ -50,7 +50,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
50
50
|
- bool: kill handler status
|
|
51
51
|
"""
|
|
52
52
|
start_message()
|
|
53
|
-
index_season_selected = dynamic_format_number(index_season_selected)
|
|
53
|
+
index_season_selected = dynamic_format_number(str(index_season_selected))
|
|
54
54
|
|
|
55
55
|
# Get info about episode
|
|
56
56
|
obj_episode = scrape_serie.episode_manager.get(index_episode_selected - 1)
|
|
@@ -22,24 +22,50 @@ console = Console()
|
|
|
22
22
|
MAP_EPISODE = config_manager.get('OUT_FOLDER', 'map_episode_name')
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def dynamic_format_number(
|
|
25
|
+
def dynamic_format_number(number_str: str) -> str:
|
|
26
26
|
"""
|
|
27
|
-
Formats
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
Formats an episode number string, intelligently handling both integer and decimal episode numbers.
|
|
28
|
+
|
|
29
|
+
This function is designed to handle various episode number formats commonly found in media series:
|
|
30
|
+
1. For integer episode numbers less than 10 (e.g., 1, 2, ..., 9), it adds a leading zero (e.g., 01, 02, ..., 09)
|
|
31
|
+
2. For integer episode numbers 10 and above, it preserves the original format without adding leading zeros
|
|
32
|
+
3. For decimal episode numbers (e.g., "7.5", "10.5"), it preserves the decimal format exactly as provided
|
|
33
|
+
|
|
34
|
+
The function is particularly useful for media file naming conventions where special episodes
|
|
35
|
+
or OVAs may have decimal notations (like episode 7.5) which should be preserved in their original format.
|
|
30
36
|
|
|
31
37
|
Parameters:
|
|
32
|
-
-
|
|
38
|
+
- number_str (str): The episode number as a string, which may contain integers or decimals.
|
|
33
39
|
|
|
34
40
|
Returns:
|
|
35
|
-
- str: The formatted number
|
|
41
|
+
- str: The formatted episode number string, with appropriate handling based on the input type.
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
44
|
+
>>> dynamic_format_number("7")
|
|
45
|
+
"07"
|
|
46
|
+
>>> dynamic_format_number("15")
|
|
47
|
+
"15"
|
|
48
|
+
>>> dynamic_format_number("7.5")
|
|
49
|
+
"7.5"
|
|
50
|
+
>>> dynamic_format_number("10.5")
|
|
51
|
+
"10.5"
|
|
36
52
|
"""
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
53
|
+
try:
|
|
54
|
+
if '.' in number_str:
|
|
55
|
+
return number_str
|
|
56
|
+
|
|
57
|
+
n = int(number_str)
|
|
58
|
+
|
|
59
|
+
if n < 10:
|
|
60
|
+
width = len(str(n)) + 1
|
|
61
|
+
else:
|
|
62
|
+
width = len(str(n))
|
|
41
63
|
|
|
42
|
-
|
|
64
|
+
return str(n).zfill(width)
|
|
65
|
+
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logging.warning(f"Could not format episode number '{number_str}': {str(e)}. Using original format.")
|
|
68
|
+
return number_str
|
|
43
69
|
|
|
44
70
|
|
|
45
71
|
def manage_selection(cmd_insert: str, max_count: int) -> List[int]:
|
|
@@ -103,14 +129,14 @@ def map_episode_title(tv_name: str, number_season: int, episode_number: int, epi
|
|
|
103
129
|
map_episode_temp = map_episode_temp.replace("%(tv_name)", os_manager.get_sanitize_file(tv_name))
|
|
104
130
|
|
|
105
131
|
if number_season != None:
|
|
106
|
-
map_episode_temp = map_episode_temp.replace("%(season)", number_season)
|
|
132
|
+
map_episode_temp = map_episode_temp.replace("%(season)", str(number_season))
|
|
107
133
|
else:
|
|
108
|
-
map_episode_temp = map_episode_temp.replace("%(season)", dynamic_format_number(0))
|
|
134
|
+
map_episode_temp = map_episode_temp.replace("%(season)", dynamic_format_number(str(0)))
|
|
109
135
|
|
|
110
136
|
if episode_number != None:
|
|
111
|
-
map_episode_temp = map_episode_temp.replace("%(episode)", dynamic_format_number(episode_number))
|
|
137
|
+
map_episode_temp = map_episode_temp.replace("%(episode)", dynamic_format_number(str(episode_number)))
|
|
112
138
|
else:
|
|
113
|
-
map_episode_temp = map_episode_temp.replace("%(episode)", dynamic_format_number(0))
|
|
139
|
+
map_episode_temp = map_episode_temp.replace("%(episode)", dynamic_format_number(str(0)))
|
|
114
140
|
|
|
115
141
|
if episode_name != None:
|
|
116
142
|
map_episode_temp = map_episode_temp.replace("%(episode_name)", os_manager.get_sanitize_file(episode_name))
|
|
@@ -227,7 +253,7 @@ def display_episodes_list(episodes_manager) -> str:
|
|
|
227
253
|
last_command = table_show_manager.run()
|
|
228
254
|
|
|
229
255
|
if last_command in ("q", "quit"):
|
|
230
|
-
console.print("\n[red]Quit
|
|
256
|
+
console.print("\n[red]Quit ...")
|
|
231
257
|
sys.exit(0)
|
|
232
258
|
|
|
233
259
|
return last_command
|
|
@@ -72,7 +72,7 @@ def get_select_title(table_show_manager, media_search_manager):
|
|
|
72
72
|
|
|
73
73
|
# Handle user's quit command
|
|
74
74
|
if last_command == "q" or last_command == "quit":
|
|
75
|
-
console.print("\n[red]Quit
|
|
75
|
+
console.print("\n[red]Quit ...")
|
|
76
76
|
sys.exit(0)
|
|
77
77
|
|
|
78
78
|
# Check if the selected index is within range
|
|
@@ -436,6 +436,18 @@ class HLS_Downloader:
|
|
|
436
436
|
if TELEGRAM_BOT:
|
|
437
437
|
bot.send_message(f"Contenuto già scaricato!", None)
|
|
438
438
|
return response
|
|
439
|
+
|
|
440
|
+
if GET_ONLY_LINK:
|
|
441
|
+
console.print(f"URL: {self.m3u8_url}[/bold red]")
|
|
442
|
+
return {
|
|
443
|
+
'path': None,
|
|
444
|
+
'url': self.m3u8_url,
|
|
445
|
+
'is_master': getattr(self.m3u8_manager, 'is_master', None),
|
|
446
|
+
'msg': None,
|
|
447
|
+
'error': error_msg,
|
|
448
|
+
'stopped': True
|
|
449
|
+
}
|
|
450
|
+
|
|
439
451
|
|
|
440
452
|
self.path_manager.setup_directories()
|
|
441
453
|
|
|
@@ -466,9 +478,8 @@ class HLS_Downloader:
|
|
|
466
478
|
|
|
467
479
|
final_file = self.merge_manager.merge()
|
|
468
480
|
self.path_manager.move_final_file(final_file)
|
|
469
|
-
self.path_manager.cleanup()
|
|
470
|
-
|
|
471
481
|
self._print_summary()
|
|
482
|
+
self.path_manager.cleanup()
|
|
472
483
|
|
|
473
484
|
return {
|
|
474
485
|
'path': self.path_manager.output_path,
|
|
@@ -74,6 +74,9 @@ class M3U8_Segments:
|
|
|
74
74
|
|
|
75
75
|
# Sync
|
|
76
76
|
self.queue = PriorityQueue()
|
|
77
|
+
self.buffer = {}
|
|
78
|
+
self.expected_index = 0
|
|
79
|
+
|
|
77
80
|
self.stop_event = threading.Event()
|
|
78
81
|
self.downloaded_segments = set()
|
|
79
82
|
self.base_timeout = 0.5
|
|
@@ -94,6 +97,15 @@ class M3U8_Segments:
|
|
|
94
97
|
self.active_retries_lock = threading.Lock()
|
|
95
98
|
|
|
96
99
|
def __get_key__(self, m3u8_parser: M3U8_Parser) -> bytes:
|
|
100
|
+
"""
|
|
101
|
+
Fetches the encryption key from the M3U8 playlist.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
m3u8_parser (M3U8_Parser): An instance of M3U8_Parser containing parsed M3U8 data.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
bytes: The decryption key in byte format.
|
|
108
|
+
"""
|
|
97
109
|
key_uri = urljoin(self.url, m3u8_parser.keys.get('uri'))
|
|
98
110
|
parsed_url = urlparse(key_uri)
|
|
99
111
|
self.key_base_url = f"{parsed_url.scheme}://{parsed_url.netloc}/"
|
|
@@ -110,6 +122,12 @@ class M3U8_Segments:
|
|
|
110
122
|
raise Exception(f"Failed to fetch key: {e}")
|
|
111
123
|
|
|
112
124
|
def parse_data(self, m3u8_content: str) -> None:
|
|
125
|
+
"""
|
|
126
|
+
Parses the M3U8 content and extracts necessary data.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
m3u8_content (str): The raw M3U8 playlist content.
|
|
130
|
+
"""
|
|
113
131
|
m3u8_parser = M3U8_Parser()
|
|
114
132
|
m3u8_parser.parse_data(uri=self.url, raw_content=m3u8_content)
|
|
115
133
|
|
|
@@ -131,6 +149,14 @@ class M3U8_Segments:
|
|
|
131
149
|
self.class_ts_estimator.total_segments = len(self.segments)
|
|
132
150
|
|
|
133
151
|
def get_info(self) -> None:
|
|
152
|
+
"""
|
|
153
|
+
Retrieves M3U8 playlist information from the given URL.
|
|
154
|
+
|
|
155
|
+
If the URL is an index URL, this method:
|
|
156
|
+
- Sends an HTTP GET request to fetch the M3U8 playlist.
|
|
157
|
+
- Parses the M3U8 content using `parse_data`.
|
|
158
|
+
- Saves the playlist to a temporary folder.
|
|
159
|
+
"""
|
|
134
160
|
if self.is_index_url:
|
|
135
161
|
try:
|
|
136
162
|
client_params = {'headers': {'User-Agent': get_userAgent()}, 'timeout': MAX_TIMEOOUT}
|
|
@@ -251,9 +277,6 @@ class M3U8_Segments:
|
|
|
251
277
|
"""
|
|
252
278
|
Writes segments to file with additional verification.
|
|
253
279
|
"""
|
|
254
|
-
buffer = {}
|
|
255
|
-
expected_index = 0
|
|
256
|
-
|
|
257
280
|
with open(self.tmp_file_path, 'wb') as f:
|
|
258
281
|
while not self.stop_event.is_set() or not self.queue.empty():
|
|
259
282
|
if self.interrupt_flag.is_set():
|
|
@@ -267,28 +290,28 @@ class M3U8_Segments:
|
|
|
267
290
|
|
|
268
291
|
# Handle failed segments
|
|
269
292
|
if segment_content is None:
|
|
270
|
-
if index == expected_index:
|
|
271
|
-
expected_index += 1
|
|
293
|
+
if index == self.expected_index:
|
|
294
|
+
self.expected_index += 1
|
|
272
295
|
continue
|
|
273
296
|
|
|
274
297
|
# Write segment if it's the next expected one
|
|
275
|
-
if index == expected_index:
|
|
298
|
+
if index == self.expected_index:
|
|
276
299
|
f.write(segment_content)
|
|
277
300
|
f.flush()
|
|
278
|
-
expected_index += 1
|
|
301
|
+
self.expected_index += 1
|
|
279
302
|
|
|
280
303
|
# Write any buffered segments that are now in order
|
|
281
|
-
while expected_index in buffer:
|
|
282
|
-
next_segment = buffer.pop(expected_index)
|
|
304
|
+
while self.expected_index in self.buffer:
|
|
305
|
+
next_segment = self.buffer.pop(self.expected_index)
|
|
283
306
|
|
|
284
307
|
if next_segment is not None:
|
|
285
308
|
f.write(next_segment)
|
|
286
309
|
f.flush()
|
|
287
310
|
|
|
288
|
-
expected_index += 1
|
|
311
|
+
self.expected_index += 1
|
|
289
312
|
|
|
290
313
|
else:
|
|
291
|
-
buffer[index] = segment_content
|
|
314
|
+
self.buffer[index] = segment_content
|
|
292
315
|
|
|
293
316
|
except queue.Empty:
|
|
294
317
|
self.current_timeout = min(MAX_TIMEOOUT, self.current_timeout * 1.1)
|
|
@@ -440,6 +463,9 @@ class M3U8_Segments:
|
|
|
440
463
|
if self.info_nFailed > 0:
|
|
441
464
|
self._display_error_summary()
|
|
442
465
|
|
|
466
|
+
self.buffer = {}
|
|
467
|
+
self.expected_index = 0
|
|
468
|
+
|
|
443
469
|
def _display_error_summary(self) -> None:
|
|
444
470
|
"""Generate final error report."""
|
|
445
471
|
console.print(f"\n[cyan]Retry Summary: "
|
|
@@ -87,7 +87,8 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
|
|
|
87
87
|
return None, False
|
|
88
88
|
|
|
89
89
|
if GET_ONLY_LINK:
|
|
90
|
-
|
|
90
|
+
console.print(f"URL: {url}[/bold red]")
|
|
91
|
+
return path, True
|
|
91
92
|
|
|
92
93
|
if not (url.lower().startswith('http://') or url.lower().startswith('https://')):
|
|
93
94
|
logging.error(f"Invalid URL: {url}")
|
|
@@ -180,7 +180,7 @@ def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: s
|
|
|
180
180
|
Each dictionary should contain the 'path' key with the path to the audio file.
|
|
181
181
|
- out_path (str): The path to save the output file.
|
|
182
182
|
"""
|
|
183
|
-
video_audio_same_duration = check_duration_v_a(video_path, audio_tracks[0].get('path'))
|
|
183
|
+
video_audio_same_duration, duration_diff = check_duration_v_a(video_path, audio_tracks[0].get('path'))
|
|
184
184
|
|
|
185
185
|
# Start command with locate ffmpeg
|
|
186
186
|
ffmpeg_cmd = [get_ffmpeg_path()]
|
|
@@ -242,7 +242,7 @@ def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: s
|
|
|
242
242
|
|
|
243
243
|
# Use shortest input path for video and audios
|
|
244
244
|
if not video_audio_same_duration:
|
|
245
|
-
|
|
245
|
+
console.log(f"[red]Use shortest input (Duration difference: {duration_diff:.2f} seconds)...")
|
|
246
246
|
ffmpeg_cmd.extend(['-shortest', '-strict', 'experimental'])
|
|
247
247
|
|
|
248
248
|
# Overwrite
|
|
@@ -57,7 +57,6 @@ def get_video_duration(file_path: str) -> float:
|
|
|
57
57
|
Returns:
|
|
58
58
|
(float): The duration of the video in seconds if successful, None if there's an error.
|
|
59
59
|
"""
|
|
60
|
-
|
|
61
60
|
try:
|
|
62
61
|
ffprobe_cmd = [get_ffprobe_path(), '-v', 'error', '-show_format', '-print_format', 'json', file_path]
|
|
63
62
|
logging.info(f"FFmpeg command: {ffprobe_cmd}")
|
|
@@ -95,7 +94,6 @@ def format_duration(seconds: float) -> Tuple[int, int, int]:
|
|
|
95
94
|
Returns:
|
|
96
95
|
list[int, int, int]: List containing hours, minutes, and seconds.
|
|
97
96
|
"""
|
|
98
|
-
|
|
99
97
|
hours, remainder = divmod(seconds, 3600)
|
|
100
98
|
minutes, seconds = divmod(remainder, 60)
|
|
101
99
|
|
|
@@ -157,11 +155,7 @@ def get_ffprobe_info(file_path):
|
|
|
157
155
|
'codec_names': codec_names
|
|
158
156
|
}
|
|
159
157
|
|
|
160
|
-
except
|
|
161
|
-
logging.error(f"ffprobe failed for file {file_path}: {e}")
|
|
162
|
-
return None
|
|
163
|
-
|
|
164
|
-
except json.JSONDecodeError as e:
|
|
158
|
+
except Exception as e:
|
|
165
159
|
logging.error(f"Failed to parse JSON output from ffprobe for file {file_path}: {e}")
|
|
166
160
|
return None
|
|
167
161
|
|
|
@@ -198,23 +192,25 @@ def need_to_force_to_ts(file_path):
|
|
|
198
192
|
return False
|
|
199
193
|
|
|
200
194
|
|
|
201
|
-
def check_duration_v_a(video_path, audio_path):
|
|
195
|
+
def check_duration_v_a(video_path, audio_path, tolerance=1.0):
|
|
202
196
|
"""
|
|
203
197
|
Check if the duration of the video and audio matches.
|
|
204
198
|
|
|
205
199
|
Parameters:
|
|
206
200
|
- video_path (str): Path to the video file.
|
|
207
201
|
- audio_path (str): Path to the audio file.
|
|
202
|
+
- tolerance (float): Allowed tolerance for the duration difference (in seconds).
|
|
208
203
|
|
|
209
204
|
Returns:
|
|
210
|
-
-
|
|
205
|
+
- tuple: (bool, float) -> True if the duration of the video and audio matches, False otherwise, along with the difference in duration.
|
|
211
206
|
"""
|
|
212
|
-
|
|
213
|
-
# Ottieni la durata del video
|
|
214
207
|
video_duration = get_video_duration(video_path)
|
|
215
|
-
|
|
216
|
-
# Ottieni la durata dell'audio
|
|
217
208
|
audio_duration = get_video_duration(audio_path)
|
|
218
209
|
|
|
219
|
-
|
|
220
|
-
|
|
210
|
+
duration_difference = abs(video_duration - audio_duration)
|
|
211
|
+
|
|
212
|
+
# Check if the duration difference is within the tolerance
|
|
213
|
+
if duration_difference <= tolerance:
|
|
214
|
+
return True, duration_difference
|
|
215
|
+
else:
|
|
216
|
+
return False, duration_difference
|
|
@@ -52,7 +52,7 @@ class M3U8_Ts_Estimator:
|
|
|
52
52
|
self.now_downloaded_size += size_download
|
|
53
53
|
logging.debug(f"Current total downloaded size: {self.now_downloaded_size}")
|
|
54
54
|
|
|
55
|
-
def capture_speed(self, interval: float = 1):
|
|
55
|
+
def capture_speed(self, interval: float = 1.5):
|
|
56
56
|
"""Capture the internet speed periodically."""
|
|
57
57
|
last_upload, last_download = 0, 0
|
|
58
58
|
speed_buffer = deque(maxlen=3)
|
|
@@ -73,7 +73,7 @@ def get_select_title(table_show_manager, generic_obj):
|
|
|
73
73
|
|
|
74
74
|
# Handle user's quit command
|
|
75
75
|
if last_command == "q" or last_command == "quit":
|
|
76
|
-
console.print("\n[red]Quit
|
|
76
|
+
console.print("\n[red]Quit ...")
|
|
77
77
|
sys.exit(0)
|
|
78
78
|
|
|
79
79
|
# Check if the selected index is within range
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: StreamingCommunity
|
|
3
|
-
Version: 2.9.
|
|
3
|
+
Version: 2.9.3
|
|
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
|
|
@@ -72,6 +72,7 @@ Requires-Dist: pyTelegramBotAPI
|
|
|
72
72
|
- 🔍 [Parser](#m3u8_parser-settings)
|
|
73
73
|
- 📝 [Command](#command)
|
|
74
74
|
- 💻 [Examples of terminal](#examples-of-terminal-usage)
|
|
75
|
+
- 🔧 [Manual domain configuration](#update-domains)
|
|
75
76
|
- 🐳 [Docker](#docker)
|
|
76
77
|
- 📝 [Telegram Usage](#telegram-usage)
|
|
77
78
|
- 🎓 [Tutorial](#tutorials)
|
|
@@ -1,64 +1,64 @@
|
|
|
1
1
|
StreamingCommunity/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
StreamingCommunity/run.py,sha256=AbEL0cyAaRgaG5qE1c7Z6SVZ4Wu7WIH9pZmwC4FDWW8,12076
|
|
3
3
|
StreamingCommunity/Api/Player/ddl.py,sha256=M_ePETCMBpIHr5K5Yb7EML5VXwqkR7vJHQcGIv4AEQw,2261
|
|
4
|
-
StreamingCommunity/Api/Player/maxstream.py,sha256=
|
|
5
|
-
StreamingCommunity/Api/Player/supervideo.py,sha256=
|
|
4
|
+
StreamingCommunity/Api/Player/maxstream.py,sha256=abUQeE4EOUV0uuVOd22_C8IdeKrAAoO5USdgdm6EzfM,5092
|
|
5
|
+
StreamingCommunity/Api/Player/supervideo.py,sha256=hr9QViI-XD0Dqhcx90oaH8_j0d6cxpVaf-EuCjMs6hI,5199
|
|
6
6
|
StreamingCommunity/Api/Player/vixcloud.py,sha256=NOZhW59hyBnG5FfmppcnIudAztr7seWzQBzAN3KC_3M,6317
|
|
7
7
|
StreamingCommunity/Api/Player/Helper/Vixcloud/js_parser.py,sha256=U-8QlD5kGzIk3-4t4D6QyYmiDe8UBrSuVi1YHRQb7AU,4295
|
|
8
|
-
StreamingCommunity/Api/Player/Helper/Vixcloud/util.py,sha256=
|
|
9
|
-
StreamingCommunity/Api/Site/1337xx/__init__.py,sha256=
|
|
8
|
+
StreamingCommunity/Api/Player/Helper/Vixcloud/util.py,sha256=dnyVxZTjjV3C6PUCu2OMeHI4NbiioBWhixlY2Ol9I74,5206
|
|
9
|
+
StreamingCommunity/Api/Site/1337xx/__init__.py,sha256=pyaQ3QlLdiqLcyNyfE6jKhOR5BXUiKLfPcIBHbk8U8U,1378
|
|
10
10
|
StreamingCommunity/Api/Site/1337xx/site.py,sha256=u6a453wVy3vJrGcLlSUhErKzr63lv-o9cx36RjFpPRs,2647
|
|
11
11
|
StreamingCommunity/Api/Site/1337xx/title.py,sha256=lGb-IbWEIfg9Eu3XIu6IfxTOjvXkFL_NO9UEZcxOAfE,1831
|
|
12
12
|
StreamingCommunity/Api/Site/animeunity/__init__.py,sha256=Lz9trBzQZL11wGyIT2Y4CWe3JxOkDUPzTQXXO76s0oQ,2278
|
|
13
|
-
StreamingCommunity/Api/Site/animeunity/film_serie.py,sha256=
|
|
14
|
-
StreamingCommunity/Api/Site/animeunity/site.py,sha256=
|
|
13
|
+
StreamingCommunity/Api/Site/animeunity/film_serie.py,sha256=I44DJ26evVpJPveoWYZvlswB6f9gEG0Br_IrogleAk0,5771
|
|
14
|
+
StreamingCommunity/Api/Site/animeunity/site.py,sha256=SRtei5Ftx9Vb9z5F4C-nbdrVV8s--VwtfE6l8Y7_8Ko,5401
|
|
15
15
|
StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py,sha256=6Vbw5KVwUbgooGjUIRAuXr9cWSkHDkAFP7EiXF2T4OM,2709
|
|
16
|
-
StreamingCommunity/Api/Site/cb01new/__init__.py,sha256=
|
|
16
|
+
StreamingCommunity/Api/Site/cb01new/__init__.py,sha256=jw-eyJunemd3uNwpow75_8s7TX8bYyiRA-zkF5NZ75w,1393
|
|
17
17
|
StreamingCommunity/Api/Site/cb01new/film.py,sha256=trrEGcklB6FhqpJvGaEwHI0EThK__e9O6DuknKAFNHw,1628
|
|
18
18
|
StreamingCommunity/Api/Site/cb01new/site.py,sha256=q9bHKgHkXWE0vOcfQ9bKU0QDzbLassfSN2im2O4EYiI,2523
|
|
19
|
-
StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py,sha256=
|
|
19
|
+
StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py,sha256=XkpTFeb-yFI_bJCIph72cVE8GVoJP7Tby-NqkZNNDM4,1533
|
|
20
20
|
StreamingCommunity/Api/Site/ddlstreamitaly/series.py,sha256=z3te51do5C_O77rDTR1N01aQ76BIGe5pm5i_PWJepQ4,3369
|
|
21
21
|
StreamingCommunity/Api/Site/ddlstreamitaly/site.py,sha256=_I4fZuzE5DnwAGOBOJK_IYW04RETwl3N2J0FUJAjQqg,2913
|
|
22
22
|
StreamingCommunity/Api/Site/ddlstreamitaly/util/ScrapeSerie.py,sha256=HY8YEvzWp3sy1q07rFLXLZhGYvapA1amMZByYvs0iJM,2553
|
|
23
|
-
StreamingCommunity/Api/Site/guardaserie/__init__.py,sha256=
|
|
24
|
-
StreamingCommunity/Api/Site/guardaserie/series.py,sha256=
|
|
23
|
+
StreamingCommunity/Api/Site/guardaserie/__init__.py,sha256=NjMn1EFWdFi9P89qpKNg3Dc84DDOSyuSBX0V5K24OjQ,1385
|
|
24
|
+
StreamingCommunity/Api/Site/guardaserie/series.py,sha256=xXuMR9NiAtXkHrErsmXRY9IkE2FVGuhqjATYEfapb0Y,5670
|
|
25
25
|
StreamingCommunity/Api/Site/guardaserie/site.py,sha256=5Y5svAT4QYZrNDPM9couf6M94g0g320NfoAuJt4dp94,2538
|
|
26
26
|
StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py,sha256=4sZRWm8r5X80q285hemRf7MAWeaN5yfOU6i1SjKU4Tg,3268
|
|
27
|
-
StreamingCommunity/Api/Site/mostraguarda/__init__.py,sha256=
|
|
27
|
+
StreamingCommunity/Api/Site/mostraguarda/__init__.py,sha256=6oGv_Q6pXFERkbpKjYjFJFblvDYSfe1zZph6_Ch8gNY,1234
|
|
28
28
|
StreamingCommunity/Api/Site/mostraguarda/film.py,sha256=dA7Vo9bU7g8eY8Vaj06_n2MHlKBMHh4B_MIw2sO872A,2719
|
|
29
29
|
StreamingCommunity/Api/Site/streamingcommunity/__init__.py,sha256=FV3ch-farw3tN_-Ay3JV_-TIoHzgQzxEJWPlFibE62Y,2351
|
|
30
30
|
StreamingCommunity/Api/Site/streamingcommunity/film.py,sha256=LaZzEQms9t7r30_PjHPgIOUkVDyotX0qFDBMKMVNSWo,2530
|
|
31
|
-
StreamingCommunity/Api/Site/streamingcommunity/series.py,sha256=
|
|
31
|
+
StreamingCommunity/Api/Site/streamingcommunity/series.py,sha256=LmvcGTr7GR4_n2G0QYnLUKFiA30OxQD_H9Y3pF4tamo,7926
|
|
32
32
|
StreamingCommunity/Api/Site/streamingcommunity/site.py,sha256=OID6QB_WMydwVCYD0SupHpEP0VwHIBmwifwuIHW9HaE,2973
|
|
33
33
|
StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py,sha256=YYetljW4yr6eoiLe6nDUuFZ9NI8K85tF6859ikITmsY,3571
|
|
34
34
|
StreamingCommunity/Api/Template/__init__.py,sha256=oyfd_4_g5p5q6mxb_rKwSsudZnTM3W3kg1tLwxg-v-Q,46
|
|
35
35
|
StreamingCommunity/Api/Template/config_loader.py,sha256=seJjLi05_8xrs4tdt4zXNCPa9TLp7n5FufFM2XErokI,2166
|
|
36
|
-
StreamingCommunity/Api/Template/site.py,sha256=
|
|
36
|
+
StreamingCommunity/Api/Template/site.py,sha256=lD7FCp-xHCTwTam51MYtI-JKRuw1VLsY9KQDtsk3MYE,2842
|
|
37
37
|
StreamingCommunity/Api/Template/Class/SearchType.py,sha256=FtO8xDUGEeJgMWsK2Ab7ZzAFsncalTYL2oEYi8uCnuk,2531
|
|
38
38
|
StreamingCommunity/Api/Template/Util/__init__.py,sha256=GZZgT816VwTYekPOwLExHenpak0gO-V3LLGTYIElt3A,234
|
|
39
39
|
StreamingCommunity/Api/Template/Util/get_domain.py,sha256=tWQKWs5LJZna4a2P0IfxlSZDbZTDjiqq9SmHTbK-PyU,2763
|
|
40
|
-
StreamingCommunity/Api/Template/Util/manage_ep.py,sha256=
|
|
40
|
+
StreamingCommunity/Api/Template/Util/manage_ep.py,sha256=aPRgSB6OiUNfYQKMTEDrjxeTFfOUfx3JypvA6nehwP4,9251
|
|
41
41
|
StreamingCommunity/Lib/Downloader/__init__.py,sha256=JhbBh5hOnSM7VmtkxJ7zZ_FtWEC1JdnKThsSBjLV5FY,140
|
|
42
|
-
StreamingCommunity/Lib/Downloader/HLS/downloader.py,sha256=
|
|
43
|
-
StreamingCommunity/Lib/Downloader/HLS/segments.py,sha256=
|
|
44
|
-
StreamingCommunity/Lib/Downloader/MP4/downloader.py,sha256=
|
|
42
|
+
StreamingCommunity/Lib/Downloader/HLS/downloader.py,sha256=2MzuMtXgRb6TcurQi0z6fJcXi0mzrbecWOk-L46bPgw,21585
|
|
43
|
+
StreamingCommunity/Lib/Downloader/HLS/segments.py,sha256=eVQzB06EochPM1E0VNWgfvEyc5wGDq_yG6Fqo7WPNic,18468
|
|
44
|
+
StreamingCommunity/Lib/Downloader/MP4/downloader.py,sha256=4rgaVTlBvNCfS2dWbTC_eCiXL8MbC1mwRGlUtvJv4nw,7466
|
|
45
45
|
StreamingCommunity/Lib/Downloader/TOR/downloader.py,sha256=KVZxCl1VB1-OuTjUhVS5Ycog_P0vCGTfRzhZPv8O7Ps,11267
|
|
46
46
|
StreamingCommunity/Lib/FFmpeg/__init__.py,sha256=6PBsZdE1jrD2EKOVyx3JEHnyDZzVeKlPkH5T0zyfOgU,130
|
|
47
47
|
StreamingCommunity/Lib/FFmpeg/capture.py,sha256=73BEpTijksErZOu46iRxwl3idKzZ-sVXXRr4nocIGY0,5168
|
|
48
|
-
StreamingCommunity/Lib/FFmpeg/command.py,sha256=
|
|
49
|
-
StreamingCommunity/Lib/FFmpeg/util.py,sha256=
|
|
48
|
+
StreamingCommunity/Lib/FFmpeg/command.py,sha256=ubpffE02nsZM7xch5gbwGlEiUzecpxcFOd63n2cqM1k,10708
|
|
49
|
+
StreamingCommunity/Lib/FFmpeg/util.py,sha256=6QzTbk5BNxKYsTl-cJgOO2XGQTWUdMloWMyrGGBs6eU,7168
|
|
50
50
|
StreamingCommunity/Lib/M3U8/__init__.py,sha256=H_KS2eDd3kVXMziFJnD0FCPvPHEizaqfoA36ElTv_r8,170
|
|
51
51
|
StreamingCommunity/Lib/M3U8/decryptor.py,sha256=kuxxsd3eN0VGRrMJWXzHo8gCpT0u3fSZs_lwxlE5Fqs,2948
|
|
52
|
-
StreamingCommunity/Lib/M3U8/estimator.py,sha256=
|
|
52
|
+
StreamingCommunity/Lib/M3U8/estimator.py,sha256=U2xnUvoAeLZT8KA1ZDZO9TRz7sXgzIyCtHClT2wcI28,5711
|
|
53
53
|
StreamingCommunity/Lib/M3U8/parser.py,sha256=xN16pQZSCN9mQl_s7OcuH07-mNgVMpAS_hERq6zp7XM,21558
|
|
54
54
|
StreamingCommunity/Lib/M3U8/url_fixer.py,sha256=zldE4yOuNBV6AAvL1KI6p7XdRI_R5YZRscbDgT1564M,1735
|
|
55
55
|
StreamingCommunity/Lib/TMBD/__init__.py,sha256=XzE42tw3Ws59DD1PF8WmGtZ0D4D7Hk3Af8QthNE-22U,66
|
|
56
56
|
StreamingCommunity/Lib/TMBD/obj_tmbd.py,sha256=dRSvJFS5yqmsBZcw2wqbStcBtXNjU_3n5czMyremAtU,1187
|
|
57
|
-
StreamingCommunity/Lib/TMBD/tmdb.py,sha256=
|
|
57
|
+
StreamingCommunity/Lib/TMBD/tmdb.py,sha256=byg0EFnlmd9JeLvn1N9K3QkB1KEfeMuFa7OVfGqks1Y,10685
|
|
58
58
|
StreamingCommunity/TelegramHelp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
59
|
StreamingCommunity/TelegramHelp/telegram_bot.py,sha256=Qe1__aoK4PpDuing8JtWgdHzLee8LuYYyfeLNA7yADU,26307
|
|
60
60
|
StreamingCommunity/Upload/update.py,sha256=TXWAOfvZr1So_oME11YvX_L5zRy2tM-ijF-_g1jf87o,2548
|
|
61
|
-
StreamingCommunity/Upload/version.py,sha256=
|
|
61
|
+
StreamingCommunity/Upload/version.py,sha256=bREs7N-kz1d-zZ5B1i3S2s-N5hBkN0o0LPTL9AuAqbY,171
|
|
62
62
|
StreamingCommunity/Util/color.py,sha256=NvD0Eni-25oOOkY-szCEoc0lGvzQxyL7xhM0RE4EvUM,458
|
|
63
63
|
StreamingCommunity/Util/config_json.py,sha256=dmo7FTboEuEs1nYf17RYaTyT03qRvOLJzGgBitYHXs4,19379
|
|
64
64
|
StreamingCommunity/Util/ffmpeg_installer.py,sha256=q5yb_ZXKe9PhcG7JbKLfo1AZa8DNukgHqymPbudDuAY,13585
|
|
@@ -67,9 +67,9 @@ StreamingCommunity/Util/logger.py,sha256=9kGD6GmWj2pM8ADpJc85o7jm8DD0c5Aguqnq-9k
|
|
|
67
67
|
StreamingCommunity/Util/message.py,sha256=SJaIPLvWeQqsIODVUKw3TgYRmBChovmlbcF6OUxqMI8,1425
|
|
68
68
|
StreamingCommunity/Util/os.py,sha256=MUGJKQbNMWeoUrnJ2Ug3hoyYlrPDqU9BY94UmiUbxfA,14858
|
|
69
69
|
StreamingCommunity/Util/table.py,sha256=X1t9VPYl9GemLMk_-x_WfpysQ-3Iv8vh0aTIJKm0fK0,8565
|
|
70
|
-
StreamingCommunity-2.9.
|
|
71
|
-
StreamingCommunity-2.9.
|
|
72
|
-
StreamingCommunity-2.9.
|
|
73
|
-
StreamingCommunity-2.9.
|
|
74
|
-
StreamingCommunity-2.9.
|
|
75
|
-
StreamingCommunity-2.9.
|
|
70
|
+
StreamingCommunity-2.9.3.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
71
|
+
StreamingCommunity-2.9.3.dist-info/METADATA,sha256=UPtxd9uEBSd8bvBBTexZ5kaeROrDyX_WGxBT6dcRYhc,21552
|
|
72
|
+
StreamingCommunity-2.9.3.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
73
|
+
StreamingCommunity-2.9.3.dist-info/entry_points.txt,sha256=Qph9XYfDC8n4LfDLOSl6gJGlkb9eFb5f-JOr_Wb_5rk,67
|
|
74
|
+
StreamingCommunity-2.9.3.dist-info/top_level.txt,sha256=YsOcxKP-WOhWpIWgBlh0coll9XUx7aqmRPT7kmt3fH0,19
|
|
75
|
+
StreamingCommunity-2.9.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|