StreamingCommunity 3.3.5__py3-none-any.whl → 3.3.6__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 (44) hide show
  1. StreamingCommunity/Api/Site/altadefinizione/__init__.py +17 -18
  2. StreamingCommunity/Api/Site/altadefinizione/series.py +4 -0
  3. StreamingCommunity/Api/Site/animeunity/__init__.py +14 -15
  4. StreamingCommunity/Api/Site/animeunity/serie.py +1 -1
  5. StreamingCommunity/Api/Site/animeworld/__init__.py +15 -13
  6. StreamingCommunity/Api/Site/animeworld/serie.py +1 -1
  7. StreamingCommunity/Api/Site/crunchyroll/__init__.py +16 -17
  8. StreamingCommunity/Api/Site/crunchyroll/series.py +6 -1
  9. StreamingCommunity/Api/Site/guardaserie/__init__.py +17 -19
  10. StreamingCommunity/Api/Site/guardaserie/series.py +4 -0
  11. StreamingCommunity/Api/Site/guardaserie/site.py +2 -7
  12. StreamingCommunity/Api/Site/mediasetinfinity/__init__.py +15 -15
  13. StreamingCommunity/Api/Site/mediasetinfinity/series.py +4 -0
  14. StreamingCommunity/Api/Site/mediasetinfinity/site.py +12 -2
  15. StreamingCommunity/Api/Site/mediasetinfinity/util/ScrapeSerie.py +67 -98
  16. StreamingCommunity/Api/Site/raiplay/__init__.py +15 -15
  17. StreamingCommunity/Api/Site/raiplay/series.py +5 -1
  18. StreamingCommunity/Api/Site/streamingcommunity/__init__.py +16 -14
  19. StreamingCommunity/Api/Site/streamingwatch/__init__.py +12 -12
  20. StreamingCommunity/Api/Site/streamingwatch/series.py +4 -0
  21. StreamingCommunity/Api/Template/Class/SearchType.py +0 -1
  22. StreamingCommunity/Api/Template/Util/manage_ep.py +1 -11
  23. StreamingCommunity/Api/Template/site.py +2 -3
  24. StreamingCommunity/Lib/Downloader/DASH/downloader.py +55 -17
  25. StreamingCommunity/Lib/Downloader/DASH/segments.py +73 -17
  26. StreamingCommunity/Lib/Downloader/HLS/downloader.py +282 -152
  27. StreamingCommunity/Lib/Downloader/HLS/segments.py +1 -5
  28. StreamingCommunity/Lib/FFmpeg/capture.py +1 -1
  29. StreamingCommunity/Lib/FFmpeg/command.py +6 -6
  30. StreamingCommunity/Lib/FFmpeg/util.py +11 -30
  31. StreamingCommunity/Lib/M3U8/estimator.py +27 -13
  32. StreamingCommunity/Upload/update.py +2 -2
  33. StreamingCommunity/Upload/version.py +1 -1
  34. StreamingCommunity/Util/installer/__init__.py +11 -0
  35. StreamingCommunity/Util/installer/device_install.py +1 -1
  36. StreamingCommunity/Util/os.py +2 -6
  37. StreamingCommunity/Util/table.py +40 -8
  38. StreamingCommunity/run.py +15 -8
  39. {streamingcommunity-3.3.5.dist-info → streamingcommunity-3.3.6.dist-info}/METADATA +38 -51
  40. {streamingcommunity-3.3.5.dist-info → streamingcommunity-3.3.6.dist-info}/RECORD +44 -43
  41. {streamingcommunity-3.3.5.dist-info → streamingcommunity-3.3.6.dist-info}/WHEEL +0 -0
  42. {streamingcommunity-3.3.5.dist-info → streamingcommunity-3.3.6.dist-info}/entry_points.txt +0 -0
  43. {streamingcommunity-3.3.5.dist-info → streamingcommunity-3.3.6.dist-info}/licenses/LICENSE +0 -0
  44. {streamingcommunity-3.3.5.dist-info → streamingcommunity-3.3.6.dist-info}/top_level.txt +0 -0
@@ -164,13 +164,13 @@ def join_video(video_path: str, out_path: str, codec: M3U8_Codec = None):
164
164
  else:
165
165
 
166
166
  if get_use_large_bar():
