StreamingCommunity 2.0.0__py3-none-any.whl → 2.0.5__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.

Files changed (33) hide show
  1. StreamingCommunity/Api/Site/1337xx/__init__.py +1 -1
  2. StreamingCommunity/Api/Site/altadefinizione/__init__.py +1 -1
  3. StreamingCommunity/Api/Site/animeunity/__init__.py +1 -1
  4. StreamingCommunity/Api/Site/cb01new/__init__.py +1 -1
  5. StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +1 -3
  6. StreamingCommunity/Api/Site/ddlstreamitaly/series.py +1 -1
  7. StreamingCommunity/Api/Site/guardaserie/__init__.py +1 -3
  8. StreamingCommunity/Api/Site/guardaserie/series.py +2 -2
  9. StreamingCommunity/Api/Site/ilcorsaronero/__init__.py +1 -1
  10. StreamingCommunity/Api/Site/ilcorsaronero/util/ilCorsarScraper.py +14 -6
  11. StreamingCommunity/Api/Site/mostraguarda/__init__.py +1 -1
  12. StreamingCommunity/Api/Site/mostraguarda/film.py +1 -3
  13. StreamingCommunity/Api/Site/streamingcommunity/__init__.py +1 -1
  14. StreamingCommunity/Api/Site/streamingcommunity/film.py +1 -1
  15. StreamingCommunity/Api/Site/streamingcommunity/series.py +1 -1
  16. StreamingCommunity/Api/Template/Util/get_domain.py +12 -11
  17. StreamingCommunity/Lib/Downloader/HLS/downloader.py +42 -34
  18. StreamingCommunity/Lib/Downloader/HLS/segments.py +10 -11
  19. StreamingCommunity/Lib/FFmpeg/capture.py +1 -1
  20. StreamingCommunity/Lib/FFmpeg/util.py +1 -1
  21. StreamingCommunity/Lib/M3U8/decryptor.py +2 -2
  22. StreamingCommunity/Lib/M3U8/estimator.py +53 -25
  23. StreamingCommunity/Upload/update.py +2 -3
  24. StreamingCommunity/Upload/version.py +1 -1
  25. StreamingCommunity/Util/ffmpeg_installer.py +65 -28
  26. StreamingCommunity/Util/os.py +16 -25
  27. StreamingCommunity/run.py +0 -1
  28. {StreamingCommunity-2.0.0.dist-info → StreamingCommunity-2.0.5.dist-info}/METADATA +9 -9
  29. {StreamingCommunity-2.0.0.dist-info → StreamingCommunity-2.0.5.dist-info}/RECORD +33 -33
  30. {StreamingCommunity-2.0.0.dist-info → StreamingCommunity-2.0.5.dist-info}/WHEEL +1 -1
  31. {StreamingCommunity-2.0.0.dist-info → StreamingCommunity-2.0.5.dist-info}/entry_points.txt +1 -0
  32. {StreamingCommunity-2.0.0.dist-info → StreamingCommunity-2.0.5.dist-info}/LICENSE +0 -0
  33. {StreamingCommunity-2.0.0.dist-info → StreamingCommunity-2.0.5.dist-info}/top_level.txt +0 -0
@@ -27,7 +27,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False):
27
27
  """
28
28
 
29
29
  if string_to_search is None:
30
- string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip()
30
+ string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{SITE_NAME}").strip()
31
31
 
32
32
  # Search on database
33
33
  len_database = title_search(quote_plus(string_to_search))
@@ -27,7 +27,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False):
27
27
  """
28
28
 
29
29
  if string_to_search is None:
30
- string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip()
30
+ string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{SITE_NAME}").strip()
31
31
 
32
32
  # Search on database
33
33
  len_database = title_search(quote_plus(string_to_search))
@@ -24,7 +24,7 @@ from .costant import SITE_NAME
24
24
  def search(string_to_search: str = None, get_onylDatabase: bool = False):
25
25
 
26
26
  if string_to_search is None:
27
- string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip()
27
+ string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{SITE_NAME}").strip()
28
28
 
29
29
  # Search on database
30
30
  len_database = title_search(quote_plus(string_to_search))
@@ -27,7 +27,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False):
27
27
  """
28
28
 
29
29
  if string_to_search is None:
30
- string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip()
30
+ string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{SITE_NAME}").strip()
31
31
 
32
32
  # Search on database
33
33
  len_database = title_search(quote_plus(string_to_search))
@@ -28,9 +28,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False):
28
28
  """
29
29
 
30
30
  if string_to_search is None:
31
-
32
- # Make request to site to get content that corrsisponde to that string
33
- string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip()
31
+ string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{SITE_NAME}").strip()
34
32
 
35
33
  # Search on database
36
34
  len_database = title_search(quote_plus(string_to_search))
@@ -25,7 +25,6 @@ from StreamingCommunity.Api.Player.ddl import VideoSource
25
25
 
26
26
  # Variable
27
27
  from .costant import ROOT_PATH, SERIES_FOLDER
28
- table_show_manager = TVShowManager()
29
28
 
30
29
 
31
30
 
@@ -120,6 +119,7 @@ def display_episodes_list(obj_episode_manager) -> str:
120
119
  """
121
120
 
122
121
  # Set up table for displaying episodes
122
+ table_show_manager = TVShowManager()
123
123
  table_show_manager.set_slice_end(10)
124
124
 
125
125
  # Add columns to the table
@@ -27,9 +27,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False):
27
27
  """
28
28
 
29
29
  if string_to_search is None:
30
-
31
- # Make request to site to get content that corrsisponde to that string
32
- string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip()
30
+ string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{SITE_NAME}").strip()
33
31
 
34
32
  # Search on database
35
33
  len_database = title_search(quote_plus(string_to_search))
@@ -25,7 +25,6 @@ from StreamingCommunity.Api.Player.supervideo import VideoSource
25
25
 
26
26
  # Variable
27
27
  from .costant import ROOT_PATH, SERIES_FOLDER
28
- table_show_manager = TVShowManager()
29
28
 
30
29
 
31
30
 
@@ -171,8 +170,9 @@ def display_episodes_list(obj_episode_manager) -> str:
171
170
  Returns:
172
171
  last_command (str): Last command entered by the user.
173
172
  """
174
-
173
+
175
174
  # Set up table for displaying episodes
175
+ table_show_manager = TVShowManager()
176
176
  table_show_manager.set_slice_end(10)
177
177
 
178
178
  # Add columns to the table
@@ -28,7 +28,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False):
28
28
  """
29
29
 
30
30
  if string_to_search is None:
31
- string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip()
31
+ string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{SITE_NAME}").strip()
32
32
 
33
33
  # Search on database
34
34
  len_database = asyncio.run(title_search(quote_plus(string_to_search)))
@@ -25,13 +25,21 @@ class IlCorsaroNeroScraper:
25
25
  self.base_url = base_url
26
26
  self.max_page = max_page
27
27
  self.headers = {
28
- 'User-Agent': get_headers(),
29
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
30
- 'Accept-Language': 'en-US,en;q=0.5',
31
- 'Connection': 'keep-alive',
32
- 'Upgrade-Insecure-Requests': '1'
28
+ 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
29
+ 'accept-language': 'it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7',
30
+ 'cache-control': 'max-age=0',
31
+ 'priority': 'u=0, i',
32
+ 'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
33
+ 'sec-ch-ua-mobile': '?0',
34
+ 'sec-ch-ua-platform': '"Windows"',
35
+ 'sec-fetch-dest': 'document',
36
+ 'sec-fetch-mode': 'navigate',
37
+ 'sec-fetch-site': 'same-origin',
38
+ 'sec-fetch-user': '?1',
39
+ 'upgrade-insecure-requests': '1',
40
+ 'user-agent': get_headers()
33
41
  }
34
-
42
+
35
43
  async def fetch_url(self, url: str) -> Optional[str]:
