StreamingCommunity 3.0.6__py3-none-any.whl → 3.0.8__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 (37) hide show
  1. StreamingCommunity/Api/Player/maxstream.py +141 -0
  2. StreamingCommunity/Api/Player/vixcloud.py +5 -3
  3. StreamingCommunity/Api/Site/1337xx/__init__.py +2 -2
  4. StreamingCommunity/Api/Site/altadefinizione/__init__.py +2 -2
  5. StreamingCommunity/Api/Site/altadefinizione/film.py +15 -35
  6. StreamingCommunity/Api/Site/animeunity/__init__.py +1 -1
  7. StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py +21 -23
  8. StreamingCommunity/Api/Site/animeworld/__init__.py +1 -1
  9. StreamingCommunity/Api/Site/animeworld/util/ScrapeSerie.py +2 -1
  10. StreamingCommunity/Api/Site/cb01new/__init__.py +72 -0
  11. StreamingCommunity/Api/Site/cb01new/film.py +62 -0
  12. StreamingCommunity/Api/Site/cb01new/site.py +78 -0
  13. StreamingCommunity/Api/Site/guardaserie/__init__.py +1 -1
  14. StreamingCommunity/Api/Site/raiplay/__init__.py +2 -2
  15. StreamingCommunity/Api/Site/raiplay/site.py +26 -94
  16. StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py +37 -17
  17. StreamingCommunity/Api/Site/streamingcommunity/__init__.py +87 -51
  18. StreamingCommunity/Api/Site/streamingcommunity/film.py +2 -2
  19. StreamingCommunity/Api/Site/streamingcommunity/series.py +4 -4
  20. StreamingCommunity/Api/Site/streamingcommunity/site.py +7 -4
  21. StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +6 -3
  22. StreamingCommunity/Api/Site/streamingwatch/__init__.py +10 -4
  23. StreamingCommunity/Api/Site/streamingwatch/site.py +12 -5
  24. StreamingCommunity/Api/Template/site.py +103 -58
  25. StreamingCommunity/Lib/Proxies/proxy.py +14 -174
  26. StreamingCommunity/TelegramHelp/config.json +62 -0
  27. StreamingCommunity/TelegramHelp/telegram_bot.py +4 -0
  28. StreamingCommunity/Upload/version.py +1 -1
  29. StreamingCommunity/Util/config_json.py +0 -4
  30. StreamingCommunity/Util/os.py +0 -6
  31. StreamingCommunity/run.py +2 -2
  32. {streamingcommunity-3.0.6.dist-info → streamingcommunity-3.0.8.dist-info}/METADATA +31 -13
  33. {streamingcommunity-3.0.6.dist-info → streamingcommunity-3.0.8.dist-info}/RECORD +37 -32
  34. {streamingcommunity-3.0.6.dist-info → streamingcommunity-3.0.8.dist-info}/WHEEL +1 -1
  35. {streamingcommunity-3.0.6.dist-info → streamingcommunity-3.0.8.dist-info}/entry_points.txt +0 -0
  36. {streamingcommunity-3.0.6.dist-info → streamingcommunity-3.0.8.dist-info}/licenses/LICENSE +0 -0
  37. {streamingcommunity-3.0.6.dist-info → streamingcommunity-3.0.8.dist-info}/top_level.txt +0 -0
@@ -7,78 +7,123 @@ import sys
7
7
  from rich.console import Console
8
8
 
9
9
 
10
+ # Internal utilities
11
+ from StreamingCommunity.Api.Template.config_loader import site_constant
12
+ from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
13
+
10
14
  # Variable
11
15
  console = Console()
12
16
  available_colors = ['red', 'magenta', 'yellow', 'cyan', 'green', 'blue', 'white']
13
17
  column_to_hide = ['Slug', 'Sub_ita', 'Last_air_date', 'Seasons_count', 'Url', 'Image', 'Path_id']
14
18
 
15
19
 
