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.
- StreamingCommunity/Api/Player/maxstream.py +141 -0
- StreamingCommunity/Api/Player/vixcloud.py +5 -3
- StreamingCommunity/Api/Site/1337xx/__init__.py +2 -2
- StreamingCommunity/Api/Site/altadefinizione/__init__.py +2 -2
- StreamingCommunity/Api/Site/altadefinizione/film.py +15 -35
- StreamingCommunity/Api/Site/animeunity/__init__.py +1 -1
- StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py +21 -23
- StreamingCommunity/Api/Site/animeworld/__init__.py +1 -1
- StreamingCommunity/Api/Site/animeworld/util/ScrapeSerie.py +2 -1
- StreamingCommunity/Api/Site/cb01new/__init__.py +72 -0
- StreamingCommunity/Api/Site/cb01new/film.py +62 -0
- StreamingCommunity/Api/Site/cb01new/site.py +78 -0
- StreamingCommunity/Api/Site/guardaserie/__init__.py +1 -1
- StreamingCommunity/Api/Site/raiplay/__init__.py +2 -2
- StreamingCommunity/Api/Site/raiplay/site.py +26 -94
- StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py +37 -17
- StreamingCommunity/Api/Site/streamingcommunity/__init__.py +87 -51
- StreamingCommunity/Api/Site/streamingcommunity/film.py +2 -2
- StreamingCommunity/Api/Site/streamingcommunity/series.py +4 -4
- StreamingCommunity/Api/Site/streamingcommunity/site.py +7 -4
- StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +6 -3
- StreamingCommunity/Api/Site/streamingwatch/__init__.py +10 -4
- StreamingCommunity/Api/Site/streamingwatch/site.py +12 -5
- StreamingCommunity/Api/Template/site.py +103 -58
- StreamingCommunity/Lib/Proxies/proxy.py +14 -174
- StreamingCommunity/TelegramHelp/config.json +62 -0
- StreamingCommunity/TelegramHelp/telegram_bot.py +4 -0
- StreamingCommunity/Upload/version.py +1 -1
- StreamingCommunity/Util/config_json.py +0 -4
- StreamingCommunity/Util/os.py +0 -6
- StreamingCommunity/run.py +2 -2
- {streamingcommunity-3.0.6.dist-info → streamingcommunity-3.0.8.dist-info}/METADATA +31 -13
- {streamingcommunity-3.0.6.dist-info → streamingcommunity-3.0.8.dist-info}/RECORD +37 -32
- {streamingcommunity-3.0.6.dist-info → streamingcommunity-3.0.8.dist-info}/WHEEL +1 -1
- {streamingcommunity-3.0.6.dist-info → streamingcommunity-3.0.8.dist-info}/entry_points.txt +0 -0
- {streamingcommunity-3.0.6.dist-info → streamingcommunity-3.0.8.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
|
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[
|
|
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) ->
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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)
|
|
@@ -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.
|
StreamingCommunity/Util/os.py
CHANGED
|
@@ -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(
|
|
223
|
-
msg.ask("[yellow]Press Enter to
|
|
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.
|
|
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="
|
|
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
|
-
|
|
778
|
-
|
|
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
|
-
|
|
784
|
-
|
|
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
|
-
|
|
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
|