36
44
  """
37
45
  Fetch the HTML content of a given URL.
@@ -27,7 +27,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False):
27
27
  """
28
28
 
29
29
  if string_to_search is None:
30
- string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip()
30
+ string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{SITE_NAME}").strip()
31
31
 
32
32
  # Not available for the moment
33
33
  if get_onylDatabase:
@@ -70,10 +70,8 @@ def download_film(movie_details: Json_film) -> str:
70
70
  player_links = soup.find("ul", class_ = "_player-mirrors").find_all("li")
71
71
  supervideo_url = "https:" + player_links[0].get("data-link")
72
72
 
73
-
74
73
  # Set domain and media ID for the video source
75
- video_source = VideoSource()
76
- video_source.setup(supervideo_url)
74
+ video_source = VideoSource(url=supervideo_url)
77
75
 
78
76
  # Define output path
79
77
  title_name = os_manager.get_sanitize_file(movie_details.title) + ".mp4"
@@ -28,7 +28,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False):
28
28
  """
29
29
 
30
30
  if string_to_search is None:
31
- string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip()
31
+ string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{SITE_NAME}").strip()
32
32
 
33
33
  # Get site domain and version and get result of the search
34
34
  site_version, domain = get_version_and_domain()
@@ -39,7 +39,7 @@ def download_film(select_title: MediaItem) -> str:
39
39
 
40
40
  # Start message and display film information
41
41
  start_message()
42
- console.print(f"[yellow]Download: [red]{select_title.slug} \n")
42
+ console.print(f"[yellow]Download: [red]{select_title.name} \n")
43
43
 
44
44
  # Init class
45
45
  video_source = VideoSource(SITE_NAME, False)
@@ -25,7 +25,6 @@ from StreamingCommunity.Api.Player.vixcloud import VideoSource
25
25
 
26
26
  # Variable
27
27
  from .costant import ROOT_PATH, SITE_NAME, SERIES_FOLDER
28
- table_show_manager = TVShowManager()
29
28
 
30
29
 
31
30
 
@@ -178,6 +177,7 @@ def display_episodes_list(scrape_serie) -> str:
178
177
  """
179
178
 
180
179
  # Set up table for displaying episodes
180
+ table_show_manager = TVShowManager()
181
181
  table_show_manager.set_slice_end(10)
182
182
 
183
183
  # Add columns to the table
@@ -76,17 +76,19 @@ def get_final_redirect_url(initial_url, max_timeout):
76
76
  console.print(f"\n[cyan]Test url[white]: [red]{initial_url}, [cyan]error[white]: [red]{e}")
77
77
  return None
78
78
 
79
- def search_domain(site_name: str, base_url: str):
79
+ def search_domain(site_name: str, base_url: str, get_first: bool = False):
80
80
  """
81
81
  Search for a valid domain for the given site name and base URL.
82
82
 
83
83
  Parameters:
84
84
  - site_name (str): The name of the site to search the domain for.
85
85
  - base_url (str): The base URL to construct complete URLs.
86
+ - get_first (bool): If True, automatically update to the first valid match without user confirmation.
86
87
 
87
88
  Returns:
88
89
  tuple: The found domain and the complete URL.
89
90
  """
91
+
90
92
  # Extract config domain
91
93
  max_timeout = config_manager.get_int("REQUESTS", "timeout")
92
94
  domain = str(config_manager.get_dict("SITE", site_name)['domain'])
@@ -107,10 +109,10 @@ def search_domain(site_name: str, base_url: str):
107
109
 
108
110
  except Exception as e:
109
111
  query = base_url.split("/")[-1]
110
-
112
+
111
113
  # Perform a Google search with multiple results
112
114
  search_results = list(search(query, num_results=10, lang="it"))
113
- console.print(f"\nGoogle search results: {search_results}")
115
+ #console.print(f"\nGoogle search results: {search_results}")
114
116
 
115
117
  def normalize_for_comparison(url):
116
118
  """Normalize URL by removing protocol, www, and trailing slashes"""
@@ -121,15 +123,15 @@ def search_domain(site_name: str, base_url: str):
121
123
 
122
124
  # Normalize the base_url we're looking for
123
125
  target_url = normalize_for_comparison(base_url)
124
-
126
+
125
127
  # Iterate through search results
126
128
  for first_url in search_results:
127
129
  console.print(f"[green]Checking url[white]: [red]{first_url}")
128
-
130
+
129
131
  # Get just the domain part of the search result
130
132
  parsed_result = urlparse(first_url)
131
133
  result_domain = normalize_for_comparison(parsed_result.netloc)
132
-
134
+
133
135
  # Compare with our target URL (without the protocol part)
134
136
  if result_domain.startswith(target_url.split("/")[-1]):
135
137
  try:
@@ -143,21 +145,20 @@ def search_domain(site_name: str, base_url: str):
143
145
 
144
146
  new_domain_extract = extract_domain(str(final_url))
145
147
 
146
- if msg.ask(f"\n[cyan]Do you want to auto update site[white] [red]'{site_name}'[cyan] with domain[white] [red]'{new_domain_extract}'.", choices=["y", "n"], default="y").lower() == "y":
147
-
148
+ if get_first or msg.ask(f"\n[cyan]Do you want to auto update site[white] [red]'{site_name}'[cyan] with domain[white] [red]'{new_domain_extract}'.", choices=["y", "n"], default="y").lower() == "y":
148
149
  # Update domain in config.json
149
150
  config_manager.config['SITE'][site_name]['domain'] = new_domain_extract
150
151
  config_manager.write_config()
151
152
 
152
153
  return new_domain_extract, f"{base_url}.{new_domain_extract}"
153
-
154
+
154
155
  except Exception as redirect_error:
155
156
  console.print(f"[red]Error following redirect for {first_url}: {redirect_error}")
156
157
  continue
157
158
 
158
- # If no matching URL is found
159
+ # If no matching URL is found return base domain
159
160
  console.print("[bold red]No valid URL found matching the base URL.[/bold red]")
160
- raise Exception("No matching domain found")
161
+ return domain, f"{base_url}.{domain}"
161
162
 
162
163
  # Handle successful initial domain check
163
164
  parsed_url = urlparse(str(response_follow.url))
@@ -176,9 +176,11 @@ class ContentExtractor:
176
176
  set_language = DOWNLOAD_SPECIFIC_AUDIO
177
177
  downloadable_languages = list(set(available_languages) & set(set_language))
178
178
 
179
- console.print(f"[cyan bold]Audio:[/cyan bold] [green]Available:[/green] [purple]{', '.join(available_languages)}[/purple] | "
180
- f"[red]Set:[/red] [purple]{', '.join(set_language)}[/purple] | "
181
- f"[yellow]Downloadable:[/yellow] [purple]{', '.join(downloadable_languages)}[/purple]")
179
+ # Only show if there is something available
180
+ if len(available_languages) > 0:
181
+ console.print(f"[cyan bold]Audio →[/cyan bold] [green]Available:[/green] [purple]{', '.join(available_languages)}[/purple] | "
182
+ f"[red]Set:[/red] [purple]{', '.join(set_language)}[/purple] | "
183
+ f"[yellow]Downloadable:[/yellow] [purple]{', '.join(downloadable_languages)}[/purple]")
182
184
 
183
185
  else:
184
186
  console.log("[red]Can't find a list of audios")
@@ -200,9 +202,11 @@ class ContentExtractor:
200
202
  set_language = DOWNLOAD_SPECIFIC_SUBTITLE
201
203
  downloadable_languages = list(set(available_languages) & set(set_language))
202
204
 