167
- capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join video")
167
+ capture_ffmpeg_real_time(ffmpeg_cmd, "[yellow][FFMPEG] [cyan]Join video")
168
168
  print()
169
169
 
170
170
  else:
171
171
  console.log("[purple]FFmpeg [white][[cyan]Join video[white]] ...")
172
172
  with suppress_output():
173
- capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join video")
173
+ capture_ffmpeg_real_time(ffmpeg_cmd, "[yellow][FFMPEG] [cyan]Join video")
174
174
  print()
175
175
 
176
176
  return out_path
@@ -299,13 +299,13 @@ def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: s
299
299
 
300
300
  else:
301
301
  if get_use_large_bar():
302
- capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join audio")
302
+ capture_ffmpeg_real_time(ffmpeg_cmd, "[yellow][FFMPEG] [cyan]Join audio")
303
303
  print()
304
304
 
305
305
  else:
306
306
  console.log("[purple]FFmpeg [white][[cyan]Join audio[white]] ...")
307
307
  with suppress_output():
308
- capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join audio")
308
+ capture_ffmpeg_real_time(ffmpeg_cmd, "[yellow][FFMPEG] [cyan]Join audio")
309
309
  print()
310
310
 
311
311
  return out_path, use_shortest
@@ -366,13 +366,13 @@ def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_pat
366
366
 
367
367
  else:
368
368
  if get_use_large_bar():
369
- capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join subtitle")
369
+ capture_ffmpeg_real_time(ffmpeg_cmd, "[yellow][FFMPEG] [cyan]Join subtitle")
370
370
  print()
371
371
 
372
372
  else:
373
373
  console.log("[purple]FFmpeg [white][[cyan]Join subtitle[white]] ...")
374
374
  with suppress_output():
375
- capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join subtitle")
375
+ capture_ffmpeg_real_time(ffmpeg_cmd, "[yellow][FFMPEG] [cyan]Join subtitle")
376
376
  print()
377
377
 
378
378
  return out_path