16
- def get_select_title(table_show_manager, media_search_manager):
20
+ def get_select_title(table_show_manager, media_search_manager, num_results_available):
17
21
  """
18
22
  Display a selection of titles and prompt the user to choose one.
23
+ Handles both console and Telegram bot input.
24
+
25
+ Parameters:
26
+ table_show_manager: Manager for console table display.
27
+ media_search_manager: Manager holding the list of media items.
28
+ num_results_available (int): The number of media items available for selection.
19
29
 
20
30
  Returns:
21
- MediaItem: The selected media item.
31
+ MediaItem: The selected media item, or None if no selection is made or an error occurs.
22
32
  """
23
- # Determine column_info dynamically for (search site)
24
33
  if not media_search_manager.media_list:
25
- console.print("\n[red]No media items available.")
34
+
35
+ # console.print("\n[red]No media items available.")
26
36
  return None
27
-
28
- # Example of available colors for columns
29
- available_colors = ['red', 'magenta', 'yellow', 'cyan', 'green', 'blue', 'white']
30
-
31
- # Retrieve the keys of the first media item as column headers
32
- first_media_item = media_search_manager.media_list[0]
33
- column_info = {"Index": {'color': available_colors[0]}} # Always include Index with a fixed color
34
-
35
- # Assign colors to the remaining keys dynamically
36
- color_index = 1
37
- for key in first_media_item.__dict__.keys():
38
-
39
- if key.capitalize() in column_to_hide:
40
- continue
41
-
42
- if key in ('id', 'type', 'name', 'score'): # Custom prioritization of colors
43
- if key == 'type':
44
- column_info["Type"] = {'color': 'yellow'}
45
- elif key == 'name':
46
- column_info["Name"] = {'color': 'magenta'}
47
- elif key == 'score':
48
- column_info["Score"] = {'color': 'cyan'}
49
-
50
- else:
51
- column_info[key.capitalize()] = {'color': available_colors[color_index % len(available_colors)]}
52
- color_index += 1
53
-
54
- table_show_manager.add_column(column_info)
55
-
56
- # Populate the table with title information
57
- for i, media in enumerate(media_search_manager.media_list):
58
- media_dict = {'Index': str(i)}
59
37
 
38
+ if site_constant.TELEGRAM_BOT:
39
+ bot = get_bot_instance()
40
+ prompt_message = f"Inserisci il numero del titolo che vuoi selezionare (da 0 a {num_results_available - 1}):"
41
+
42
+ user_input_str = bot.ask(
43
+ "select_title_from_list_number",
44
+ prompt_message,
45
+ None
46
+ )
47
+
48
+ if user_input_str is None:
49
+ bot.send_message("Timeout: nessuna selezione ricevuta.", None)
50
+ return None
51
+
52
+ try:
53
+ chosen_index = int(user_input_str)
54
+ if 0 <= chosen_index < num_results_available:
55
+ selected_item = media_search_manager.get(chosen_index)
56
+ if selected_item:
57
+ return selected_item
58
+
59
+ else:
60
+ bot.send_message(f"Errore interno: Impossibile recuperare il titolo con indice {chosen_index}.", None)
61
+ return None
62
+ else:
63
+ bot.send_message(f"Selezione '{chosen_index}' non valida. Inserisci un numero compreso tra 0 e {num_results_available - 1}.", None)
64
+ return None
65
+
66
+ except ValueError:
67
+ bot.send_message(f"Input '{user_input_str}' non valido. Devi inserire un numero.", None)
68
+ return None
69
+
70
+ except Exception as e:
71
+ bot.send_message(f"Si è verificato un errore durante la selezione: {e}", None)
72
+ return None
73
+
74
+ else:
75
+
76
+ # Logica originale per la console
77
+ if not media_search_manager.media_list:
78
+ console.print("\n[red]No media items available.")
79
+ return None
80
+
81
+ first_media_item = media_search_manager.media_list[0]
82
+ column_info = {"Index": {'color': available_colors[0]}}
83
+
84
+ color_index = 1
60
85
  for key in first_media_item.__dict__.keys():
61
86
  if key.capitalize() in column_to_hide:
62
87
  continue