203
- console.print(f"[cyan bold]Subtitle:[/cyan bold] [green]Available:[/green] [purple]{', '.join(available_languages)}[/purple] | "
204
- f"[red]Set:[/red] [purple]{', '.join(set_language)}[/purple] | "
205
- f"[yellow]Downloadable:[/yellow] [purple]{', '.join(downloadable_languages)}[/purple]")
205
+ # Only show if there is something available
206
+ if len(available_languages) > 0:
207
+ console.print(f"[cyan bold]Subtitle →[/cyan bold] [green]Available:[/green] [purple]{', '.join(available_languages)}[/purple] | "
208
+ f"[red]Set:[/red] [purple]{', '.join(set_language)}[/purple] | "
209
+ f"[yellow]Downloadable:[/yellow] [purple]{', '.join(downloadable_languages)}[/purple]")
206
210
 
207
211
  else:
208
212
  console.log("[red]Can't find a list of subtitles")
@@ -212,13 +216,18 @@ class ContentExtractor:
212
216
  It identifies the best video quality and displays relevant information to the user.
213
217
  """
214
218
  logging.info(f"class 'ContentExtractor'; call _collect_video()")
219
+ set_resolution = "Best"
215
220
 
216
221
  # Collect custom quality video if a specific resolution is set
217
222
  if FILTER_CUSTOM_REOLUTION != -1:
218
223
  self.m3u8_index, video_res = self.obj_parse._video.get_custom_uri(y_resolution=FILTER_CUSTOM_REOLUTION)
224
+ set_resolution = f"{FILTER_CUSTOM_REOLUTION}p"
219
225
 
220
- # Otherwise, get the best available video quality
221
- self.m3u8_index, video_res = self.obj_parse._video.get_best_uri()
226
+ else:
227
+
228
+ # Otherwise, get the best available video quality
229
+ self.m3u8_index, video_res = self.obj_parse._video.get_best_uri()
230
+
222
231
  self.codec: M3U8_Codec = self.obj_parse.codec
223
232
 
224
233
  # List all available resolutions
@@ -227,18 +236,34 @@ class ContentExtractor:
227
236
  logging.info(f"M3U8 index selected: {self.m3u8_index}, with resolution: {video_res}")
228
237
 
229
238
  # Create a formatted table to display video info
230
- console.print(f"[cyan bold]Video:[/cyan bold] [green]Available resolutions:[/green] [purple]{', '.join(list_available_resolution)}[/purple] | "
231
- f"[yellow]Downloadable:[/yellow] [purple]{video_res[0]}x{video_res[1]}[/purple]")
239
+ console.print(f"[cyan bold]Video[/cyan bold] [green]Available:[/green] [purple]{', '.join(list_available_resolution)}[/purple] | "
240
+ f"[red]Set:[/red] [purple]{set_resolution}[/purple] | "
241
+ f"[yellow]Downloadable:[/yellow] [purple]{video_res[0]}x{video_res[1]}[/purple]")
232
242
 
233
243
  if self.codec is not None:
244
+
245
+ # Generate the string for available codec information
246
+ available_codec_info = (
247
+ f"[green]v[/green]: [yellow]{self.codec.video_codec_name}[/yellow] "
248
+ f"([green]b[/green]: [yellow]{self.codec.video_bitrate // 1000}k[/yellow]), "
249
+ f"[green]a[/green]: [yellow]{self.codec.audio_codec_name}[/yellow] "
250
+ f"([green]b[/green]: [yellow]{self.codec.audio_bitrate // 1000}k[/yellow])"
251
+ )
252
+
253
+ # Determine what to display for "Set"
254
+ # If the codec usage is enabled in the configuration, use the detailed codec info
255
+ # Otherwise, display "copy"
234
256
  if config_manager.get_bool("M3U8_CONVERSION", "use_codec"):
235
- codec_info = (f"[green]v[/green]: [yellow]{self.codec.video_codec_name}[/yellow] "
236
- f"([green]b[/green]: [yellow]{self.codec.video_bitrate // 1000}k[/yellow]), "
237
- f"[green]a[/green]: [yellow]{self.codec.audio_codec_name}[/yellow] "
238
- f"([green]b[/green]: [yellow]{self.codec.audio_bitrate // 1000}k[/yellow])")
257
+ set_codec_info = available_codec_info
239
258
  else:
240
- codec_info = "[cyan]copy[/cyan]"
241
- console.print(f"[bold green]Codec:[/bold green] {codec_info}")
259
+ set_codec_info = "[purple]copy[/purple]"
260
+
261
+ # Print the formatted result with "Available" and "Set" information
262
+ console.print(
263
+ f"[bold cyan]Codec →[/bold cyan] [green]Available:[/green] {available_codec_info} | "
264
+ f"[red]Set:[/red] {set_codec_info}"
265
+ )
266
+
242
267
 
243
268
  # Fix the URL if it does not include the full protocol
244
269
  if "http" not in self.m3u8_index:
@@ -486,19 +511,6 @@ class ContentJoiner:
486
511
  self.there_is_audio = len(downloaded_audio) > 0
487
512
  self.there_is_subtitle = len(downloaded_subtitle) > 0
488
513
 
489
- if self.there_is_audio or self.there_is_subtitle:
490
-
491
- # Display the status of available media
492
- table = Table(show_header=False, box=None)
493
-
494
- table.add_row(f"[green]Video - audio", f"[yellow]{self.there_is_audio}")
495
- table.add_row(f"[green]Video - Subtitle", f"[yellow]{self.there_is_subtitle}")
496
-
497
- print("")
498
- console.rule("[bold green] JOIN ", style="bold red")
499
- console.print(table)
500
- print("")
501
-
502
514
  # Start the joining process
503
515
  self.conversione()
504
516
 
@@ -584,10 +596,6 @@ class ContentJoiner:
584
596
  # Check if the joined video file already exists
585
597
  if not os.path.exists(path_join_video):
586
598
 
587
- # Set codec to None if not defined in class
588
- #if not hasattr(self, 'codec'):
589
- # self.codec = None
590
-
591
599
  # Join the video segments into a single video file
592
600
  join_video(
593
601
  video_path=self.downloaded_video[0].get('path'),
@@ -844,7 +852,7 @@ class HLS_Downloader:
844
852
  f"[bold green]Download completed![/bold green]\n"
845
853
  f"[cyan]File size: [bold red]{formatted_size}[/bold red]\n"
846
854
  f"[cyan]Duration: [bold]{formatted_duration}[/bold]\n"
847
- f"[cyan]Output: [bold]{self.output_filename}[/bold]"
855
+ f"[cyan]Output: [bold]{os.path.abspath(self.output_filename)}[/bold]"
848
856
  )
849
857
 
850
858
  if missing_ts:
@@ -432,7 +432,7 @@ class M3U8_Segments:
432
432
  if "audio" in str(type):
433
433
  TQDM_MAX_WORKER = AUDIO_WORKERS
434
434
 
435
- console.print(f"[cyan]Video workers[white]: [green]{VIDEO_WORKERS} [white]| [cyan]Audio workers[white]: [green]{AUDIO_WORKERS}")
435
+ #console.print(f"[cyan]Video workers[white]: [green]{VIDEO_WORKERS} [white]| [cyan]Audio workers[white]: [green]{AUDIO_WORKERS}")
436
436
 
437
437
  # Custom bar for mobile and pc
438
438
  if TQDM_USE_LARGE_BAR:
@@ -544,18 +544,17 @@ class M3U8_Segments:
544
544
  file_size = os.path.getsize(self.tmp_file_path)
545
545
  if file_size == 0:
546
546
  raise Exception("Output file is empty")
547
-
548
- # Get expected time
549
- ex_hours, ex_minutes, ex_seconds = format_duration(self.expected_real_time_s)
550
- ex_formatted_duration = f"[yellow]{int(ex_hours)}[red]h [yellow]{int(ex_minutes)}[red]m [yellow]{int(ex_seconds)}[red]s"
551
- console.print(f"[cyan]Max retry per URL[white]: [green]{self.info_maxRetry}[green] [white]| [cyan]Total retry done[white]: [green]{self.info_nRetry}[green] [white]| [cyan]Missing TS: [red]{self.info_nFailed} [white]| [cyan]Duration: {print_duration_table(self.tmp_file_path, None, True)} [white]| [cyan]Expected duation: {ex_formatted_duration} \n")
547
+
548
+ # Display additional info only if there is failed segments
549
+ if self.info_nFailed > 0:
550
+
551
+ # Get expected time
552
+ ex_hours, ex_minutes, ex_seconds = format_duration(self.expected_real_time_s)
553
+ ex_formatted_duration = f"[yellow]{int(ex_hours)}[red]h [yellow]{int(ex_minutes)}[red]m [yellow]{int(ex_seconds)}[red]s"
554
+ console.print(f"[cyan]Max retry per URL[white]: [green]{self.info_maxRetry}[green] [white]| [cyan]Total retry done[white]: [green]{self.info_nRetry}[green] [white]| [cyan]Missing TS: [red]{self.info_nFailed} [white]| [cyan]Duration: {print_duration_table(self.tmp_file_path, None, True)} [white]| [cyan]Expected duation: {ex_formatted_duration} \n")
552
555
 
553
556
  if self.info_nRetry >= len(self.segments) * (1/3.33):
554
- console.print(
555
- "[yellow]⚠ Warning:[/yellow] Too many retries detected! "
556
- "Consider reducing the number of [cyan]workers[/cyan] in the [magenta]config.json[/magenta] file. "
557
- "This will impact [bold]performance[/bold]. \n"
558
- )
557
+ console.print("[yellow]⚠ Warning:[/yellow] Too many retries detected! Consider reducing the number of [cyan]workers[/cyan] in the [magenta]config.json[/magenta] file. This will impact [bold]performance[/bold]. \n")
559
558
 
560
559
  # Info to return
561
560
  return {'type': type, 'nFailed': self.info_nFailed}
@@ -59,7 +59,7 @@ def capture_output(process: subprocess.Popen, description: str) -> None:
59
59
 
60
60
 
61
61
  # Construct the progress string with formatted output information
62
- progress_string = (f"[yellow][FFmpeg] [white][{description}[white]]: "
62
+ progress_string = (f" {description}[white]: "
63
63
  f"([green]'speed': [yellow]{data.get('speed', 'N/A')}[white], "
64
64
  f"[green]'size': [yellow]{internet_manager.format_file_size(byte_size)}[white])")
65
65
  max_length = max(max_length, len(progress_string))
@@ -208,7 +208,7 @@ def is_png_format_or_codec(file_info):
208
208
  if not file_info:
209
209
  return False
210
210
 
211
- console.print(f"[yellow][FFmpeg] [cyan]Avaiable codec[white]: [red]{file_info['codec_names']}")
211
+ #console.print(f"[yellow][FFmpeg] [cyan]Avaiable codec[white]: [red]{file_info['codec_names']}")
212
212
  return file_info['format_name'] == 'png_pipe' or 'png' in file_info['codec_names']
213
213
 
214
214
 
@@ -17,7 +17,7 @@ crypto_installed = crypto_spec is not None
17
17
 
18
18
 
19
19
  if crypto_installed:
20
- console.print("[cyan]Decrypy use: Cryptodomex")
20
+ logging.info("[cyan]Decrypy use: Cryptodomex")
21
21
  from Cryptodome.Cipher import AES
22
22
  from Cryptodome.Util.Padding import unpad
23
23
 
@@ -93,7 +93,7 @@ else:
93
93
  # Check if openssl command is available
94
94
  try:
95
95
  openssl_available = subprocess.run(["openssl", "version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0
96
- console.print("[cyan]Decrypy use: OPENSSL")
96
+ logging.info("[cyan]Decrypy use: OPENSSL")
97
97
  except:
98
98
  openssl_available = False
99
99
 
@@ -1,5 +1,3 @@
1
- # 20.02.24
2
-
3
1
  import os
4
2
  import time
5
3
  import logging
@@ -34,10 +32,13 @@ class M3U8_Ts_Estimator:
34
32
  self.now_downloaded_size = 0
35
33
  self.total_segments = total_segments
36
34
  self.lock = threading.Lock()
37
- self.speed = {"upload": "N/A", "download": "N/A"} # Default to N/A
38
- self.speed_thread = threading.Thread(target=self.capture_speed)
39
- self.speed_thread.daemon = True
40
- self.speed_thread.start()
35
+ self.speed = {"upload": "N/A", "download": "N/A"}
36
+
37
+ # Only start the speed capture thread if TQDM_USE_LARGE_BAR is True
38
+ if not TQDM_USE_LARGE_BAR:
39
+ self.speed_thread = threading.Thread(target=self.capture_speed)
40
+ self.speed_thread.daemon = True
41
+ self.speed_thread.start()
41
42
 
42
43
  def add_ts_file(self, size: int, size_download: int, duration: float):
43
44
  """
