StreamingCommunity 3.3.0__py3-none-any.whl → 3.3.1__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/Site/altadefinizione/__init__.py +37 -17
- StreamingCommunity/Api/Site/animeunity/__init__.py +36 -16
- StreamingCommunity/Api/Site/animeworld/__init__.py +50 -6
- StreamingCommunity/Api/Site/crunchyroll/__init__.py +42 -16
- StreamingCommunity/Api/Site/crunchyroll/site.py +1 -1
- StreamingCommunity/Api/Site/guardaserie/__init__.py +50 -6
- StreamingCommunity/Api/Site/mediasetinfinity/__init__.py +43 -5
- StreamingCommunity/Api/Site/mediasetinfinity/film.py +1 -1
- StreamingCommunity/Api/Site/mediasetinfinity/site.py +6 -3
- StreamingCommunity/Api/Site/mediasetinfinity/util/ScrapeSerie.py +6 -7
- StreamingCommunity/Api/Site/mediasetinfinity/util/get_license.py +162 -0
- StreamingCommunity/Api/Site/raiplay/__init__.py +45 -14
- StreamingCommunity/Api/Site/raiplay/series.py +9 -5
- StreamingCommunity/Api/Site/streamingcommunity/__init__.py +7 -2
- StreamingCommunity/Api/Site/streamingwatch/__init__.py +44 -14
- StreamingCommunity/Lib/Downloader/DASH/downloader.py +87 -55
- StreamingCommunity/Lib/Downloader/HLS/downloader.py +18 -6
- StreamingCommunity/Lib/Downloader/HLS/segments.py +1 -1
- StreamingCommunity/Lib/FFmpeg/command.py +66 -7
- StreamingCommunity/Lib/FFmpeg/util.py +16 -13
- StreamingCommunity/Upload/version.py +1 -1
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/METADATA +2 -11
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/RECORD +27 -27
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/WHEEL +0 -0
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/entry_points.txt +0 -0
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/licenses/LICENSE +0 -0
- {streamingcommunity-3.3.0.dist-info → streamingcommunity-3.3.1.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# 16.03.25
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
import logging
|
|
5
4
|
from urllib.parse import urlparse
|
|
6
5
|
|
|
@@ -16,10 +15,6 @@ from StreamingCommunity.Util.config_json import config_manager
|
|
|
16
15
|
from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager
|
|
17
16
|
|
|
18
17
|
|
|
19
|
-
# Logic class
|
|
20
|
-
from .get_license import get_bearer_token
|
|
21
|
-
|
|
22
|
-
|
|
23
18
|
# Variable
|
|
24
19
|
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
25
20
|
|
|
@@ -47,7 +42,10 @@ class GetSerieInfo:
|
|
|
47
42
|
return self.serie_id
|
|
48
43
|
|
|
49
44
|
def _get_public_id(self):
|
|
50
|
-
|
|
45
|
+
self.public_id = "PR1GhC"
|
|
46
|
+
return self.public_id
|
|
47
|
+
|
|
48
|
+
"""
|
|
51
49
|
bearer_token = get_bearer_token()
|
|
52
50
|
headers = {
|
|
53
51
|
'authorization': f'Bearer {bearer_token}',
|
|
@@ -73,6 +71,7 @@ class GetSerieInfo:
|
|
|
73
71
|
else:
|
|
74
72
|
logging.error(f"Failed to get public ID: {response.status_code}")
|
|
75
73
|
return None
|
|
74
|
+
"""
|
|
76
75
|
|
|
77
76
|
def _get_series_data(self):
|
|
78
77
|
"""Ottiene i dati della serie tramite l'API"""
|
|
@@ -290,4 +289,4 @@ class GetSerieInfo:
|
|
|
290
289
|
logging.error(f"Episode index {episode_index} is out of range for season {season_number}")
|
|
291
290
|
return None
|
|
292
291
|
|
|
293
|
-
return episodes[episode_index]
|
|
292
|
+
return episodes[episode_index]
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# 16.03.25
|
|
2
2
|
|
|
3
|
+
import json
|
|
4
|
+
import time
|
|
3
5
|
from urllib.parse import urlencode
|
|
4
6
|
import xml.etree.ElementTree as ET
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
# External library
|
|
8
10
|
import httpx
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from seleniumbase import Driver
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
# Internal utilities
|
|
@@ -14,9 +18,143 @@ from StreamingCommunity.Util.headers import get_headers, get_userAgent
|
|
|
14
18
|
|
|
15
19
|
|
|
16
20
|
# Variable
|
|
21
|
+
console = Console()
|
|
17
22
|
MAX_TIMEOUT = config_manager.get_int("REQUESTS", "timeout")
|
|
23
|
+
beToken = None
|
|
24
|
+
network_data = []
|
|
18
25
|
|
|
19
26
|
|
|
27
|
+
def save_network_data(data):
|
|
28
|
+
"""Save network data and check for beToken"""
|
|
29
|
+
global network_data
|
|
30
|
+
|
|
31
|
+
# Filter only for login API responses
|
|
32
|
+
if data.get('method') == 'Network.responseReceived':
|
|
33
|
+
params = data.get('params', {})
|
|
34
|
+
response = params.get('response', {})
|
|
35
|
+
url = response.get('url', '')
|
|
36
|
+
|
|
37
|
+
if "persona/login/v2.0" in url and params.get('type') != 'Preflight':
|
|
38
|
+
network_data.append(data)
|
|
39
|
+
console.print(f"[green]Found login API request to: {url}")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def generate_betoken(username: str, password: str, sleep_action: float = 1.0) -> str:
|
|
43
|
+
driver = Driver(uc=True, uc_cdp_events=True, incognito=True, headless=True)
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
console.print("[cyan]Launching browser...")
|
|
47
|
+
be_token_holder = {"token": None}
|
|
48
|
+
global network_data
|
|
49
|
+
network_data = [] # Reset network data
|
|
50
|
+
|
|
51
|
+
# Load home page
|
|
52
|
+
console.print("[cyan]Navigating to Mediaset Play...")
|
|
53
|
+
driver.uc_open_with_reconnect("https://www.mediasetplay.mediaset.it", sleep_action)
|
|
54
|
+
|
|
55
|
+
# Add CDP listeners after opening the page
|
|
56
|
+
driver.add_cdp_listener(
|
|
57
|
+
"Network.responseReceived",
|
|
58
|
+
lambda data: save_network_data(data)
|
|
59
|
+
)
|
|
60
|
+
driver.sleep(sleep_action)
|
|
61
|
+
|
|
62
|
+
# Accept privacy policy if present
|
|
63
|
+
try:
|
|
64
|
+
driver.click("#rti-privacy-accept-btn-screen1-id", timeout=3)
|
|
65
|
+
except Exception:
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
# Click Login using the specific div structure
|
|
69
|
+
console.print("[cyan]Clicking login button...")
|
|
70
|
+
try:
|
|
71
|
+
driver.click('div.dwv_v span:contains("Login")', timeout=5)
|
|
72
|
+
except Exception as e:
|
|
73
|
+
print(f"Error clicking login: {e}")
|
|
74
|
+
try:
|
|
75
|
+
driver.click('div.dwv_v', timeout=3)
|
|
76
|
+
except Exception:
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
driver.sleep(sleep_action)
|
|
80
|
+
|
|
81
|
+
# Click "Accedi con email e password" using the specific input
|
|
82
|
+
console.print("[cyan]Clicking email/password login...")
|
|
83
|
+
try:
|
|
84
|
+
driver.click('input.gigya-input-submit[value="Accedi con email e password"]', timeout=5)
|
|
85
|
+
except Exception as e:
|
|
86
|
+
print(f"Error clicking email login: {e}")
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
driver.sleep(sleep_action)
|
|
90
|
+
|
|
91
|
+
# Fill login credentials using specific IDs
|
|
92
|
+
console.print("[cyan]Filling login credentials...")
|
|
93
|
+
try:
|
|
94
|
+
email_input = 'input[name="username"].gigya-input-text'
|
|
95
|
+
driver.wait_for_element(email_input, timeout=5)
|
|
96
|
+
driver.type(email_input, username)
|
|
97
|
+
|
|
98
|
+
password_input = 'input[name="password"].gigya-input-password'
|
|
99
|
+
driver.wait_for_element(password_input, timeout=5)
|
|
100
|
+
driver.type(password_input, password)
|
|
101
|
+
|
|
102
|
+
except Exception as e:
|
|
103
|
+
print(f"Error filling credentials: {e}")
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
driver.sleep(sleep_action)
|
|
107
|
+
|
|
108
|
+
# Click Continue/Procedi using the submit button
|
|
109
|
+
console.print("[cyan]Clicking continue button...")
|
|
110
|
+
try:
|
|
111
|
+
driver.click('input.gigya-input-submit[type="submit"][value="Continua"]', timeout=5)
|
|
112
|
+
except Exception as e:
|
|
113
|
+
print(f"Error clicking continue: {e}")
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
# Wait for login response and parse network data
|
|
117
|
+
console.print("[cyan]Waiting for login response...")
|
|
118
|
+
for attempt in range(30):
|
|
119
|
+
driver.sleep(0.3)
|
|
120
|
+
|
|
121
|
+
# Check network data for beToken - skip preflight requests
|
|
122
|
+
for data in network_data:
|
|
123
|
+
if data.get('method') == 'Network.responseReceived':
|
|
124
|
+
params = data.get('params', {})
|
|
125
|
+
response = params.get('response', {})
|
|
126
|
+
url = response.get('url', '')
|
|
127
|
+
request_type = params.get('type', '')
|
|
128
|
+
|
|
129
|
+
if "persona/login/v2.0" in url and request_type != 'Preflight':
|
|
130
|
+
request_id = params.get('requestId')
|
|
131
|
+
|
|
132
|
+
if request_id:
|
|
133
|
+
try:
|
|
134
|
+
response_body = driver.execute_cdp_cmd(
|
|
135
|
+
'Network.getResponseBody',
|
|
136
|
+
{'requestId': request_id}
|
|
137
|
+
)
|
|
138
|
+
body = response_body.get('body', '')
|
|
139
|
+
|
|
140
|
+
if body:
|
|
141
|
+
response_data = json.loads(body)
|
|
142
|
+
be_token = response_data.get("response", {}).get("beToken")
|
|
143
|
+
|
|
144
|
+
if be_token:
|
|
145
|
+
be_token_holder["token"] = be_token
|
|
146
|
+
console.print("[green]Login successful! BeToken found!")
|
|
147
|
+
return be_token
|
|
148
|
+
|
|
149
|
+
except Exception:
|
|
150
|
+
continue
|
|
151
|
+
|
|
152
|
+
console.print(f"[yellow]Login completed. Total network events captured: {len(network_data)}")
|
|
153
|
+
return be_token_holder["token"]
|
|
154
|
+
|
|
155
|
+
finally:
|
|
156
|
+
driver.quit()
|
|
157
|
+
|
|
20
158
|
def get_bearer_token():
|
|
21
159
|
"""
|
|
22
160
|
Gets the BEARER_TOKEN for authentication.
|
|
@@ -24,6 +162,29 @@ def get_bearer_token():
|
|
|
24
162
|
Returns:
|
|
25
163
|
str: The bearer token string.
|
|
26
164
|
"""
|
|
165
|
+
global beToken
|
|
166
|
+
|
|
167
|
+
# Read beToken from config if already present
|
|
168
|
+
beToken = config_manager.get_dict("SITE_LOGIN", "mediasetinfinity")["beToken"]
|
|
169
|
+
if beToken is not None and len(beToken) != 0:
|
|
170
|
+
return beToken
|
|
171
|
+
|
|
172
|
+
username = config_manager.get_dict("SITE_LOGIN", "mediasetinfinity").get("username", "")
|
|
173
|
+
password = config_manager.get_dict("SITE_LOGIN", "mediasetinfinity").get("password", "")
|
|
174
|
+
|
|
175
|
+
if username and password:
|
|
176
|
+
beToken = generate_betoken(username, password)
|
|
177
|
+
|
|
178
|
+
if beToken is not None:
|
|
179
|
+
|
|
180
|
+
# Save current beToken
|
|
181
|
+
current_value = config_manager.get("SITE_LOGIN", "mediasetinfinity", dict)
|
|
182
|
+
current_value["beToken"] = beToken
|
|
183
|
+
config_manager.set_key("SITE_LOGIN", "mediasetinfinity", current_value)
|
|
184
|
+
config_manager.save_config()
|
|
185
|
+
|
|
186
|
+
return beToken
|
|
187
|
+
|
|
27
188
|
return config_manager.get_dict("SITE_LOGIN", "mediasetinfinity")["beToken"]
|
|
28
189
|
|
|
29
190
|
def get_playback_url(BEARER_TOKEN, CONTENT_ID):
|
|
@@ -158,6 +319,7 @@ def get_tracking_info(BEARER_TOKEN, PLAYBACK_JSON):
|
|
|
158
319
|
response.raise_for_status()
|
|
159
320
|
|
|
160
321
|
smil_xml = response.text
|
|
322
|
+
time.sleep(0.2)
|
|
161
323
|
results = parse_smil_for_tracking_and_video(smil_xml)
|
|
162
324
|
return results
|
|
163
325
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# 21.05.24
|
|
2
2
|
|
|
3
|
+
import sys
|
|
4
|
+
import subprocess
|
|
5
|
+
|
|
3
6
|
|
|
4
7
|
# External library
|
|
5
8
|
from rich.console import Console
|
|
@@ -33,8 +36,40 @@ console = Console()
|
|
|
33
36
|
def get_user_input(string_to_search: str = None):
|
|
34
37
|
"""
|
|
35
38
|
Asks the user to input a search term.
|
|
39
|
+
Handles both Telegram bot input and direct input.
|
|
40
|
+
If string_to_search is provided, it's returned directly (after stripping).
|
|
36
41
|
"""
|
|
37
|
-
|
|
42
|
+
if string_to_search is not None:
|
|
43
|
+
return string_to_search.strip()
|
|
44
|
+
|
|
45
|
+
if site_constant.TELEGRAM_BOT:
|
|
46
|
+
bot = get_bot_instance()
|
|
47
|
+
user_response = bot.ask(
|
|
48
|
+
"key_search", # Request type
|
|
49
|
+
"Enter the search term\nor type 'back' to return to the menu: ",
|
|
50
|
+
None
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if user_response is None:
|
|
54
|
+
bot.send_message("Timeout: No search term entered.", None)
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
if user_response.lower() == 'back':
|
|
58
|
+
bot.send_message("Returning to the main menu...", None)
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
# Restart the script
|
|
62
|
+
subprocess.Popen([sys.executable] + sys.argv)
|
|
63
|
+
sys.exit()
|
|
64
|
+
|
|
65
|
+
except Exception as e:
|
|
66
|
+
bot.send_message(f"Error during restart attempt: {e}", None)
|
|
67
|
+
return None # Return None if restart fails
|
|
68
|
+
|
|
69
|
+
return user_response.strip()
|
|
70
|
+
|
|
71
|
+
else:
|
|
72
|
+
return msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
|
|
38
73
|
|
|
39
74
|
def process_search_result(select_title, selections=None):
|
|
40
75
|
"""
|
|
@@ -44,6 +79,9 @@ def process_search_result(select_title, selections=None):
|
|
|
44
79
|
select_title (MediaItem): The selected media item
|
|
45
80
|
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
|
|
46
81
|
{'season': season_selection, 'episode': episode_selection}
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
bool: True if processing was successful, False otherwise
|
|
47
85
|
"""
|
|
48
86
|
if not select_title:
|
|
49
87
|
if site_constant.TELEGRAM_BOT:
|
|
@@ -51,7 +89,7 @@ def process_search_result(select_title, selections=None):
|
|
|
51
89
|
bot.send_message("No title selected or selection cancelled.", None)
|
|
52
90
|
else:
|
|
53
91
|
console.print("[yellow]No title selected or selection cancelled.")
|
|
54
|
-
return
|
|
92
|
+
return False
|
|
55
93
|
|
|
56
94
|
if select_title.type == 'tv':
|
|
57
95
|
season_selection = None
|
|
@@ -62,24 +100,16 @@ def process_search_result(select_title, selections=None):
|
|
|
62
100
|
episode_selection = selections.get('episode')
|
|
63
101
|
|
|
64
102
|
download_series(select_title, season_selection, episode_selection)
|
|
103
|
+
return True
|
|
65
104
|
|
|
66
105
|
else:
|
|
67
106
|
download_film(select_title)
|
|
107
|
+
return True
|
|
68
108
|
|
|
69
109
|
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
|
|
70
110
|
"""
|
|
71
111
|
Main function of the application for search.
|
|
72
112
|
|
|
73
|
-
Parameters:
|
|
74
|
-
string_to_search (str, optional): String to search for
|
|
75
|
-
get_onlyDatabase (bool, optional): If True, return only the database object
|
|
76
|
-
direct_item (dict, optional): Direct item to process (bypass search)
|
|
77
|
-
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
|
|
78
|
-
{'season': season_selection, 'episode': episode_selection}
|
|
79
|
-
"""
|
|
80
|
-
"""
|
|
81
|
-
Main function of the application for search.
|
|
82
|
-
|
|
83
113
|
Parameters:
|
|
84
114
|
string_to_search (str, optional): String to search for
|
|
85
115
|
get_onlyDatabase (bool, optional): If True, return only the database object
|
|
@@ -94,7 +124,7 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
|
|
|
94
124
|
if direct_item:
|
|
95
125
|
select_title = MediaItem(**direct_item)
|
|
96
126
|
process_search_result(select_title, selections)
|
|
97
|
-
return
|
|
127
|
+
return True
|
|
98
128
|
|
|
99
129
|
# Get the user input for the search term
|
|
100
130
|
actual_search_query = get_user_input(string_to_search)
|
|
@@ -116,6 +146,7 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
|
|
|
116
146
|
if len_database > 0:
|
|
117
147
|
select_title = get_select_title(table_show_manager, media_search_manager, len_database)
|
|
118
148
|
process_search_result(select_title, selections)
|
|
149
|
+
return True
|
|
119
150
|
|
|
120
151
|
else:
|
|
121
152
|
if bot:
|
|
@@ -125,4 +156,4 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
|
|
|
125
156
|
|
|
126
157
|
# Do not call search() recursively here to avoid infinite loops on no results.
|
|
127
158
|
# The flow should return to the caller (e.g., main menu in run.py).
|
|
128
|
-
return
|
|
159
|
+
return
|
|
@@ -11,7 +11,7 @@ from rich.prompt import Prompt
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
# Internal utilities
|
|
14
|
-
from StreamingCommunity.Util.headers import get_headers
|
|
14
|
+
from StreamingCommunity.Util.headers import get_headers, get_userAgent
|
|
15
15
|
from StreamingCommunity.Util.os import get_wvd_path
|
|
16
16
|
from StreamingCommunity.Util.message import start_message
|
|
17
17
|
|
|
@@ -92,17 +92,21 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
92
92
|
console.print(f"[bold red] CDM file not found or invalid path: {cdm_device_path}[/bold red]")
|
|
93
93
|
sys.exit(0)
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
full_license_url = generate_license_url(obj_episode.mpd_id)
|
|
96
|
+
license_headers = {
|
|
97
|
+
'nv-authorizations': full_license_url.split("?")[1].split("=")[1],
|
|
98
|
+
'user-agent': get_userAgent(),
|
|
99
|
+
}
|
|
96
100
|
|
|
97
101
|
dash_process = DASH_Downloader(
|
|
98
102
|
cdm_device=cdm_device_path,
|
|
99
|
-
license_url=
|
|
103
|
+
license_url=full_license_url.split("?")[0],
|
|
100
104
|
mpd_url=master_playlist,
|
|
101
105
|
output_path=os.path.join(mp4_path, mp4_name),
|
|
102
106
|
)
|
|
103
107
|
dash_process.parse_manifest(custom_headers=get_headers())
|
|
104
108
|
|
|
105
|
-
if dash_process.download_and_decrypt():
|
|
109
|
+
if dash_process.download_and_decrypt(custom_headers=license_headers):
|
|
106
110
|
dash_process.finalize_output()
|
|
107
111
|
|
|
108
112
|
# Get final output path and status
|
|
@@ -202,4 +206,4 @@ def download_series(select_season: MediaItem, season_selection: str = None, epis
|
|
|
202
206
|
if len(list_season_select) > 1 or index_season_selected == "*":
|
|
203
207
|
download_episode(season_number, scrape_serie, download_all=True)
|
|
204
208
|
else:
|
|
205
|
-
download_episode(season_number, scrape_serie, download_all=False, episode_selection=episode_selection)
|
|
209
|
+
download_episode(season_number, scrape_serie, download_all=False, episode_selection=episode_selection)
|
|
@@ -78,6 +78,8 @@ def process_search_result(select_title, selections=None):
|
|
|
78
78
|
select_title (MediaItem): The selected media item. Can be None if selection fails.
|
|
79
79
|
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
|
|
80
80
|
e.g., {'season': season_selection, 'episode': episode_selection}
|
|
81
|
+
Returns:
|
|
82
|
+
bool: True if processing was successful, False otherwise
|
|
81
83
|
"""
|
|
82
84
|
if not select_title:
|
|
83
85
|
if site_constant.TELEGRAM_BOT:
|
|
@@ -85,7 +87,7 @@ def process_search_result(select_title, selections=None):
|
|
|
85
87
|
bot.send_message("No title selected or selection cancelled.", None)
|
|
86
88
|
else:
|
|
87
89
|
console.print("[yellow]No title selected or selection cancelled.")
|
|
88
|
-
return
|
|
90
|
+
return False
|
|
89
91
|
|
|
90
92
|
if select_title.type == 'tv':
|
|
91
93
|
season_selection = None
|
|
@@ -96,9 +98,11 @@ def process_search_result(select_title, selections=None):
|
|
|
96
98
|
episode_selection = selections.get('episode')
|
|
97
99
|
|
|
98
100
|
download_series(select_title, season_selection, episode_selection)
|
|
101
|
+
return True
|
|
99
102
|
|
|
100
103
|
else:
|
|
101
104
|
download_film(select_title)
|
|
105
|
+
return True
|
|
102
106
|
|
|
103
107
|
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
|
|
104
108
|
"""
|
|
@@ -119,7 +123,7 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
|
|
|
119
123
|
if direct_item:
|
|
120
124
|
select_title_obj = MediaItem(**direct_item)
|
|
121
125
|
process_search_result(select_title_obj, selections)
|
|
122
|
-
return
|
|
126
|
+
return True
|
|
123
127
|
|
|
124
128
|
actual_search_query = get_user_input(string_to_search)
|
|
125
129
|
|
|
@@ -140,6 +144,7 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
|
|
|
140
144
|
if len_database > 0:
|
|
141
145
|
select_title = get_select_title(table_show_manager, media_search_manager, len_database)
|
|
142
146
|
process_search_result(select_title, selections)
|
|
147
|
+
return True
|
|
143
148
|
|
|
144
149
|
else:
|
|
145
150
|
if bot:
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# 29.04.25
|
|
2
2
|
|
|
3
|
+
import sys
|
|
4
|
+
import subprocess
|
|
5
|
+
|
|
3
6
|
# External library
|
|
4
7
|
from rich.console import Console
|
|
5
8
|
from rich.prompt import Prompt
|
|
@@ -33,9 +36,39 @@ def get_user_input(string_to_search: str = None):
|
|
|
33
36
|
"""
|
|
34
37
|
Asks the user to input a search term.
|
|
35
38
|
Handles both Telegram bot input and direct input.
|
|
39
|
+
If string_to_search is provided, it's returned directly (after stripping).
|
|
36
40
|
"""
|
|
37
|
-
string_to_search
|
|
38
|
-
|
|
41
|
+
if string_to_search is not None:
|
|
42
|
+
return string_to_search.strip()
|
|
43
|
+
|
|
44
|
+
if site_constant.TELEGRAM_BOT:
|
|
45
|
+
bot = get_bot_instance()
|
|
46
|
+
user_response = bot.ask(
|
|
47
|
+
"key_search", # Request type
|
|
48
|
+
"Enter the search term\nor type 'back' to return to the menu: ",
|
|
49
|
+
None
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if user_response is None:
|
|
53
|
+
bot.send_message("Timeout: No search term entered.", None)
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
if user_response.lower() == 'back':
|
|
57
|
+
bot.send_message("Returning to the main menu...", None)
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
# Restart the script
|
|
61
|
+
subprocess.Popen([sys.executable] + sys.argv)
|
|
62
|
+
sys.exit()
|
|
63
|
+
|
|
64
|
+
except Exception as e:
|
|
65
|
+
bot.send_message(f"Error during restart attempt: {e}", None)
|
|
66
|
+
return None # Return None if restart fails
|
|
67
|
+
|
|
68
|
+
return user_response.strip()
|
|
69
|
+
|
|
70
|
+
else:
|
|
71
|
+
return msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
|
|
39
72
|
|
|
40
73
|
def process_search_result(select_title, selections=None):
|
|
41
74
|
"""
|
|
@@ -45,6 +78,9 @@ def process_search_result(select_title, selections=None):
|
|
|
45
78
|
select_title (MediaItem): The selected media item
|
|
46
79
|
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
|
|
47
80
|
{'season': season_selection, 'episode': episode_selection}
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
bool: True if processing was successful, False otherwise
|
|
48
84
|
"""
|
|
49
85
|
if not select_title:
|
|
50
86
|
if site_constant.TELEGRAM_BOT:
|
|
@@ -52,7 +88,8 @@ def process_search_result(select_title, selections=None):
|
|
|
52
88
|
bot.send_message("No title selected or selection cancelled.", None)
|
|
53
89
|
else:
|
|
54
90
|
console.print("[yellow]No title selected or selection cancelled.")
|
|
55
|
-
return
|
|
91
|
+
return False
|
|
92
|
+
|
|
56
93
|
if select_title.type == 'tv':
|
|
57
94
|
season_selection = None
|
|
58
95
|
episode_selection = None
|
|
@@ -62,24 +99,16 @@ def process_search_result(select_title, selections=None):
|
|
|
62
99
|
episode_selection = selections.get('episode')
|
|
63
100
|
|
|
64
101
|
download_series(select_title, season_selection, episode_selection)
|
|
102
|
+
return True
|
|
65
103
|
|
|
66
104
|
else:
|
|
67
105
|
download_film(select_title)
|
|
106
|
+
return True
|
|
68
107
|
|
|
69
108
|
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
|
|
70
109
|
"""
|
|
71
110
|
Main function of the application for search.
|
|
72
111
|
|
|
73
|
-
Parameters:
|
|
74
|
-
string_to_search (str, optional): String to search for
|
|
75
|
-
get_onlyDatabase (bool, optional): If True, return only the database object
|
|
76
|
-
direct_item (dict, optional): Direct item to process (bypass search)
|
|
77
|
-
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
|
|
78
|
-
{'season': season_selection, 'episode': episode_selection}
|
|
79
|
-
"""
|
|
80
|
-
"""
|
|
81
|
-
Main function of the application for search.
|
|
82
|
-
|
|
83
112
|
Parameters:
|
|
84
113
|
string_to_search (str, optional): String to search for
|
|
85
114
|
get_onlyDatabase (bool, optional): If True, return only the database object
|
|
@@ -94,7 +123,7 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
|
|
|
94
123
|
if direct_item:
|
|
95
124
|
select_title = MediaItem(**direct_item)
|
|
96
125
|
process_search_result(select_title, selections)
|
|
97
|
-
return
|
|
126
|
+
return True
|
|
98
127
|
|
|
99
128
|
# Get the user input for the search term
|
|
100
129
|
actual_search_query = get_user_input(string_to_search)
|
|
@@ -116,6 +145,7 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
|
|
|
116
145
|
if len_database > 0:
|
|
117
146
|
select_title = get_select_title(table_show_manager, media_search_manager, len_database)
|
|
118
147
|
process_search_result(select_title, selections)
|
|
148
|
+
return True
|
|
119
149
|
|
|
120
150
|
else:
|
|
121
151
|
if bot:
|