63
-
64
- # Ensure all values are strings for rich add table
65
- media_dict[key.capitalize()] = str(getattr(media, key))
66
-
67
- table_show_manager.add_tv_show(media_dict)
68
-
69
- # Run the table and handle user input
70
- last_command = table_show_manager.run(force_int_input=True, max_int_input=len(media_search_manager.media_list))
71
- table_show_manager.clear()
72
-
73
- # Handle user's quit command
74
- if last_command == "q" or last_command == "quit":
75
- console.print("\n[red]Quit ...")
76
- sys.exit(0)
77
-
78
- # Check if the selected index is within range
79
- if 0 <= int(last_command) < len(media_search_manager.media_list):
80
- return media_search_manager.get(int(last_command))
81
-
82
- else:
83
- console.print("\n[red]Wrong index")
84
- sys.exit(0)
88
+ if key in ('id', 'type', 'name', 'score'):
89
+ if key == 'type': column_info["Type"] = {'color': 'yellow'}
90
+ elif key == 'name': column_info["Name"] = {'color': 'magenta'}
91
+ elif key == 'score': column_info["Score"] = {'color': 'cyan'}
92
+ else:
93
+ column_info[key.capitalize()] = {'color': available_colors[color_index % len(available_colors)]}
94
+ color_index += 1
95
+
96
+ table_show_manager.clear()
97
+ table_show_manager.add_column(column_info)
98
+
99
+ for i, media in enumerate(media_search_manager.media_list):
100
+ media_dict = {'Index': str(i)}
101
+ for key in first_media_item.__dict__.keys():
102
+ if key.capitalize() in column_to_hide:
103
+ continue
104
+ media_dict[key.capitalize()] = str(getattr(media, key))
105
+ table_show_manager.add_tv_show(media_dict)
106
+
107
+ last_command_str = table_show_manager.run(force_int_input=True, max_int_input=len(media_search_manager.media_list))
108
+ table_show_manager.clear()
109
+
110
+ if last_command_str is None or last_command_str.lower() in ["q", "quit"]:
111
+ console.print("\n[red]Selezione annullata o uscita.")
112
+ return None
113
+
114
+ try:
115
+
116
+ selected_index = int(last_command_str)
117
+
118
+ if 0 <= selected_index < len(media_search_manager.media_list):
119
+ return media_search_manager.get(selected_index)
120
+
121
+ else:
122
+ console.print("\n[red]Indice errato o non valido.")
123
+ # sys.exit(0)
124
+ return None
125
+
126
+ except ValueError:
127
+ console.print("\n[red]Input non numerico ricevuto dalla tabella.")
128
+ # sys.exit(0)
129
+ return None
@@ -1,20 +1,15 @@
1
1
  # 29.04.25
2
2
 
3
- import os
4
3
  import sys
5
4
  import time
6
- import json
7
5
  import signal
8
6
  import warnings
9
7
  warnings.filterwarnings("ignore", category=UserWarning)
10
- from datetime import datetime, timedelta
11
- from concurrent.futures import ThreadPoolExecutor, as_completed
12
8
 
13
9
 
14
10
  # External library
15
11
  import httpx
16
12
  from rich import print
17
- from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn
18
13
 
19
14
 
20
15
  # Internal utilities
@@ -27,118 +22,18 @@ MAX_TIMEOUT = config_manager.get_int("REQUESTS", "timeout")
27
22
 
28
23
 
29
24
  class ProxyFinder:
30
- def __init__(self, url, timeout_threshold: float = 7.0, max_proxies: int = 150, max_workers: int = 12):
25
+ def __init__(self, url, timeout_threshold: float = 7.0):
31
26
  self.url = url
32
27
  self.timeout_threshold = timeout_threshold
33
- self.max_proxies = max_proxies
34
- self.max_workers = max_workers
35
- self.found_proxy = None
36
28
  self.shutdown_flag = False
37
- self.json_file = os.path.join(os.path.dirname(__file__), 'working_proxies.json')
38
29
  signal.signal(signal.SIGINT, self._handle_interrupt)
39
30
 