@@ -56,43 +57,71 @@ class M3U8_Ts_Estimator:
56
57
  self.ts_file_sizes.append(size)
57
58
  self.now_downloaded_size += size_download
58
59
 
59
- def capture_speed(self, interval: float = 1):
60
+ def capture_speed(self, interval: float = 1, pid: int = None):
60
61
  """
61
- Capture the internet speed periodically and store the values.
62
+ Capture the internet speed periodically for a specific process (identified by PID)
63
+ or the entire system if no PID is provided.
62
64
  """
63
- def get_network_io():
64
- """Get network I/O counters, handle missing psutil gracefully."""
65
+
66
+ def get_network_io(process=None):
67
+ """
68
+ Get network I/O counters for a specific process or system-wide if no process is specified.
69
+ """
65
70
  try:
66
- io_counters = psutil.net_io_counters()
67
- return io_counters
68
-
71
+ if process:
72
+ io_counters = process.io_counters()
73
+ return io_counters
74
+ else:
75
+ io_counters = psutil.net_io_counters()
76
+ return io_counters
69
77
  except Exception as e:
70
78
  logging.warning(f"Unable to access network I/O counters: {e}")
71
79
  return None
72
80
 
81
+ # If a PID is provided, attempt to attach to the corresponding process
82
+ process = None
83
+ if pid is not None:
84
+ try:
85
+ process = psutil.Process(pid)
86
+ except psutil.NoSuchProcess:
87
+ logging.error(f"Process with PID {pid} does not exist.")
88
+ return
89
+ except Exception as e:
90
+ logging.error(f"Failed to attach to process with PID {pid}: {e}")
91
+ return
92
+
73
93
  while True:
74
- old_value = get_network_io()
94
+ old_value = get_network_io(process)
75
95
 
76
- if old_value is None: # If psutil is not available, continue with default values
96
+ if old_value is None: # If psutil fails, continue with the next interval
77
97
  time.sleep(interval)
78
98
  continue
79
99
 
80
100
  time.sleep(interval)
81
- new_value = get_network_io()
82
-
101
+ new_value = get_network_io(process)
102
+
83
103
  if new_value is None: # Handle again if psutil fails in the next call
84
104
  time.sleep(interval)
85
105
  continue
86
106
 
87
107
  with self.lock:
88
- upload_speed = (new_value.bytes_sent - old_value.bytes_sent) / interval
89
- download_speed = (new_value.bytes_recv - old_value.bytes_recv) / interval
108
+
109
+ # Calculate speed based on process-specific counters if process is specified
110
+ if process:
111
+ upload_speed = (new_value.write_bytes - old_value.write_bytes) / interval
112
+ download_speed = (new_value.read_bytes - old_value.read_bytes) / interval
113
+
114
+ else:
115
+ # System-wide counters
116
+ upload_speed = (new_value.bytes_sent - old_value.bytes_sent) / interval
117
+ download_speed = (new_value.bytes_recv - old_value.bytes_recv) / interval
90
118
 
91
119
  self.speed = {
92
120
  "upload": internet_manager.format_transfer_speed(upload_speed),
93
121
  "download": internet_manager.format_transfer_speed(download_speed)
94
122
  }