@@ -133,8 +133,10 @@ def print_duration_table(file_path: str, description: str = "Duration", return_s
133
133
  def get_ffprobe_info(file_path):
134
134
  """
135
135
  Get format and codec information for a media file using ffprobe.
136
+
136
137
  Parameters:
137
138
  - file_path (str): Path to the media file.
139
+
138
140
  Returns:
139
141
  dict: A dictionary containing the format name and a list of codec names.
140
142
  Returns None if file does not exist or ffprobe crashes.
@@ -143,25 +145,8 @@ def get_ffprobe_info(file_path):
143
145
  logging.error(f"File not found: {file_path}")
144
146
  return None
145
147
 
146
- # Get ffprobe path and verify it exists
147
- ffprobe_path = get_ffprobe_path()
148
- if not ffprobe_path or not os.path.exists(ffprobe_path):
149
- logging.error(f"FFprobe not found at path: {ffprobe_path}")
150
- return None
151
-
152
- # Verify file permissions
153
- try:
154
- file_stat = os.stat(file_path)
155
- logging.info(f"File permissions: {oct(file_stat.st_mode)}")
156
- if not os.access(file_path, os.R_OK):
157
- logging.error(f"No read permission for file: {file_path}")
158
- return None
159
- except OSError as e:
160
- logging.error(f"Cannot access file {file_path}: {e}")
161
- return None
162
-
163
148
  try:
164
- cmd = [ffprobe_path, '-v', 'error', '-show_format', '-show_streams', '-print_format', 'json', file_path]
149
+ cmd = [get_ffprobe_path(), '-v', 'error', '-show_format', '-show_streams', '-print_format', 'json', file_path]
165
150
  logging.info(f"Running FFprobe command: {' '.join(cmd)}")
166
151
 
167
152
  # Use subprocess.run instead of Popen for better error handling
@@ -177,20 +162,15 @@ def get_ffprobe_info(file_path):
177
162
  logging.error(f"FFprobe stderr: {result.stderr}")
178
163
  logging.error(f"FFprobe stdout: {result.stdout}")
179
164
  logging.error(f"Command: {' '.join(cmd)}")
180
- logging.error(f"FFprobe path permissions: {oct(os.stat(ffprobe_path).st_mode)}")
181
165
  return None
182
166
 
183
167
  # Parse JSON output
184
- try:
185
- info = json.loads(result.stdout)
186
- return {
187
- 'format_name': info.get('format', {}).get('format_name'),
188
- 'codec_names': [stream.get('codec_name') for stream in info.get('streams', [])]
189
- }
190
- except json.JSONDecodeError as e:
191
- logging.error(f"Failed to parse FFprobe output: {e}")
192
- return None
193
-
168
+ info = json.loads(result.stdout)
169
+ return {
170
+ 'format_name': info.get('format', {}).get('format_name'),
171
+ 'codec_names': [stream.get('codec_name') for stream in info.get('streams', [])]
172
+ }
173
+
194
174
  except Exception as e:
195
175
  logging.error(f"FFprobe execution failed: {e}")
196
176
  return None
@@ -223,11 +203,12 @@ def need_to_force_to_ts(file_path):
223
203
  Parameters:
224
204
  - file_path (str): Path to the input media file.
225
205
  """
226
- logging.info(f"Processing file: {file_path}")
227
206
  file_info = get_ffprobe_info(file_path)
228
207
 
229
208
  if is_png_format_or_codec(file_info):
209
+ console.print(f"[yellow]Warning: The input file [green]{os.path.basename(file_path)}[/green] is in PNG format or contains a PNG codec. It will be converted to TS format for processing.[/yellow]")
230
210
  return True
211
+
231
212
  return False
232
213
 
233
214
 
@@ -30,6 +30,7 @@ class M3U8_Ts_Estimator:
30
30
  self.lock = threading.Lock()
31
31
  self.speed = {"upload": "N/A", "download": "N/A"}
32
32
  self._running = True
33
+ self.downloaded_segments_count = 0
33
34
  self.speed_thread = threading.Thread(target=self.capture_speed)
34
35
  self.speed_thread.daemon = True
35
36
  self.speed_thread.start()
@@ -44,7 +45,8 @@ class M3U8_Ts_Estimator:
44
45
  logging.error(f"Invalid input values: size={size}")
45
46
  return
46
47
 
47
- self.ts_file_sizes.append(size)
48
+ with self.lock:
49
+ self.ts_file_sizes.append(size)
48
50
 
49
51
  def capture_speed(self, interval: float = 3.0):
50
52
  """Capture the internet speed periodically."""
@@ -103,27 +105,37 @@ class M3U8_Ts_Estimator:
103
105
 
104
106
  def calculate_total_size(self) -> str:
105
107
  """
106
- Calculate the total size of the files.
108
+ Calculate the estimated total size of all segments.
107
109
 
108
110
  Returns:
109
- str: The mean size of the files in a human-readable format.
111
+ str: The estimated total size in a human-readable format.
110
112
  """
111
113
  try:
112
- if not self.ts_file_sizes:
113
- return "0 B"
114
-
115
- total_size = sum(self.ts_file_sizes)
116
- mean_size = total_size / len(self.ts_file_sizes)
117
- return internet_manager.format_file_size(mean_size)
114
+ with self.lock:
115
+ if not self.ts_file_sizes:
116
+ return "0 B"
117
+
118
+ mean_segment_size = sum(self.ts_file_sizes) / len(self.ts_file_sizes)
119
+ estimated_total_size = mean_segment_size * self.total_segments
120
+ return internet_manager.format_file_size(estimated_total_size)
118
121
 
119
122
  except Exception as e:
120
123
  logging.error("An unexpected error occurred: %s", e)
121
124
  return "Error"
122
125
 
123
- def update_progress_bar(self, total_downloaded: int, progress_counter: tqdm) -> None:
124
- """Update progress bar"""
126
+ def update_progress_bar(self, segment_size: int, progress_counter: tqdm) -> None:
127
+ """
128
+ Update progress bar with segment information.
129
+
130
+ Parameters:
131
+ - segment_size (int): Size in bytes of the current downloaded segment
132
+ - progress_counter (tqdm): Progress bar instance to update
133
+ """
125
134
  try:
126
- self.add_ts_file(total_downloaded * self.total_segments)
135
+ self.add_ts_file(segment_size)
136
+
137
+ with self.lock:
138
+ self.downloaded_segments_count += 1
127
139
 
128
140
  file_total_size = self.calculate_total_size()
129
141
  if file_total_size == "Error":
@@ -151,4 +163,6 @@ class M3U8_Ts_Estimator:
151
163
 
152
164
  def stop(self):
153
165
  """Stop speed monitoring thread."""
154
- self._running = False
166
+ self._running = False
167
+ if self.speed_thread.is_alive():
168
+ self.speed_thread.join(timeout=5.0)
@@ -6,6 +6,7 @@ import time
6
6
  import asyncio
7
7
  import importlib.metadata
8
8
 
9
+
9
10
  # External library
10
11
  import httpx
11
12
  from rich.console import Console
@@ -79,7 +80,6 @@ def update():
79
80
  try:
80
81
  current_version = importlib.metadata.version(__title__)
81
82
  except importlib.metadata.PackageNotFoundError:
82
- #console.print(f"[yellow]Warning: Could not determine installed version for '{__title__}' via importlib.metadata. Falling back to source version.[/yellow]")
83
83
  current_version = source_code_version
84
84
 
85
85
  # Get commit details
@@ -98,4 +98,4 @@ def update():
98
98
  console.print(f"\n[red]{__title__} has been downloaded [yellow]{total_download_count} [red]times, but only [yellow]{percentual_stars}% [red]of users have starred it.\n\
99
99
  [cyan]Help the repository grow today by leaving a [yellow]star [cyan]and [yellow]sharing [cyan]it with others online!")
100
100
 
101
- time.sleep(2.5)
101
+ time.sleep(2)
@@ -1,5 +1,5 @@
1
1
  __title__ = 'StreamingCommunity'
2
- __version__ = '3.3.5'
2
+ __version__ = '3.3.6'
3
3
  __author__ = 'Arrowar'
4
4
  __description__ = 'A command-line program to download film'
5
5
  __copyright__ = 'Copyright 2025'
@@ -0,0 +1,11 @@
1
+ # 18.07.25
2
+
3
+ from .ffmpeg_install import check_ffmpeg
4
+ from .bento4_install import check_mp4decrypt
5
+ from .device_install import check_device_wvd_path
6
+
7
+ __all__ = [
8
+ "check_ffmpeg",
9
+ "check_mp4decrypt",
10
+ "check_device_wvd_path"
11
+ ]
@@ -114,7 +114,7 @@ class DeviceDownloader:
114
114
  return None
115
115
 
116
116
 
117
- def check_wvd_path() -> Optional[str]:
117
+ def check_device_wvd_path() -> Optional[str]:
118
118
  """
119
119
  Check for device.wvd file in binary directory and extract from PNG if not found.
120
120
  """
@@ -2,7 +2,6 @@
2
2
 
3
3
  import io
4
4
  import os
5
- import time
6
5
  import shutil
7
6
  import logging
8
7
  import socket
@@ -19,9 +18,7 @@ from pathvalidate import sanitize_filename, sanitize_filepath
19
18
 
20
19
 
21
20
  # Internal utilities
22
- from .installer.ffmpeg_install import check_ffmpeg
23
- from .installer.bento4_install import check_mp4decrypt
24
- from .installer.device_install import check_wvd_path
21
+ from .installer import check_ffmpeg, check_mp4decrypt, check_device_wvd_path
25
22
 
26
23
 
27
24
  # Variable
@@ -318,10 +315,9 @@ class OsSummary:
318
315
  # Check for binaries
319
316
  self.ffmpeg_path, self.ffprobe_path, _ = check_ffmpeg()
320
317
  self.mp4decrypt_path = check_mp4decrypt()
321
- self.wvd_path = check_wvd_path()
318
+ self.wvd_path = check_device_wvd_path()
322
319
 
323
320
  self._display_binary_paths()
324
- time.sleep(0.3)
325
321
 
326
322
  def _display_binary_paths(self):
327
323
  """Display the paths of all detected binaries."""
@@ -29,7 +29,9 @@ TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
29
29
 
30
30
  class TVShowManager:
31
31
  def __init__(self):
32
- """Initialize TVShowManager with default values."""
32
+ """
33
+ Initialize TVShowManager with default values.
34
+ """
33
35
  self.console = Console()
34
36
  self.tv_shows: List[Dict[str, Any]] = []
35
37
  self.slice_start = 0
@@ -53,7 +55,8 @@ class TVShowManager:
53
55
  Parameters:
54
56
  - tv_show (Dict[str, Any]): Dictionary containing TV show details.
55
57
  """
56
- self.tv_shows.append(tv_show)
58
+ if tv_show:
59
+ self.tv_shows.append(tv_show)
57
60
 
58
61
  def display_data(self, data_slice: List[Dict[str, Any]]) -> None:
59
62
  """
@@ -62,6 +65,14 @@ class TVShowManager:
62
65
  Parameters:
63
66
  - data_slice (List[Dict[str, Any]]): List of dictionaries containing TV show details to display.
64
67
  """
68
+ if not data_slice:
69
+ logging.error("Nothing to display.")
70
+ return
71
+
72
+ if not self.column_info:
73
+ logging.error("Error: Column information not configured.")
74
+ return
75
+
65
76
  table = Table(border_style="white")
66
77
 
67
78
  # Add columns dynamically based on provided column information
@@ -72,8 +83,9 @@ class TVShowManager:
72
83
 
73
84
  # Add rows dynamically based on available TV show data
74
85
  for entry in data_slice:
75
- row_data = [str(entry.get(col_name, '')) for col_name in self.column_info.keys()]
76
- table.add_row(*row_data)
86
+ if entry:
87
+ row_data = [str(entry.get(col_name, '')) for col_name in self.column_info.keys()]
88
+ table.add_row(*row_data)
77
89
 
78
90
  self.console.print(table)
79
91
 
@@ -125,6 +137,14 @@ class TVShowManager:
125
137
  Returns:
126
138
  str: Last command executed before breaking out of the loop.
127
139
  """
140
+ if not self.tv_shows:
141
+ logging.error("Error: No data available for display.")
142
+ return ""
143
+
144
+ if not self.column_info:
145
+ logging.error("Error: Columns not configured.")
146
+ return ""
147
+
128
148
  total_items = len(self.tv_shows)
129
149
  last_command = ""
130
150
  is_telegram = config_manager.get_bool('DEFAULT', 'telegram_bot')
@@ -132,9 +152,17 @@ class TVShowManager:
132
152
 
133
153
  while True:
134
154
  start_message()
135
- self.display_data(self.tv_shows[self.slice_start:self.slice_end])
155
+
156
+ # Check and adjust slice indices if out of bounds
157
+ current_slice = self.tv_shows[self.slice_start:self.slice_end]
158
+ if not current_slice and total_items > 0:
159
+ self.slice_start = 0
160
+ self.slice_end = min(self.step, total_items)
161
+ current_slice = self.tv_shows[self.slice_start:self.slice_end]
162
+
163
+ self.display_data(current_slice)
136
164
 
137
- # Find research function from call stack
165
+ # Resto del codice rimane uguale...
138
166
  research_func = next((
139
167
  f for f in get_call_stack()
140
168
  if f['function'] == 'search' and f['script'] == '__init__.py'
@@ -225,5 +253,9 @@ class TVShowManager:
225
253
  return last_command
226
254
 
227
255
  def clear(self) -> None:
228
- """Clear all TV shows data."""
229
- self.tv_shows = []
256
+ """
257
+ Clear all TV shows data.
258
+ """
259
+ self.tv_shows = []
260
+ self.slice_start = 0
261
+ self.slice_end = self.step
StreamingCommunity/run.py CHANGED
@@ -360,15 +360,24 @@ def setup_argument_parser(search_functions):
360
360
 
361
361
  # Add arguments
362
362
  parser.add_argument("script_id", nargs="?", default="unknown", help="ID dello script")
363
- parser.add_argument('--add_siteName', type=bool, help='Enable/disable adding site name to file name')
363
+ parser.add_argument('-s', '--search', default=None, help='Search terms')
364
+ parser.add_argument('--global', action='store_true', help='Global search across sites')
364
365
  parser.add_argument('--not_close', type=bool, help='Keep console open after execution')
365
- parser.add_argument('--default_video_worker', type=int, help='Video workers for M3U8 download (default: 12)')
366
- parser.add_argument('--default_audio_worker', type=int, help='Audio workers for M3U8 download (default: 12)')
366
+
367
367
  parser.add_argument('--specific_list_audio', type=str, help='Audio languages (e.g., ita,eng)')
368
368
  parser.add_argument('--specific_list_subtitles', type=str, help='Subtitle languages (e.g., eng,spa)')
369
- parser.add_argument('--global', action='store_true', help='Global search across sites')
369
+ parser.add_argument('--force_resolution', type=str, choices=["Best", "Worst", "720p", "1080p", "480p", "360p"],
370
+ help='Choose video resolution:\n'
371
+ ' "Best": Highest available resolution\n'
372
+ ' "Worst": Lowest available resolution\n'
373
+ ' "720p": Force 720p resolution\n'
374
+ ' Specific resolutions:\n'
375
+ ' - 1080p (1920x1080)\n'
376
+ ' - 720p (1280x720)\n'
377
+ ' - 480p (640x480)\n'
378
+ ' - 360p (640x360)')
379
+
370
380
  parser.add_argument('--category', type=int, help='Category (1: anime, 2: film_&_serie, 3: serie, 4: torrent)')
371
- parser.add_argument('-s', '--search', default=None, help='Search terms')
372
381
  parser.add_argument('--auto-first', action='store_true', help='Auto-download first result (use with --site and --search)')
373
382
  parser.add_argument('--site', type=str, help='Site by name or index')
374
383
 
@@ -380,10 +389,8 @@ def apply_config_updates(args):
380
389
  config_updates = {}
381
390
 
382
391
  arg_mappings = {
383
- 'add_siteName': 'DEFAULT.add_siteName',
384
392
  'not_close': 'DEFAULT.not_close',
385
- 'default_video_worker': 'M3U8_DOWNLOAD.default_video_worker',
386
- 'default_audio_worker': 'M3U8_DOWNLOAD.default_audio_worker'
393
+ 'force_resolution': 'M3U8_CONVERSION.force_resolution',
387
394
  }
388
395
 
389
396
  for arg_name, config_key in arg_mappings.items():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: StreamingCommunity
3
- Version: 3.3.5
3
+ Version: 3.3.6
4
4
  Home-page: https://github.com/Arrowar/StreamingCommunity
5
5
  Author: Arrowar
6
6
  Project-URL: Bug Reports, https://github.com/Arrowar/StreamingCommunity/issues
@@ -45,7 +45,9 @@ Dynamic: requires-python
45
45
  [![License](https://img.shields.io/github/license/Arrowar/StreamingCommunity?logo=gnu&logoColor=white&labelColor=2d3748&color=e53e3e&style=for-the-badge)](https://github.com/Arrowar/StreamingCommunity/blob/main/LICENSE)
46
46
 
47
47
  ## 💝 Support the Project
48
- [![Donate PayPal](https://img.shields.io/badge/💳_Donate-PayPal-00457C?style=for-the-badge&logo=paypal&logoColor=white&labelColor=2d3748)](https://www.paypal.com/donate/?hosted_button_id=UXTWMT8P6HE2C)
48
+ <a href='https://ko-fi.com/E1E11LVC83' target='_blank'>
49
+ <img height='36' style='border:0px;height:36px;' src='https://storage.ko-fi.com/cdn/kofi4.png?v=6' border='0' alt='Buy Me a Coffee at ko-fi.com' />
50
+ </a>
49
51
 
50
52
  ## 🚀 Download & Install
51
53
  [![Windows](https://img.shields.io/badge/🪟_Windows-0078D4?style=for-the-badge&logo=windows&logoColor=white&labelColor=2d3748)](https://github.com/Arrowar/StreamingCommunity/releases/latest/download/StreamingCommunity_win.exe)
@@ -62,50 +64,44 @@ Dynamic: requires-python
62
64
  ---
63
65
 
64
66
  ## 📖 Table of Contents
65
- - [Installation](#manual-installation)
66
- - [Quick Start](#quick-start)
67
- - [Downloaders](#downloaders)
68
- - [Configuration](#configuration)
69
- - [Login](.github/.site/login.md)
70
- - [Usage Examples](#usage-examples)
71
- - [Global Search](#global-search)
72
- - [Advanced Features](#advanced-options)
73
- - [Deployment](#docker)
74
- - [Tutorials](#tutorials)
75
- - [Related Projects](#useful-project)
67
+ - ⚙️ [Installation](#installation)
68
+ - 🚀 [Quick Start](#quick-start)
69
+ - 📥 [Downloaders](#downloaders)
70
+ - 🛠️ [Configuration](#configuration)
71
+ - 🔐 [Login](.github/.site/login.md)
72
+ - 🌐 [Domain](https://arrowar.github.io/StreamingCommunity)
73
+ - 💡 [Usage Examples](#usage-examples)
74
+ - 🔍 [Global Search](#global-search)
75
+ - 🧩 [Advanced Features](#advanced-options)
76
+ - 🐳 [Deployment](#docker)
77
+ - 🎓 [Tutorials](#tutorials)
78
+ - 🔗 [Related Projects](#useful-project)
76
79
 
77
80
  ---
78
81
 
79
- ## Manual Installation
82
+ ## Installation
80
83
 
81
- <summary>⚙️ Python Dependencies</summary>
84
+ ### Prerequisites
85
+ Make sure you have Python installed on your system:
86
+ - **Windows**: Download from [python.org](https://python.org) or install via Microsoft Store
87
+ - **Linux**: `sudo apt install python3 python3-pip` (Ubuntu/Debian) or equivalent for your distro
88
+ - **MacOS**: `brew install python3` or download from [python.org](https://python.org)
82
89
 
83
- #### On Windows:
84
-
85
- ```powershell
90
+ ### Dependencies
91
+ ```bash
92
+ # Windows
86
93
  pip install -r requirements.txt
87
- ```
88
94
 
89
- #### On Linux/MacOS:
90
-
91
- ```bash
95
+ # Linux/MacOS
92
96
  pip3 install -r requirements.txt
93
- # or alternatively:
94
- python3 -m pip install -r requirements.txt
95
97
  ```
96
98
 
97
- <detail>
98
- <summary> Update </summary>
99
-
100
- ### On Windows:
101
-
102
- ```powershell
99
+ ### Update
100
+ ```bash
101
+ # Windows
103
102
  python update.py
104
- ```
105
103
 
106
- ### On Linux/MacOS:
107
-
108
- ```bash
104
+ # Linux/MacOS
109
105
  python3 update.py
110
106
  ```
111
107
 
@@ -113,26 +109,17 @@ python3 update.py
113
109
 
114
110
  ## Quick Start
115
111
 
116
- After installing via pip, just run:
112
+ **Via pip installation:**
117
113
  ```bash
118
114
  StreamingCommunity
119
115
  ```
120
- to start the application.
121
-
122
- Run the script:
123
- ```bash
124
- python run_streaming.py
125
- ```
126
116
 
127
117
  **Manual execution:**
128
-
129
- **Windows:**
130
- ```powershell
118
+ ```bash
119
+ # Windows
131
120
  python test_run.py
132
- ```
133
121
 
134
- **Linux/MacOS:**
135
- ```bash
122
+ # Linux/MacOS
136
123
  python3 test_run.py
137
124
  ```
138
125
 
@@ -155,7 +142,7 @@ downloader = HLS_Downloader(
155
142
  downloader.download()
156
143
  ```
157
144
 
158
- See [HLS example](./Test/EasyDownload//HLS.py) for complete usage.
145
+ See [HLS example](./Test/Downloads//HLS.py) for complete usage.
159
146
 
160
147
  <summary>📽️ MP4</summary>
161
148
 
@@ -172,7 +159,7 @@ downloader = MP4_downloader(
172
159
  downloader.download()
173
160
  ```
174
161
 
175
- See [MP4 example](./Test/EasyDownload/MP4.py) for complete usage.
162
+ See [MP4 example](./Test/Downloads/MP4.py) for complete usage.
176
163
 
177
164
  <summary>🧲 TOR</summary>
178
165
 
@@ -188,7 +175,7 @@ client.add_magnet_link("magnet:?xt=urn:btih:example_hash&dn=example_name", save_
188
175
  client.start_download()
189
176
  ```
190
177
 
191
- See [Torrent example](./Test/EasyDownload/TOR.py) for complete usage.
178
+ See [Torrent example](./Test/Downloads/TOR.py) for complete usage.
192
179
 
193
180
  <summary>🎞️ DASH</summary>
194
181
 
@@ -210,7 +197,7 @@ if dash_process.download_and_decrypt():
210
197
  dash_process.get_status()
211
198
  ```
212
199
 
213
- See [DASH example](./Test/EasyDownload/DASH.py) for complete usage.
200
+ See [DASH example](./Test/Downloads/DASH.py) for complete usage.
214
201
 
215
202
  ---
216
203