40
- def load_saved_proxies(self) -> tuple:
41
- """Load saved proxies if they're not expired (2 hours old)"""
42
- try:
43
- if not os.path.exists(self.json_file):
44
- return None, None
45
-
46
- with open(self.json_file, 'r') as f:
47
- data = json.load(f)
48
-
49
- if not data.get('proxies') or not data.get('last_update'):
50
- return None, None
51
-
52
- last_update = datetime.fromisoformat(data['last_update'])
53
- if datetime.now() - last_update > timedelta(hours=2):
54
- return None, None
55
-
56
- return data['proxies'], last_update
57
- except Exception:
58
- return None, None
59
-
60
- def save_working_proxy(self, proxy: str, response_time: float):
61
- """Save working proxy to JSON file"""
62
- data = {
63
- 'proxies': [{'proxy': proxy, 'response_time': response_time}],
64
- 'last_update': datetime.now().isoformat()
65
- }
66
- try:
67
- with open(self.json_file, 'w') as f:
68
- json.dump(data, f, indent=4)
69
- except Exception as e:
70
- print(f"[bold red]Error saving proxy:[/bold red] {str(e)}")
71
-
72
- def fetch_geonode(self) -> list:
73
- proxies = []
74
- try:
75
- response = httpx.get(
76
- "https://proxylist.geonode.com/api/proxy-list?protocols=http%2Chttps&limit=100&page=1&sort_by=speed&sort_type=asc",
77
- headers=get_headers(),
78
- timeout=MAX_TIMEOUT
79
- )
80
- data = response.json()
81
- proxies = [(f"http://{p['ip']}:{p['port']}", "Geonode") for p in data.get('data', [])]
82
-
83
- except Exception as e:
84
- print(f"[bold red]Error in Geonode:[/bold red] {str(e)[:100]}")
85
-
86
- return proxies
87
-
88
- def fetch_proxyscrape(self) -> list:
89
- proxies = []
90
- try:
91
- response = httpx.get(
92
- "https://api.proxyscrape.com/v4/free-proxy-list/get?request=get_proxies&protocol=http&skip=0&proxy_format=protocolipport&format=json&limit=100&timeout=1000",
93
- headers=get_headers(),
94
- timeout=MAX_TIMEOUT
95
- )
96
- data = response.json()
97
- if 'proxies' in data and isinstance(data['proxies'], list):
98
- proxies = [(proxy_data['proxy'], "ProxyScrape") for proxy_data in data['proxies'] if 'proxy' in proxy_data]
99
-
100
- except Exception as e:
101
- print(f"[bold red]Error in ProxyScrape:[/bold red] {str(e)[:100]}")
102
-
103
- return proxies
104
-
105
- def fetch_proxies_from_sources(self) -> list:
106
- #print("[cyan]Fetching proxies from sources...[/cyan]")
107
- with ThreadPoolExecutor(max_workers=3) as executor:
108
- proxyscrape_future = executor.submit(self.fetch_proxyscrape)
109
- geonode_future = executor.submit(self.fetch_geonode)
110
-
111
- sources_proxies = {}
112
-
113
- try:
114
- proxyscrape_result = proxyscrape_future.result()
115
- sources_proxies["proxyscrape"] = proxyscrape_result[:int(self.max_proxies/2)]
116
- except Exception as e:
117
- print(f"[bold red]Error fetching from proxyscrape:[/bold red] {str(e)[:100]}")
118
- sources_proxies["proxyscrape"] = []
119
-
120
- try:
121
- geonode_result = geonode_future.result()
122
- sources_proxies["geonode"] = geonode_result[:int(self.max_proxies/2)]
123
- except Exception as e:
124
- print(f"[bold red]Error fetching from geonode:[/bold red] {str(e)[:100]}")
125
- sources_proxies["geonode"] = []
126
-
127
- merged_proxies = []
128
-
129
- if "proxyscrape" in sources_proxies:
130
- merged_proxies.extend(sources_proxies["proxyscrape"])
131
-
132
- if "geonode" in sources_proxies:
133
- merged_proxies.extend(sources_proxies["geonode"])
134
-
135
- proxy_list = merged_proxies[:self.max_proxies]
136
- return proxy_list
137
-
138
31
  def _test_single_request(self, proxy_info: tuple) -> tuple:
139
32
  proxy, source = proxy_info
140
33
  try:
141
34
  start = time.time()
35
+ print(f"[yellow]Testing proxy for URL: {self.url}...")
36
+
142
37
  with httpx.Client(proxy=proxy, timeout=self.timeout_threshold) as client:
143
38
  response = client.get(self.url, headers=get_headers())