95
123
 
124
+
96
125
  def get_average_speed(self) -> float:
97
126
  """
98
127
  Calculate the average internet speed.
@@ -159,18 +188,17 @@ class M3U8_Ts_Estimator:
159
188
  units_file_downloaded = downloaded_file_size_str.split(' ')[1]
160
189
  units_file_total_size = file_total_size.split(' ')[1]
161
190
 
162
- average_internet_speed = self.get_average_speed()[0]
163
- average_internet_unit = self.get_average_speed()[1]
164
-
165
191
  # Update the progress bar's postfix
166
192
  if TQDM_USE_LARGE_BAR:
193
+ average_internet_speed = self.get_average_speed()[0]
194
+ average_internet_unit = self.get_average_speed()[1]
167
195
  progress_counter.set_postfix_str(
168
196
  f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< {Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size} "
169
197
  f"{Colors.WHITE}| {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit}"
170
198
  )
199
+
171
200
  else:
172
201
  progress_counter.set_postfix_str(
173
202
  f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded}{Colors.RED} {units_file_downloaded} "
174
- f"{Colors.WHITE}| {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit}"
175
- )
176
-
203
+ f"{Colors.WHITE}| {Colors.CYAN}N/A{Colors.RED} N/A"
204
+ )
@@ -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"[red]Everything is up to date")
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
 
@@ -1,5 +1,5 @@
1
1
  __title__ = 'StreamingCommunity'
2
- __version__ = '2.0.0'
2
+ __version__ = '2.1.0'
3
3
  __author__ = 'Lovi-0'
4
4
  __description__ = 'A command-line program to download film'
5
5
  __copyright__ = 'Copyright 2024'
@@ -116,21 +116,34 @@ 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
- for executable in executables:
129
- exe_paths = glob.glob(os.path.join(self.base_dir, executable))
130
- if exe_paths:
131
- found_executables.append(exe_paths[0])
132
- else:
133
- found_executables.append(None)
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
+ # Original behavior for other operating systems
144
+ for executable in executables:
145
+ exe_paths = glob.glob(os.path.join(self.base_dir, executable))
146
+ found_executables.append(exe_paths[0] if exe_paths else None)
134
147
 
135
148
  return tuple(found_executables) if len(found_executables) == 3 else (None, None, None)
136
149
 
@@ -275,37 +288,61 @@ class FFMPEGDownloader:
275
288
  def check_ffmpeg() -> Tuple[Optional[str], Optional[str], Optional[str]]:
276
289
  """
277
290
  Check for FFmpeg executables in the system and download them if not found.
291
+ Enhanced detection for macOS systems.
278
292
 
279
293
  Returns:
280
294
  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