144
39
  if response.status_code == 200:
@@ -161,72 +56,17 @@ class ProxyFinder:
161
56
  return (proxy, success2 and time2 <= self.timeout_threshold, avg_time, text1, source)
162
57
 
163
58
  def _handle_interrupt(self, sig, frame):
164
- print("\n[bold yellow]Received keyboard interrupt. Terminating...[/bold yellow]")
59
+ print("\n[red]Received keyboard interrupt. Terminating...")
165
60
  self.shutdown_flag = True
166
61
  sys.exit(0)
167
62
 
168
- def find_fast_proxy(self) -> tuple:
169
- saved_proxies, last_update = self.load_saved_proxies()
170
- if saved_proxies:
171
- print("[cyan]Testing saved proxy...[/cyan]")
172
- for proxy_data in saved_proxies:
173
- result = self.test_proxy((proxy_data['proxy'], 'cached'))
174
- if result[1]:
175
- return proxy_data['proxy'], result[3], result[2]
176
- else:
177
- print(f"[red]Saved proxy {proxy_data['proxy']} failed - response time: {result[2]:.2f}s[/red]")
178
-
179
- proxies = self.fetch_proxies_from_sources()
180
- if not proxies:
181
- print("[bold red]No proxies fetched to test.[/bold red]")
182
- return (None, None, None)
183
-
184
- found_proxy = None
185
- response_text = None
186
- source = None
187
- failed_count = 0
188
- success_count = 0
189
-
190
- #print(f"[cyan]Testing {len(proxies)} proxies...[/cyan]")
191
- with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
192
- futures = {executor.submit(self.test_proxy, p): p for p in proxies}
193
- with Progress(
194
- SpinnerColumn(),
195
- TextColumn("[progress.description]{task.description}"),
196
- BarColumn(),
197
- TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
198
- TextColumn("[cyan]{task.fields[success]}[/cyan]/[red]{task.fields[failed]}[/red]"),
199
- TimeRemainingColumn(),
200
- ) as progress:
201
- task = progress.add_task(
202
- "[cyan]Testing Proxies",
203
- total=len(futures),
204
- success=success_count,
205
- failed=failed_count
206
- )
207
-
208
- for future in as_completed(futures):
209
- if self.shutdown_flag:
210
- break
211
-
212
- try:
213
- proxy, success, elapsed, response, proxy_source = future.result()
214
- if success:
215
- success_count += 1
216
- print(f"[bold green]Found valid proxy:[/bold green] {proxy} ({elapsed:.2f}s)")
217
- found_proxy = proxy
218
- response_text = response
219
- self.save_working_proxy(proxy, elapsed)
220
- self.shutdown_flag = True
221
- break
222
- else:
223
- failed_count += 1
224
- except Exception:
225
- failed_count += 1
226
-
227
- progress.update(task, advance=1, success=success_count, failed=failed_count)
228
-
229
- if not found_proxy:
230
- print("[bold red]No working proxies found[/bold red]")
231
-
232
- return (found_proxy, response_text, source)
63
+ def find_fast_proxy(self) -> str:
64
+ try:
65
+ proxy_config = config_manager.get("REQUESTS", "proxy")
66
+ if proxy_config and isinstance(proxy_config, dict) and 'http' in proxy_config:
67
+ print("[cyan]Using configured proxy from config.json...[/cyan]")
68
+ return proxy_config['http']
69
+ except Exception as e:
70
+ print(f"[red]Error getting configured proxy: {str(e)}[/red]")
71
+
72
+ return None
@@ -0,0 +1,62 @@
1
+ {
2
+ "DEFAULT": {
3
+ "debug": false,
4
+ "show_message": true,
5
+ "clean_console": true,
6
+ "show_trending": true,
7
+ "use_api": true,
8
+ "not_close": false,
9
+ "telegram_bot": true,
10
+ "download_site_data": true,
11
+ "validate_github_config": true
12
+ },
13
+ "OUT_FOLDER": {
14
+ "root_path": "/mnt/data/media/",
15
+ "movie_folder_name": "films",
16
+ "serie_folder_name": "serie_tv",
17
+ "anime_folder_name": "Anime",
18
+ "map_episode_name": "E%(episode)_%(episode_name)",
19
+ "add_siteName": false
20
+ },
21
+ "QBIT_CONFIG": {
22
+ "host": "192.168.1.51",
23
+ "port": "6666",
24
+ "user": "admin",
25
+ "pass": "adminadmin"
26
+ },
27
+ "M3U8_DOWNLOAD": {
28
+ "tqdm_delay": 0.01,
29
+ "default_video_workser": 12,
30
+ "default_audio_workser": 12,
31
+ "segment_timeout": 8,
32
+ "download_audio": true,
33
+ "merge_audio": true,
34
+ "specific_list_audio": [
35
+ "ita"
36
+ ],
37
+ "download_subtitle": true,
38
+ "merge_subs": true,
39
+ "specific_list_subtitles": [
40
+ "ita",
41
+ "eng"
42
+ ],
43
+ "cleanup_tmp_folder": true
44
+ },
45
+ "M3U8_CONVERSION": {
46
+ "use_codec": false,
47
+ "use_vcodec": true,
48
+ "use_acodec": true,
49
+ "use_bitrate": true,
50
+ "use_gpu": false,
51
+ "default_preset": "ultrafast"
52
+ },
53
+ "M3U8_PARSER": {
54
+ "force_resolution": "Best",
55
+ "get_only_link": false
56
+ },
57
+ "REQUESTS": {
58
+ "verify": false,
59
+ "timeout": 20,
60
+ "max_retry": 8
61
+ }
62
+ }
@@ -575,6 +575,10 @@ class TelegramBot:
575
575
  cleaned_output = cleaned_output.replace(
576
576
  "\n\n", "\n"
577
577
  ) # Rimuovi newline multipli
578
+
579
+ # Inizializza le variabili
580
+ cleaned_output_0 = None # o ""
581
+ cleaned_output_1 = None # o ""
578
582
 
579
583
  # Dentro cleaned_output c'è una stringa recupero quello che si trova tra ## ##
580
584
  download_section = re.search(r"##(.*?)##", cleaned_output, re.DOTALL)
@@ -1,5 +1,5 @@
1
1
  __title__ = 'StreamingCommunity'
2
- __version__ = '3.0.6'
2
+ __version__ = '3.0.8'
3
3
  __author__ = 'Arrowar'
4
4
  __description__ = 'A command-line program to download film'
5
5
  __copyright__ = 'Copyright 2024'
@@ -39,9 +39,6 @@ class ConfigManager:
39
39
 
40
40
  # Get the actual path of the module file
41
41
  current_file_path = os.path.abspath(__file__)
42
- # Navigate upwards to find the project root
43
- # Assuming this file is in a package structure like StreamingCommunity/Util/config_json.py
44
- # We need to go up 2 levels to reach the project root
45
42
  base_path = os.path.dirname(os.path.dirname(os.path.dirname(current_file_path)))
46
43
 
47
44
  # Initialize file paths
@@ -562,7 +559,6 @@ class ConfigManager:
562
559
  return section in config_source
563
560
 
564
561
 
565
- # Helper function to check the platform
566
562
  def get_use_large_bar():