295
  """
290
296
  try:
291
- if platform.system().lower() == 'windows':
292
- ffmpeg_path = subprocess.check_output(['where', 'ffmpeg'], text=True).strip().split('\n')[0] if subprocess.call(['where', 'ffmpeg'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0 else None
293
- ffprobe_path = subprocess.check_output(['where', 'ffprobe'], text=True).strip().split('\n')[0] if subprocess.call(['where', 'ffprobe'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0 else None
294
- ffplay_path = subprocess.check_output(['where', 'ffplay'], text=True).strip().split('\n')[0] if subprocess.call(['where', 'ffplay'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0 else None
297
+ system_platform = platform.system().lower()
298
+
299
+ # Special handling for macOS
300
+ if system_platform == 'darwin':
301
+ # Common installation paths on macOS
302
+ potential_paths = [
303
+ '/usr/local/bin', # Homebrew default
304
+ '/opt/homebrew/bin', # Apple Silicon Homebrew
305
+ '/usr/bin', # System default
306
+ os.path.expanduser('~/Applications/binary'), # Custom installation
307
+ '/Applications/binary' # Custom installation
308
+ ]
295
309
 
296
- if all([ffmpeg_path, ffprobe_path, ffplay_path]):
297
- return ffmpeg_path, ffprobe_path, ffplay_path
310
+ for path in potential_paths:
311
+ ffmpeg_path = os.path.join(path, 'ffmpeg')
312
+ ffprobe_path = os.path.join(path, 'ffprobe')
313
+ ffplay_path = os.path.join(path, 'ffplay')
314
+
315
+ if (os.path.exists(ffmpeg_path) and os.path.exists(ffprobe_path) and
316
+ os.access(ffmpeg_path, os.X_OK) and os.access(ffprobe_path, os.X_OK)):
317
+ # Return found executables, with ffplay being optional
318
+ ffplay_path = ffplay_path if os.path.exists(ffplay_path) else None
319
+ return ffmpeg_path, ffprobe_path, ffplay_path
320
+
321
+ # Windows detection
322
+ elif system_platform == 'windows':
323
+ try:
324
+ ffmpeg_path = subprocess.check_output(['where', 'ffmpeg'], text=True).strip().split('\n')[0]
325
+ ffprobe_path = subprocess.check_output(['where', 'ffprobe'], text=True).strip().split('\n')[0]
326
+ ffplay_path = subprocess.check_output(['where', 'ffplay'], text=True).strip().split('\n')[0]
327
+
328
+ if ffmpeg_path and ffprobe_path:
329
+ return ffmpeg_path, ffprobe_path, ffplay_path
330
+ except subprocess.CalledProcessError:
331
+ pass
332
+
333
+ # Linux detection
298
334
  else:
299
335
  ffmpeg_path = shutil.which('ffmpeg')
300
336
  ffprobe_path = shutil.which('ffprobe')
301
337
  ffplay_path = shutil.which('ffplay')
302
338
 
303
- if all([ffmpeg_path, ffprobe_path, ffplay_path]):
339
+ if ffmpeg_path and ffprobe_path:
304
340
  return ffmpeg_path, ffprobe_path, ffplay_path
305
-
341
+
342
+ # If executables were not found, attempt to download FFmpeg
306
343
  downloader = FFMPEGDownloader()
307
344
  return downloader.download()
308
-
345
+
309
346
  except Exception as e:
310
- logging.error(f"Error checking FFmpeg: {e}")
347
+ logging.error(f"Error checking or downloading FFmpeg executables: {e}")
311
348
  return None, None, None
@@ -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
- Run 'where ffmpeg' command to check FFmpeg's location.
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, stderr=subprocess.STDOUT, text=True).strip()
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
- console.print("FFmpeg not found in system PATH", style="bold red")
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
- # Usa il comando 'where' su Windows
471
- if platform.system() == "Windows":
472
- command = 'where'
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 path enviroment
479
- if self.ffmpeg_path != None and "binary" not in 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 != None and "binary" not in 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
- # Locate ffmpeg from bin installation
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]Cant locate ffmpeg or ffprobe")
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
@@ -120,7 +120,6 @@ def initialize():
120
120
  # Attempting GitHub update
121
121
  try:
122
122
  git_update()
123
- print()
124
123
  except:
125
124
  console.log("[red]Error with loading github.")
126
125
 
@@ -1,17 +1,17 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: StreamingCommunity
3
- Version: 2.0.0
4
- Summary: A streaming community package
3
+ Version: 2.0.5
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: Streaming Community
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
@@ -359,10 +359,8 @@ forced-ita hin - Hindi pol - Polish tur - Turkish
359
359
 
360
360
  <br>
361
361
 
362
-
363
362
  # COMMAND
364
363
 
365
-
366
364
  - Download a specific season by entering its number.
367
365
  * **Example:** `1` will download *Season 1* only.
368
366
 
@@ -433,7 +431,7 @@ The `run-container` command mounts also the `config.json` file, so any change to
433
431
 
434
432
  # To Do
435
433
 
436
- - Create website API -> https://github.com/Lovi-0/StreamingCommunity/tree/test_gui_1
434
+ - Finish [website API](https://github.com/Lovi-0/StreamingCommunity/tree/test_gui_1)
437
435
 
438
436
  # Contributing
439
437
 
@@ -454,3 +452,5 @@ This software is provided "as is", without warranty of any kind, express or impl
454
452
  <a href="https://github.com/Lovi-0/StreamingCommunity/graphs/contributors" alt="View Contributors">
455
453
  <img src="https://contrib.rocks/image?repo=Lovi-0/StreamingCommunity&max=1000&columns=10" alt="Contributors" />
456
454
  </a>
455
+
456
+
@@ -1,92 +1,92 @@
1
1
  StreamingCommunity/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- StreamingCommunity/run.py,sha256=8juF-UPFnzzs0AqJkF_FF10PkYisbqc70xypln1aMRc,6435
2
+ StreamingCommunity/run.py,sha256=bi57FYyAS49NCXObiJpwunGbnpRzXG0EpLjBOq86hjQ,6418
3
3
  StreamingCommunity/Api/Player/ddl.py,sha256=cRPb3sfX5o_rkJ33qe7rDGmlgRgYMr0LTmdZLCsbO90,2409
4
4
  StreamingCommunity/Api/Player/maxstream.py,sha256=0NwzyTFrIWC21xZytmg4Pl_ShAPfqIOXNU6XpEy9KuY,5304
5
5
  StreamingCommunity/Api/Player/supervideo.py,sha256=gNoAo_LVZWycfIzmK28erWRTXwNDV6FKJSLJ2Lrw_Ok,6118
6
6
  StreamingCommunity/Api/Player/vixcloud.py,sha256=pTl60wxSQmyCWjhv1RfHST_hacnwT3N9WnYnUJwKank,9498
7
7
  StreamingCommunity/Api/Player/Helper/Vixcloud/js_parser.py,sha256=HniFPpcF1qtMAT8z5bf4noHJO85x8wRke6KUxB5r-F0,4438
8
8
  StreamingCommunity/Api/Player/Helper/Vixcloud/util.py,sha256=4DomUZ6qg_GGCIfQ38-CKrO24zyzm8voJgv8OiMxDbw,5257
9
- StreamingCommunity/Api/Site/1337xx/__init__.py,sha256=tNVEjRSnXOeWcJTF5c840YFBTiOgSXD0GaM-VsxOGT8,1217
9
+ StreamingCommunity/Api/Site/1337xx/__init__.py,sha256=gZFNPJahddRTELSdO4_-yt50wl0cZRDuxdRqIhYzSiQ,1219
10
10
  StreamingCommunity/Api/Site/1337xx/costant.py,sha256=3jDI0qlysmtfiZIAciNA-pBLSza5GBYKZlONyMpb-FI,451
11
11
  StreamingCommunity/Api/Site/1337xx/site.py,sha256=Cn6DC5Od89f8xj22x2NPUgAPlBmWEgXG5J3Aa810Pok,2689
12
12
  StreamingCommunity/Api/Site/1337xx/title.py,sha256=lGJCxmlWvSWtxn69ClOCA4e_zbtHk_otHV7DiSaKcLo,1980
13
- StreamingCommunity/Api/Site/altadefinizione/__init__.py,sha256=FHR7TlsM5LXQkESeM54gfNXSsv1wKZVKilqRlp49pgo,1208
13
+ StreamingCommunity/Api/Site/altadefinizione/__init__.py,sha256=sTUnun_Ew5YrxmpYZvwHV_qj-NIYTOpJOujwN0O7iU8,1210
14
14
  StreamingCommunity/Api/Site/altadefinizione/costant.py,sha256=bxFBx_dljZM8x4nxig8pg35ylDQnR4NNr2SV3_S7Lg0,451
15
15
  StreamingCommunity/Api/Site/altadefinizione/film.py,sha256=aKVnn-7xdxnWJZvkDZwbxVXP-vmZ4RN3jIIBivEAcY0,2253
16
16
  StreamingCommunity/Api/Site/altadefinizione/site.py,sha256=JqNiP4Rqmn7zLALKBIAv0U_KY6uLYvGgg4_6Dw77SKU,2883
17
- StreamingCommunity/Api/Site/animeunity/__init__.py,sha256=3RUvqPerfHYdCX1F1nokjCrSs6BHzj4lCd5y5ZlCbB4,1298
17
+ StreamingCommunity/Api/Site/animeunity/__init__.py,sha256=rcG40DnZPn3vvniohYo5SgyGNTZY24XXfV5bDAEpTWE,1300
18
18
  StreamingCommunity/Api/Site/animeunity/costant.py,sha256=bxFBx_dljZM8x4nxig8pg35ylDQnR4NNr2SV3_S7Lg0,451
19
19
  StreamingCommunity/Api/Site/animeunity/film_serie.py,sha256=clYVjrZRKjWRnJCAvsLEEAfKMtPN9s7pgCgAx64eJuU,4162
20
20
  StreamingCommunity/Api/Site/animeunity/site.py,sha256=WYfPtlaInQuV98Vc7CEFeRSri3EduC9ix6niVXJTnw4,5306
21
21
  StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py,sha256=Gv0xvTJMHru3YExAqQ-ZFfwIxs_227mB3d0JpsI3oAY,2983
22
- StreamingCommunity/Api/Site/cb01new/__init__.py,sha256=A8M8LIcPXdzwaPOARY4LaKlYGlEA_a7OxLYzFzWH7BI,1228
22
+ StreamingCommunity/Api/Site/cb01new/__init__.py,sha256=ClPCBPR6dCdVIZvXknUNum86AOkxCycIKzNl4ZJGVPk,1230
23
23
  StreamingCommunity/Api/Site/cb01new/costant.py,sha256=EcUm_oH28CGbi5Dm2sBBAHJ1ywzn6R6mwk-Rx8mj7aQ,453
24
24
  StreamingCommunity/Api/Site/cb01new/film.py,sha256=uc_SCS645rtwyYCofnzMj7_xc5tna1R4-hHdFHmo9T8,2268
25
25
  StreamingCommunity/Api/Site/cb01new/site.py,sha256=dDv9P9GWtL07eygwndrPsPVmPd0kueeAn2vLPw4vHCc,2153
26
- StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py,sha256=U0mFvhAr9bDt3v_qFOyUC-LC1nsMTQMLvEc4GRrNnQk,1454
26
+ StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py,sha256=3vjwaEXlnrDAProWxyWpkN2Zg528XnpNwUrOvyTHPTY,1374
27
27
  StreamingCommunity/Api/Site/ddlstreamitaly/costant.py,sha256=r-rc_9dIFkfFZwJmY3noG1LgkKgHDxEEzQY-8aWj3dI,515
28
- StreamingCommunity/Api/Site/ddlstreamitaly/series.py,sha256=m-H_CtHIlN_3KPv5d7kgm2au_lZAni8eXuA1-0O5LIo,4263
28
+ StreamingCommunity/Api/Site/ddlstreamitaly/series.py,sha256=QMXO7312R3M6HylF-3Y_SY7MQVgZLBq0dcSb10eNROk,4267
29
29
  StreamingCommunity/Api/Site/ddlstreamitaly/site.py,sha256=0UfXLlABh7-ejU2cRTVsWPAPLaARbF6xu1PV4kyZvG4,2903
30
30
  StreamingCommunity/Api/Site/ddlstreamitaly/util/ScrapeSerie.py,sha256=f7uA3VTANmWnLvYhjxMjzKRxU6FTgH2H8MSntWgc8jw,2652
31
- StreamingCommunity/Api/Site/guardaserie/__init__.py,sha256=UaTo1uXMgL8lVb72PoD4VtecQlqpC-PNPUEIBH756bE,1301
31
+ StreamingCommunity/Api/Site/guardaserie/__init__.py,sha256=1NUo7z1OqDQ-DmXj6Db303SeC-0OQ_ElKcxo73JbpOM,1221
32
32
  StreamingCommunity/Api/Site/guardaserie/costant.py,sha256=OOxqZVqlZ9XcdrOLLOz4U7MA2SrRbZ_qvvlGpuQzczo,453
33
- StreamingCommunity/Api/Site/guardaserie/series.py,sha256=3pXkwbponT6sS2vDVD2QvEk7jhYHoalR80jcpx-hRZE,6873
33
+ StreamingCommunity/Api/Site/guardaserie/series.py,sha256=6qaeIYZrk8b1nC-B0D0Nkn3Zi1YpGTsXrX5ZMhAqO-c,6881
34
34
  StreamingCommunity/Api/Site/guardaserie/site.py,sha256=2f_Q3D25bf0vNAx0XgPRTcay0QeWOm8espzDo9Lp3EA,2632
35
35
  StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py,sha256=Os4lKYQB8PT1b-jzozRIpdovGh4gUQuXrPEHyLriYio,3399
36
- StreamingCommunity/Api/Site/ilcorsaronero/__init__.py,sha256=E75cZUptIDjf1GR-dUY62v6qrTGuBa-r8EACUhVf1Tc,1246
36
+ StreamingCommunity/Api/Site/ilcorsaronero/__init__.py,sha256=JBvYZqLlk5nckjYRApX5AZ58OfxF-yTTu6SE54F49Ms,1248
37
37
  StreamingCommunity/Api/Site/ilcorsaronero/costant.py,sha256=3jDI0qlysmtfiZIAciNA-pBLSza5GBYKZlONyMpb-FI,451
38
38
  StreamingCommunity/Api/Site/ilcorsaronero/site.py,sha256=dkNOUZanaPtQJIrOskzOC4cEOHFG3gnwH5RFYaw_ERU,1812
39
39
  StreamingCommunity/Api/Site/ilcorsaronero/title.py,sha256=5cPMiBWoZPUerS2aI_6qO14hA14ztW-cWkaIInUYBlI,1377
40
- StreamingCommunity/Api/Site/ilcorsaronero/util/ilCorsarScraper.py,sha256=vMYSoa5bn5dIfsjMkb6_0fNf8fOu-7f2wJ9j9alzzFY,4791
41
- StreamingCommunity/Api/Site/mostraguarda/__init__.py,sha256=G6Ozksp8CGTHKy1LsdYJAxX9prnH3CPU_68xJHhRLDo,1175
40
+ StreamingCommunity/Api/Site/ilcorsaronero/util/ilCorsarScraper.py,sha256=PYV_OOU8KyO9ysVchfuFNfikCGmyE-NHknhGB64XOsw,5258
41
+ StreamingCommunity/Api/Site/mostraguarda/__init__.py,sha256=0HRPWgz4pFpVR6_mUQy8SLGcUgQdseVrH-QqnfKlxW4,1177
42
42
  StreamingCommunity/Api/Site/mostraguarda/costant.py,sha256=bxFBx_dljZM8x4nxig8pg35ylDQnR4NNr2SV3_S7Lg0,451
43
- StreamingCommunity/Api/Site/mostraguarda/film.py,sha256=21U1FvttpcYdIzPPY0kKPIgPe0z0IkGvU-wB11Dp7hQ,3081
44
- StreamingCommunity/Api/Site/streamingcommunity/__init__.py,sha256=KsgVib2AOW-afAMFmSgZQUUhjdpawq2kdDxhQ0_enrU,1485
43
+ StreamingCommunity/Api/Site/mostraguarda/film.py,sha256=R93viJqWom4xMsvG-yZqi36bnTlxkSGyW5AbmR0vRyE,3053
44
+ StreamingCommunity/Api/Site/streamingcommunity/__init__.py,sha256=TF8q_xHPXmwEkhDj3MS8xOVHgRQRmK-WNo0pQHChV74,1487
45
45
  StreamingCommunity/Api/Site/streamingcommunity/costant.py,sha256=XEksndtxSC8As-r15f0hRuFAum-CjseegrVjFYOc_XY,436
46
- StreamingCommunity/Api/Site/streamingcommunity/film.py,sha256=f_jfKDVHr0o_-X7XEglUG0hW3U5RCtjzZcIh98S8TKs,2340
47
- StreamingCommunity/Api/Site/streamingcommunity/series.py,sha256=xLdoAr6qH9PMnPFEx1upCwvw7szxeFYIUD2tZvwpvE0,7389
46
+ StreamingCommunity/Api/Site/streamingcommunity/film.py,sha256=dI6NHtsBitVU8rJsdwo9u_WXSoG-uWrFDMWxqYvNmPQ,2339
47
+ StreamingCommunity/Api/Site/streamingcommunity/series.py,sha256=-oEprxWV55XWK2fz4CyP-KtdGt68WhVZGZsyiQ6KkTY,7393
48
48
  StreamingCommunity/Api/Site/streamingcommunity/site.py,sha256=6nW5USa6X5xiZvEHMRKipjZzsV__f8iV1Hx8dqEW2w0,3902
49
49
  StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py,sha256=sNK61c1V6ULTk2kJFBOq3q_jzv281gfE52cjh7Oufsg,4313
50
50
  StreamingCommunity/Api/Template/__init__.py,sha256=lhVpudlILM1L9iBZ_7vfYwgKyrdR1OWeLvk364-fJpU,48
51
51
  StreamingCommunity/Api/Template/site.py,sha256=Xk_XpZ97xDUhGeiwo-52b7h1kOVsRQVdCqoXcMs2zHk,2995
52
52
  StreamingCommunity/Api/Template/Class/SearchType.py,sha256=lJ054oi7baFU8HoXzq_mO4foRz0wtF9GIGOOFS8VpmY,2632
53
53
  StreamingCommunity/Api/Template/Util/__init__.py,sha256=e-206LT2iAnL9I21fR_1FRm8F3WsbBRrUmraPP2Xfzo,202
54
- StreamingCommunity/Api/Template/Util/get_domain.py,sha256=AiLqXWF3RAgkwEZ663QD2ReH0afhuOHCs7fir6Rwgg4,6396
54
+ StreamingCommunity/Api/Template/Util/get_domain.py,sha256=HFV5R-D6Nu0yhP19PCRSZOSFvvNq5FKWg9lHd1juTxI,6479
55
55
  StreamingCommunity/Api/Template/Util/manage_ep.py,sha256=TkeRH8120Bsu7gTOJb2GRdZI6LZmgYCD60tQVruwkvI,6697
56
56
  StreamingCommunity/Api/Template/Util/recall_search.py,sha256=N-h00R2S8rQud3mNtUmaakK_8d9e5YxgHv4QwcJ4aZw,1184
57
57
  StreamingCommunity/Lib/Downloader/__init__.py,sha256=vAn-rpmlSmojvz4Quv47A5HLq4yBR_7noy4r6hqk0OQ,144
58
- StreamingCommunity/Lib/Downloader/HLS/downloader.py,sha256=pPgoohAhfxs0HBqtudBRmgGSlUMn6MwDE1kjT4vZ--I,39558
58
+ StreamingCommunity/Lib/Downloader/HLS/downloader.py,sha256=_3kXihkLDChHIXlRzgN0o0prgyduONx7K7DE0pJm23Y,39919
59
59
  StreamingCommunity/Lib/Downloader/HLS/proxyes.py,sha256=Mrs5Mr6ATv-6BUS7CLcZjw3JNH7g_XOz7boeB1oQAQ8,3385
60
- StreamingCommunity/Lib/Downloader/HLS/segments.py,sha256=cs81AgyowaAKDbN9Kz1jdCXpr0It5SeaKQeRbE0nwOM,22415
60
+ StreamingCommunity/Lib/Downloader/HLS/segments.py,sha256=D7XwnranQP24zy5Ht3ctBIJ7NVBLt5jB2-zT3SHeNH0,22461
61
61
  StreamingCommunity/Lib/Downloader/MP4/downloader.py,sha256=gWhNHhL5f3bXAGUaOlH2wCwda0sKi-R9s6PxOIp7LrU,6028
62
62
  StreamingCommunity/Lib/Downloader/TOR/downloader.py,sha256=67RDi3Er5xpoHFIn11sGcCB1xgIEGE-Nhn9wqDfmGak,11617
63
63
  StreamingCommunity/Lib/FFmpeg/__init__.py,sha256=0KehwaTYL72PJaJJo2fzveGgc9F2-abIk7w6gXsfX1w,135
64
- StreamingCommunity/Lib/FFmpeg/capture.py,sha256=pgYsJGnC90dNZm6bBgdlXGhkJWDS-aDvZNzbP8hCJYY,5623
64
+ StreamingCommunity/Lib/FFmpeg/capture.py,sha256=YptVE5Ff6-hJXmsARxtXclWXmVl16y6QLR-VMybBaEg,5601
65
65
  StreamingCommunity/Lib/FFmpeg/command.py,sha256=EfI3Q4tQkV7XmKWx0eBDnuIhn0O_lfPiAiI5IET3nLk,10289
66
- StreamingCommunity/Lib/FFmpeg/util.py,sha256=trIi1un5lpYdIewD1eXddgNU8f65FwwltFP2s0D4xkI,8340
66
+ StreamingCommunity/Lib/FFmpeg/util.py,sha256=QTtBaFdrUa5CFSPq1ycr3_zw81A7zVRmQN7FoxUwuR8,8341
67
67
  StreamingCommunity/Lib/M3U8/__init__.py,sha256=sD2VIslF43OrudA1r-9xkSfUvSblr1LOZpaIM89F6M4,175
68
- StreamingCommunity/Lib/M3U8/decryptor.py,sha256=sm-kG-3zzOIqrPMNiID68bepoWKQ1F9gbKdiCmxjdP0,6475
69
- StreamingCommunity/Lib/M3U8/estimator.py,sha256=aaOLtrTtFwGEVxu76DPy4ACJ4kSBUJqc9sH-N6Ch2wc,6655
68
+ StreamingCommunity/Lib/M3U8/decryptor.py,sha256=q6GVlM9UtiR_Yop7-MDUTShAESc66LX2k2Eqvj3vge8,6473
69
+ StreamingCommunity/Lib/M3U8/estimator.py,sha256=_tYTCjWmd4zlmywozricbUo2tDk81N2UVMh5BRdwRmo,7918
70
70
  StreamingCommunity/Lib/M3U8/parser.py,sha256=xmyCU4AYGIOUheTZ4OBPmQl_R4dJ3Y72i9UUa6_aErA,23553
71
71
  StreamingCommunity/Lib/M3U8/url_fixer.py,sha256=6NVKhc8R5CqarDM5TLWy6QU05qXPouW31gyilQszwbI,1675
72
72
  StreamingCommunity/Lib/TMBD/__init__.py,sha256=b3yUqfeBFpnKH-MScrZ3r90cpXc2ufCC-El3whK1zk8,55
73
73
  StreamingCommunity/Lib/TMBD/obj_tmbd.py,sha256=HEL3jAqUYtVgX7GCaw60EAD3JGvEJLOQXfD6lRYEjxA,1968
74
74
  StreamingCommunity/Lib/TMBD/tmdb.py,sha256=3UO_0uzi8xtrokX8Y_vO8vx_V8XHSkOVCqtgT-289GM,12000
75
- StreamingCommunity/Upload/update.py,sha256=MF1M-vypz-z6tn9z_lQkZCpXD02GvHC2UvsBcK0mNC0,2339
76
- StreamingCommunity/Upload/version.py,sha256=rqufxkhxojj2efd6SdeXRGYhZrlBbi_jGdALT1WsnC0,175
75
+ StreamingCommunity/Upload/update.py,sha256=ly2E4gf5RCkhlHvDyVNP57jkGGH41V-UHLYwBwgypP4,2324
76
+ StreamingCommunity/Upload/version.py,sha256=NnaKT3FVdTBYEFQQvkTJdUwaLcGasNTNHck7hLJ1A3g,175
77
77
  StreamingCommunity/Util/_jsonConfig.py,sha256=CLvk6HWxUklZztoy55SzEOvdsbNo-pFcVQVanwixXCc,7001
78
78
  StreamingCommunity/Util/call_stack.py,sha256=zuYbO8dV8bCa7fCdtaKQYuheA5K7BkTm3Oj8JA6GCpM,1417
79
79
  StreamingCommunity/Util/color.py,sha256=1iQUf5xDp5XKKbXl9MvKEXJvv44Zf0P4J2Nu5ATIId8,479
80
80
  StreamingCommunity/Util/console.py,sha256=bXP_iibXMpRIJ_zs03xFmSbkvMP3SguIuEUJZovoOqw,230
81
- StreamingCommunity/Util/ffmpeg_installer.py,sha256=buiAxxcgHWPzj9ngq4-SCkZRXOmXEqfDwmuZH6g2-Q4,12558
81
+ StreamingCommunity/Util/ffmpeg_installer.py,sha256=fDV45sb17KNO7DCzMfTtV1fKsrQUNNMInOTVtL_BokE,13907
82
82
  StreamingCommunity/Util/headers.py,sha256=r5hK8HVF-y6dQzCQpDnpbDGP02n29OYlzgaZAorcmG0,4565
83
83
  StreamingCommunity/Util/logger.py,sha256=5XmFquGYt4FjvKNyYKa21mLLKARmzGWk-mBo05ezlHQ,1914
84
84
  StreamingCommunity/Util/message.py,sha256=pgUf50z4kSJjKHBlBKn5bd26xlAAEvlLiMs9dvcvJ_s,3675
85
- StreamingCommunity/Util/os.py,sha256=FydTixaZyrHN0ZPPmqXwuPCkUz7IG8-yIcMA1osD-sI,19472
85
+ StreamingCommunity/Util/os.py,sha256=K8bgAxFq8pPFaMQPfeTSQ_E9aEERN7pwWB_XEXBC84Q,19429
86
86
  StreamingCommunity/Util/table.py,sha256=lKCgvrtfODuQY5dFJqlNUYL2aFzfij-efS5oGv84E44,8508
87
- StreamingCommunity-2.0.0.dist-info/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
88
- StreamingCommunity-2.0.0.dist-info/METADATA,sha256=_O1JRXFC_HXh2OcqlwmBK4tBqVBcvpyjf3_xbmVmVhk,13439
89
- StreamingCommunity-2.0.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
90
- StreamingCommunity-2.0.0.dist-info/entry_points.txt,sha256=Qph9XYfDC8n4LfDLOSl6gJGlkb9eFb5f-JOr_Wb_5rk,67
91
- StreamingCommunity-2.0.0.dist-info/top_level.txt,sha256=YsOcxKP-WOhWpIWgBlh0coll9XUx7aqmRPT7kmt3fH0,19
92
- StreamingCommunity-2.0.0.dist-info/RECORD,,
87
+ StreamingCommunity-2.0.5.dist-info/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
88
+ StreamingCommunity-2.0.5.dist-info/METADATA,sha256=tuuAltpAwEyHXXoonOx6SwLg98u-CWdVsjnn6MYTwCk,13406
89
+ StreamingCommunity-2.0.5.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
90
+ StreamingCommunity-2.0.5.dist-info/entry_points.txt,sha256=-iQU6qfeHFwauAg4iZhifWhNZAkiV-x3XuEauo_EjUc,68
91
+ StreamingCommunity-2.0.5.dist-info/top_level.txt,sha256=YsOcxKP-WOhWpIWgBlh0coll9XUx7aqmRPT7kmt3fH0,19
92
+ StreamingCommunity-2.0.5.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.1.0)
2
+ Generator: bdist_wheel (0.45.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
2
  streamingcommunity = StreamingCommunity.run:main
3
+