567
563
  """
568
564
  Determine if the large bar feature should be enabled.
@@ -296,12 +296,6 @@ class InternManager():
296
296
  "Google": ["8.8.8.8", "8.8.4.4"],
297
297
  "OpenDNS": ["208.67.222.222", "208.67.220.220"],
298
298
  "Quad9": ["9.9.9.9", "149.112.112.112"],
299
- "AdGuard": ["94.140.14.14", "94.140.15.15"],
300
- "Comodo": ["8.26.56.26", "8.20.247.20"],
301
- "Level3": ["209.244.0.3", "209.244.0.4"],
302
- "Norton": ["199.85.126.10", "199.85.127.10"],
303
- "CleanBrowsing": ["185.228.168.9", "185.228.169.9"],
304
- "Yandex": ["77.88.8.8", "77.88.8.1"]
305
299
  }
306
300
 
307
301
  try:
StreamingCommunity/run.py CHANGED
@@ -219,8 +219,8 @@ def main(script_id = 0):
219
219
  console.print("[blue]• Quad9 (9.9.9.9) 'https://docs.quad9.net/Setup_Guides/Windows/Windows_10/'")
220
220
  console.print("\n[yellow]⚠️ The program will not work until you configure your DNS settings.")
221
221
 
222
- time.sleep(1)
223
- msg.ask("[yellow]Press Enter to exit...")
222
+ time.sleep(2)
223
+ msg.ask("[yellow]Press Enter to continue ...")
224
224
 
225
225
  # Load search functions
226
226
  search_functions = load_search_functions()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: StreamingCommunity
3
- Version: 3.0.6
3
+ Version: 3.0.8
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
@@ -35,7 +35,7 @@ Dynamic: requires-dist
35
35
  Dynamic: requires-python
36
36
 
37
37
  <p align="center">
38
- <img src="https://i.ibb.co/v6RnT0wY/s2.jpg" alt="Project Logo" width="600"/>
38
+ <img src="https://i.ibb.co/v6RnT0wY/s2.jpg" alt="Project Logo" width="450"/>
39
39
  </p>
40
40
 
41
41
  <p align="center">
@@ -506,7 +506,11 @@ To enable qBittorrent integration, follow the setup guide [here](https://github.
506
506
  "REQUESTS": {
507
507
  "verify": false,
508
508
  "timeout": 20,
509
- "max_retry": 8
509
+ "max_retry": 8,
510
+ "proxy": {
511
+ "http": "http://username:password@host:port",
512
+ "https": "https://username:password@host:port"
513
+ }
510
514
  }
511
515
  }
512
516
  ```
@@ -514,6 +518,22 @@ To enable qBittorrent integration, follow the setup guide [here](https://github.
514
518
  - `verify`: Verifies SSL certificates
515
519
  - `timeout`: Maximum timeout (in seconds) for each request
516
520
  - `max_retry`: Number of retry attempts per segment during M3U8 index download
521
+ - `proxy`: Proxy configuration for HTTP/HTTPS requests
522
+ * Set to empty string `""` to disable proxies (default)
523
+ * Example with authentication:
524
+ ```json
525
+ "proxy": {
526
+ "http": "http://username:password@host:port",
527
+ "https": "https://username:password@host:port"
528
+ }
529
+ ```
530
+ * Example without authentication:
531
+ ```json
532
+ "proxy": {
533
+ "http": "http://host:port",
534
+ "https": "https://host:port"
535
+ }
536
+ ```
517
537
  </details>
518
538
 
519
539
  <details>
@@ -764,26 +784,24 @@ The `run-container` command mounts also the `config.json` file, so any change to
764
784
  The bot was created to replace terminal commands and allow interaction via Telegram. Each download runs within a screen session, enabling multiple downloads to run simultaneously.
765
785
 
766
786
  To run the bot in the background, simply start it inside a screen session and then press Ctrl + A, followed by D, to detach from the session without stopping the bot.
767
- </details>
768
-
769
- <details>
770
- <summary>🤖 Bot Commands</summary>
771
787
 
772
788
  Command Functions:
773
789
 
774
790
  🔹 /start – Starts a new search for a download. This command performs the same operations as manually running the script in the terminal with test_run.py.
775
791
 
776
792
  🔹 /list – Displays the status of active downloads, with options to:
777
- - Stop an incorrect download using /stop <ID>
778
- - View the real-time output of a download using /screen <ID>
793
+
794
+ Stop an incorrect download using /stop <ID>.
795
+
796
+ View the real-time output of a download using /screen <ID>.
779
797
 
780
798
  ⚠ Warning: If a download is interrupted, incomplete files may remain in the folder specified in config.json. These files must be deleted manually to avoid storage or management issues.
781
- </details>
782
799
 
783
- <details>
784
- <summary>🔧 Environment Setup</summary>
800
+ 🛠 Configuration: Currently, the bot's settings are stored in the config.json file, which is located in the same directory as the telegram_bot.py script.
801
+
802
+ ## .env Example:
785
803
 
786
- Create an `.env` file with:
804
+ You need to create an .env file and enter your Telegram token and user ID to authorize only one user to use it
787
805
 
788
806
  ```
789
807
  TOKEN_TELEGRAM=IlTuo2131TOKEN$12D3Telegram