StreamingCommunity 2.9.2__py3-none-any.whl → 2.9.4__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/Helper/Vixcloud/util.py +40 -38
- StreamingCommunity/Api/Player/maxstream.py +6 -11
- StreamingCommunity/Api/Player/supervideo.py +4 -0
- StreamingCommunity/Api/Site/1337xx/site.py +1 -9
- StreamingCommunity/Api/Site/altadefinizione/__init__.py +61 -0
- StreamingCommunity/Api/Site/altadefinizione/film.py +98 -0
- StreamingCommunity/Api/Site/altadefinizione/series.py +164 -0
- StreamingCommunity/Api/Site/altadefinizione/site.py +75 -0
- StreamingCommunity/Api/Site/altadefinizione/util/ScrapeSerie.py +72 -0
- StreamingCommunity/Api/Site/animeunity/film_serie.py +2 -2
- StreamingCommunity/Api/Site/animeunity/site.py +15 -41
- StreamingCommunity/Api/Site/cb01new/site.py +5 -16
- StreamingCommunity/Api/Site/ddlstreamitaly/site.py +1 -9
- StreamingCommunity/Api/Site/guardaserie/series.py +1 -1
- StreamingCommunity/Api/Site/guardaserie/site.py +1 -9
- StreamingCommunity/Api/Site/streamingcommunity/series.py +30 -12
- StreamingCommunity/Api/Site/streamingcommunity/site.py +10 -10
- StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +38 -17
- StreamingCommunity/Api/Template/Class/SearchType.py +1 -1
- StreamingCommunity/Api/Template/Util/__init__.py +0 -1
- StreamingCommunity/Api/Template/Util/manage_ep.py +43 -16
- StreamingCommunity/Api/Template/config_loader.py +0 -4
- StreamingCommunity/Api/Template/site.py +1 -1
- StreamingCommunity/Lib/Downloader/HLS/downloader.py +13 -2
- StreamingCommunity/Lib/Downloader/HLS/segments.py +37 -11
- StreamingCommunity/Lib/Downloader/MP4/downloader.py +5 -3
- StreamingCommunity/Lib/FFmpeg/command.py +2 -2
- StreamingCommunity/Lib/FFmpeg/util.py +11 -15
- StreamingCommunity/Lib/M3U8/estimator.py +4 -4
- StreamingCommunity/Lib/TMBD/tmdb.py +1 -1
- StreamingCommunity/Upload/version.py +1 -1
- StreamingCommunity/Util/config_json.py +0 -3
- StreamingCommunity/__init__.py +6 -0
- {StreamingCommunity-2.9.2.dist-info → streamingcommunity-2.9.4.dist-info}/METADATA +91 -7
- {StreamingCommunity-2.9.2.dist-info → streamingcommunity-2.9.4.dist-info}/RECORD +39 -35
- {StreamingCommunity-2.9.2.dist-info → streamingcommunity-2.9.4.dist-info}/WHEEL +1 -1
- StreamingCommunity/Api/Template/Util/get_domain.py +0 -100
- {StreamingCommunity-2.9.2.dist-info → streamingcommunity-2.9.4.dist-info}/LICENSE +0 -0
- {StreamingCommunity-2.9.2.dist-info → streamingcommunity-2.9.4.dist-info}/entry_points.txt +0 -0
- {StreamingCommunity-2.9.2.dist-info → streamingcommunity-2.9.4.dist-info}/top_level.txt +0 -0
|
@@ -12,13 +12,13 @@ from rich.console import Console
|
|
|
12
12
|
|
|
13
13
|
# Internal utilities
|
|
14
14
|
from StreamingCommunity.Util.config_json import config_manager
|
|
15
|
+
from StreamingCommunity.Util.headers import get_userAgent
|
|
15
16
|
from StreamingCommunity.Util.table import TVShowManager
|
|
16
17
|
from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
# Logic class
|
|
20
21
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
21
|
-
from StreamingCommunity.Api.Template.Util import search_domain
|
|
22
22
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
|
23
23
|
|
|
24
24
|
|
|
@@ -29,7 +29,7 @@ table_show_manager = TVShowManager()
|
|
|
29
29
|
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
def get_token(
|
|
32
|
+
def get_token() -> dict:
|
|
33
33
|
"""
|
|
34
34
|
Function to retrieve session tokens from a specified website.
|
|
35
35
|
|
|
@@ -40,8 +40,6 @@ def get_token(site_name: str, domain: str) -> dict:
|
|
|
40
40
|
Returns:
|
|
41
41
|
- dict: A dictionary containing session tokens. The keys are 'XSRF_TOKEN', 'animeunity_session', and 'csrf_token'.
|
|
42
42
|
"""
|
|
43
|
-
|
|
44
|
-
# Send a GET request to the specified URL composed of the site name and domain
|
|
45
43
|
response = httpx.get(
|
|
46
44
|
url=site_constant.FULL_URL,
|
|
47
45
|
timeout=max_timeout
|
|
@@ -50,17 +48,11 @@ def get_token(site_name: str, domain: str) -> dict:
|
|
|
50
48
|
|
|
51
49
|
# Initialize variables to store CSRF token
|
|
52
50
|
find_csrf_token = None
|
|
53
|
-
|
|
54
|
-
# Parse the HTML response using BeautifulSoup
|
|
55
51
|
soup = BeautifulSoup(response.text, "html.parser")
|
|
56
52
|
|
|
57
|
-
# Loop through all meta tags in the HTML response
|
|
58
53
|
for html_meta in soup.find_all("meta"):
|
|
59
|
-
|
|
60
|
-
# Check if the meta tag has a 'name' attribute equal to "csrf-token"
|
|
61
54
|
if html_meta.get('name') == "csrf-token":
|
|
62
55
|
|
|
63
|
-
# If found, retrieve the content of the meta tag, which is the CSRF token
|
|
64
56
|
find_csrf_token = html_meta.get('content')
|
|
65
57
|
|
|
66
58
|
logging.info(f"Extract: ('animeunity_session': {response.cookies['animeunity_session']}, 'csrf_token': {find_csrf_token})")
|
|
@@ -83,13 +75,12 @@ def get_real_title(record):
|
|
|
83
75
|
Returns:
|
|
84
76
|
- str: The title found in the record. If no title is found, returns None.
|
|
85
77
|
"""
|
|
86
|
-
|
|
87
|
-
if record['title'] is not None:
|
|
88
|
-
return record['title']
|
|
89
|
-
|
|
90
|
-
elif record['title_eng'] is not None:
|
|
78
|
+
if record['title_eng'] is not None:
|
|
91
79
|
return record['title_eng']
|
|
92
80
|
|
|
81
|
+
elif record['title'] is not None:
|
|
82
|
+
return record['title']
|
|
83
|
+
|
|
93
84
|
else:
|
|
94
85
|
return record['title_it']
|
|
95
86
|
|
|
@@ -110,33 +101,14 @@ def title_search(title: str) -> int:
|
|
|
110
101
|
media_search_manager.clear()
|
|
111
102
|
table_show_manager.clear()
|
|
112
103
|
|
|
113
|
-
#
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if domain_to_use is None or base_url is None:
|
|
117
|
-
console.print("[bold red]Error: Unable to determine valid domain or base URL.[/bold red]")
|
|
118
|
-
console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]")
|
|
119
|
-
sys.exit(1)
|
|
120
|
-
|
|
121
|
-
data = get_token(site_constant.SITE_NAME, domain_to_use)
|
|
122
|
-
|
|
123
|
-
# Prepare cookies to be used in the request
|
|
124
|
-
cookies = {
|
|
125
|
-
'animeunity_session': data.get('animeunity_session')
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
# Prepare headers for the request
|
|
104
|
+
# Create parameter for request
|
|
105
|
+
data = get_token()
|
|
106
|
+
cookies = {'animeunity_session': data.get('animeunity_session')}
|
|
129
107
|
headers = {
|
|
130
|
-
'
|
|
131
|
-
'accept-language': 'it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7',
|
|
132
|
-
'content-type': 'application/json;charset=UTF-8',
|
|
108
|
+
'user-agent': get_userAgent(),
|
|
133
109
|
'x-csrf-token': data.get('csrf_token')
|
|
134
110
|
}
|
|
135
|
-
|
|
136
|
-
# Prepare JSON data to be sent in the request
|
|
137
|
-
json_data = {
|
|
138
|
-
'title': title
|
|
139
|
-
}
|
|
111
|
+
json_data = {'title': title}
|
|
140
112
|
|
|
141
113
|
# Send a POST request to the API endpoint for live search
|
|
142
114
|
try:
|
|
@@ -151,6 +123,7 @@ def title_search(title: str) -> int:
|
|
|
151
123
|
|
|
152
124
|
except Exception as e:
|
|
153
125
|
console.print(f"Site: {site_constant.SITE_NAME}, request search error: {e}")
|
|
126
|
+
return 0
|
|
154
127
|
|
|
155
128
|
# Inizializza la lista delle scelte
|
|
156
129
|
if site_constant.TELEGRAM_BOT:
|
|
@@ -167,8 +140,9 @@ def title_search(title: str) -> int:
|
|
|
167
140
|
'slug': dict_title.get('slug'),
|
|
168
141
|
'name': dict_title.get('name'),
|
|
169
142
|
'type': dict_title.get('type'),
|
|
170
|
-
'
|
|
171
|
-
'episodes_count': dict_title.get('episodes_count')
|
|
143
|
+
'status': dict_title.get('status'),
|
|
144
|
+
'episodes_count': dict_title.get('episodes_count'),
|
|
145
|
+
'plot': ' '.join((words := str(dict_title.get('plot', '')).split())[:10]) + ('...' if len(words) > 10 else '')
|
|
172
146
|
})
|
|
173
147
|
|
|
174
148
|
if site_constant.TELEGRAM_BOT:
|
|
@@ -17,7 +17,6 @@ from StreamingCommunity.Util.table import TVShowManager
|
|
|
17
17
|
|
|
18
18
|
# Logic class
|
|
19
19
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
20
|
-
from StreamingCommunity.Api.Template.Util import search_domain
|
|
21
20
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
|
22
21
|
|
|
23
22
|
|
|
@@ -41,15 +40,7 @@ def title_search(word_to_search: str) -> int:
|
|
|
41
40
|
media_search_manager.clear()
|
|
42
41
|
table_show_manager.clear()
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
domain_to_use, base_url = search_domain(site_constant.FULL_URL)
|
|
46
|
-
|
|
47
|
-
if domain_to_use is None or base_url is None:
|
|
48
|
-
console.print("[bold red]Error: Unable to determine valid domain or base URL.[/bold red]")
|
|
49
|
-
console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]")
|
|
50
|
-
sys.exit(1)
|
|
51
|
-
|
|
52
|
-
search_url = f"{site_constant.FULL_URL}/?s={word_to_search}"
|
|
43
|
+
search_url = f"{site_constant.FULL_URL}/?story={word_to_search}&do=search&subaction=search"
|
|
53
44
|
console.print(f"[cyan]Search url: [yellow]{search_url}")
|
|
54
45
|
|
|
55
46
|
try:
|
|
@@ -58,20 +49,18 @@ def title_search(word_to_search: str) -> int:
|
|
|
58
49
|
|
|
59
50
|
except Exception as e:
|
|
60
51
|
console.print(f"Site: {site_constant.SITE_NAME}, request search error: {e}")
|
|
52
|
+
return 0
|
|
61
53
|
|
|
62
54
|
# Create soup and find table
|
|
63
55
|
soup = BeautifulSoup(response.text, "html.parser")
|
|
64
56
|
|
|
65
|
-
for div in soup.find_all("div", class_ = "
|
|
57
|
+
for div in soup.find_all("div", class_ = "short-main"):
|
|
66
58
|
try:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
title = div.find("h3").find("a").get_text(strip=True)
|
|
70
|
-
desc = div.find("p").find("strong").text
|
|
59
|
+
url = div.find("a").get("href")
|
|
60
|
+
title = div.find("a").get_text(strip=True)
|
|
71
61
|
|
|
72
62
|
title_info = {
|
|
73
63
|
'name': title,
|
|
74
|
-
'desc': desc,
|
|
75
64
|
'url': url
|
|
76
65
|
}
|
|
77
66
|
|
|
@@ -18,7 +18,6 @@ from StreamingCommunity.Util.table import TVShowManager
|
|
|
18
18
|
|
|
19
19
|
# Logic class
|
|
20
20
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
21
|
-
from StreamingCommunity.Api.Template.Util import search_domain
|
|
22
21
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
|
23
22
|
|
|
24
23
|
|
|
@@ -42,14 +41,6 @@ def title_search(word_to_search: str) -> int:
|
|
|
42
41
|
media_search_manager.clear()
|
|
43
42
|
table_show_manager.clear()
|
|
44
43
|
|
|
45
|
-
# Check if domain is working
|
|
46
|
-
domain_to_use, base_url = search_domain(site_constant.FULL_URL)
|
|
47
|
-
|
|
48
|
-
if domain_to_use is None or base_url is None:
|
|
49
|
-
console.print("[bold red]Error: Unable to determine valid domain or base URL.[/bold red]")
|
|
50
|
-
console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]")
|
|
51
|
-
sys.exit(1)
|
|
52
|
-
|
|
53
44
|
search_url = f"{site_constant.FULL_URL}/search/?&q={word_to_search}&quick=1&type=videobox_video&nodes=11"
|
|
54
45
|
console.print(f"[cyan]Search url: [yellow]{search_url}")
|
|
55
46
|
|
|
@@ -59,6 +50,7 @@ def title_search(word_to_search: str) -> int:
|
|
|
59
50
|
|
|
60
51
|
except Exception as e:
|
|
61
52
|
console.print(f"Site: {site_constant.SITE_NAME}, request search error: {e}")
|
|
53
|
+
return 0
|
|
62
54
|
|
|
63
55
|
# Create soup and find table
|
|
64
56
|
soup = BeautifulSoup(response.text, "html.parser")
|
|
@@ -51,7 +51,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scap
|
|
|
51
51
|
- bool: kill handler status
|
|
52
52
|
"""
|
|
53
53
|
start_message()
|
|
54
|
-
index_season_selected = dynamic_format_number(index_season_selected)
|
|
54
|
+
index_season_selected = dynamic_format_number(str(index_season_selected))
|
|
55
55
|
|
|
56
56
|
# Get info about episode
|
|
57
57
|
obj_episode = scape_info_serie.list_episodes[index_episode_selected - 1]
|
|
@@ -16,7 +16,6 @@ from StreamingCommunity.Util.table import TVShowManager
|
|
|
16
16
|
|
|
17
17
|
# Logic class
|
|
18
18
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
19
|
-
from StreamingCommunity.Api.Template.Util import search_domain
|
|
20
19
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
|
21
20
|
|
|
22
21
|
|
|
@@ -41,14 +40,6 @@ def title_search(word_to_search: str) -> int:
|
|
|
41
40
|
media_search_manager.clear()
|
|
42
41
|
table_show_manager.clear()
|
|
43
42
|
|
|
44
|
-
# Check if domain is working
|
|
45
|
-
domain_to_use, base_url = search_domain(site_constant.FULL_URL)
|
|
46
|
-
|
|
47
|
-
if domain_to_use is None or base_url is None:
|
|
48
|
-
console.print("[bold red]Error: Unable to determine valid domain or base URL.[/bold red]")
|
|
49
|
-
console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]")
|
|
50
|
-
sys.exit(1)
|
|
51
|
-
|
|
52
43
|
search_url = f"{site_constant.FULL_URL}/?story={word_to_search}&do=search&subaction=search"
|
|
53
44
|
console.print(f"[cyan]Search url: [yellow]{search_url}")
|
|
54
45
|
|
|
@@ -58,6 +49,7 @@ def title_search(word_to_search: str) -> int:
|
|
|
58
49
|
|
|
59
50
|
except Exception as e:
|
|
60
51
|
console.print(f"Site: {site_constant.SITE_NAME}, request search error: {e}")
|
|
52
|
+
return 0
|
|
61
53
|
|
|
62
54
|
# Create soup and find table
|
|
63
55
|
soup = BeautifulSoup(response.text, "html.parser")
|
|
@@ -50,10 +50,17 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
50
50
|
- bool: kill handler status
|
|
51
51
|
"""
|
|
52
52
|
start_message()
|
|
53
|
-
index_season_selected = dynamic_format_number(index_season_selected)
|
|
53
|
+
index_season_selected = dynamic_format_number(str(index_season_selected))
|
|
54
|
+
|
|
55
|
+
# SPECIAL: Get season number
|
|
56
|
+
season = None
|
|
57
|
+
for s in scrape_serie.seasons_manager.seasons:
|
|
58
|
+
if s.number == int(index_season_selected):
|
|
59
|
+
season = s
|
|
60
|
+
break
|
|
54
61
|
|
|
55
62
|
# Get info about episode
|
|
56
|
-
obj_episode =
|
|
63
|
+
obj_episode = season.episodes.get(index_episode_selected - 1)
|
|
57
64
|
console.print(f"[yellow]Download: [red]{index_season_selected}:{index_episode_selected} {obj_episode.name}")
|
|
58
65
|
print()
|
|
59
66
|
|
|
@@ -100,14 +107,16 @@ def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, vid
|
|
|
100
107
|
- index_season_selected (int): Index of the selected season.
|
|
101
108
|
- download_all (bool): Download all episodes in the season.
|
|
102
109
|
"""
|
|
103
|
-
|
|
104
|
-
# Clean memory of all episodes and get the number of the season
|
|
105
|
-
scrape_serie.episode_manager.clear()
|
|
106
|
-
|
|
107
|
-
# Start message and collect information about episodes
|
|
108
110
|
start_message()
|
|
109
111
|
scrape_serie.collect_info_season(index_season_selected)
|
|
110
|
-
|
|
112
|
+
|
|
113
|
+
# SPECIAL: Get season number
|
|
114
|
+
season = None
|
|
115
|
+
for s in scrape_serie.seasons_manager.seasons:
|
|
116
|
+
if s.number == index_season_selected:
|
|
117
|
+
season = s
|
|
118
|
+
break
|
|
119
|
+
episodes_count = len(season.episodes.episodes)
|
|
111
120
|
|
|
112
121
|
if download_all:
|
|
113
122
|
|
|
@@ -123,7 +132,7 @@ def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, vid
|
|
|
123
132
|
else:
|
|
124
133
|
|
|
125
134
|
# Display episodes list and manage user selection
|
|
126
|
-
last_command = display_episodes_list(
|
|
135
|
+
last_command = display_episodes_list(season.episodes.episodes)
|
|
127
136
|
list_episode_select = manage_selection(last_command, episodes_count)
|
|
128
137
|
|
|
129
138
|
try:
|
|
@@ -163,7 +172,7 @@ def download_series(select_season: MediaItem) -> None:
|
|
|
163
172
|
|
|
164
173
|
# Collect information about seasons
|
|
165
174
|
scrape_serie.collect_info_title()
|
|
166
|
-
seasons_count = scrape_serie.
|
|
175
|
+
seasons_count = len(scrape_serie.seasons_manager)
|
|
167
176
|
|
|
168
177
|
# Prompt user for season selection and download episodes
|
|
169
178
|
console.print(f"\n[green]Seasons found: [red]{seasons_count}")
|
|
@@ -197,14 +206,23 @@ def download_series(select_season: MediaItem) -> None:
|
|
|
197
206
|
|
|
198
207
|
# Loop through the selected seasons and download episodes
|
|
199
208
|
for i_season in list_season_select:
|
|
209
|
+
|
|
210
|
+
# SPECIAL: Get season number
|
|
211
|
+
season = None
|
|
212
|
+
for s in scrape_serie.seasons_manager.seasons:
|
|
213
|
+
if s.number == i_season:
|
|
214
|
+
season = s
|
|
215
|
+
break
|
|
216
|
+
season_number = season.number
|
|
217
|
+
|
|
200
218
|
if len(list_season_select) > 1 or index_season_selected == "*":
|
|
201
219
|
|
|
202
220
|
# Download all episodes if multiple seasons are selected or if '*' is used
|
|
203
|
-
download_episode(
|
|
221
|
+
download_episode(season_number, scrape_serie, video_source, download_all=True)
|
|
204
222
|
else:
|
|
205
223
|
|
|
206
224
|
# Otherwise, let the user select specific episodes for the single season
|
|
207
|
-
download_episode(
|
|
225
|
+
download_episode(season_number, scrape_serie, video_source, download_all=False)
|
|
208
226
|
|
|
209
227
|
if site_constant.TELEGRAM_BOT:
|
|
210
228
|
bot.send_message(f"Finito di scaricare tutte le serie e episodi", None)
|
|
@@ -17,7 +17,6 @@ from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
|
|
|
17
17
|
|
|
18
18
|
# Logic class
|
|
19
19
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
20
|
-
from StreamingCommunity.Api.Template.Util import search_domain
|
|
21
20
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
|
22
21
|
|
|
23
22
|
|
|
@@ -38,13 +37,6 @@ def title_search(title_search: str) -> int:
|
|
|
38
37
|
Returns:
|
|
39
38
|
int: The number of titles found.
|
|
40
39
|
"""
|
|
41
|
-
domain_to_use, base_url = search_domain(site_constant.FULL_URL)
|
|
42
|
-
|
|
43
|
-
if domain_to_use is None or base_url is None:
|
|
44
|
-
console.print("[bold red]Error: Unable to determine valid domain or base URL.[/bold red]")
|
|
45
|
-
console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]")
|
|
46
|
-
sys.exit(1)
|
|
47
|
-
|
|
48
40
|
if site_constant.TELEGRAM_BOT:
|
|
49
41
|
bot = get_bot_instance()
|
|
50
42
|
|
|
@@ -60,12 +52,20 @@ def title_search(title_search: str) -> int:
|
|
|
60
52
|
|
|
61
53
|
except Exception as e:
|
|
62
54
|
console.print(f"Site: {site_constant.SITE_NAME}, request search error: {e}")
|
|
55
|
+
return 0
|
|
63
56
|
|
|
64
57
|
# Prepara le scelte per l'utente
|
|
65
58
|
if site_constant.TELEGRAM_BOT:
|
|
66
59
|
choices = []
|
|
67
|
-
|
|
68
|
-
|
|
60
|
+
|
|
61
|
+
# Collect json data
|
|
62
|
+
try:
|
|
63
|
+
data = response.json().get('data', [])
|
|
64
|
+
except Exception as e:
|
|
65
|
+
console.log(f"Error parsing JSON response: {e}")
|
|
66
|
+
return 0
|
|
67
|
+
|
|
68
|
+
for i, dict_title in enumerate(data):
|
|
69
69
|
try:
|
|
70
70
|
media_search_manager.add_media({
|
|
71
71
|
'id': dict_title.get('id'),
|
|
@@ -12,7 +12,7 @@ from bs4 import BeautifulSoup
|
|
|
12
12
|
# Internal utilities
|
|
13
13
|
from StreamingCommunity.Util.headers import get_userAgent
|
|
14
14
|
from StreamingCommunity.Util.config_json import config_manager
|
|
15
|
-
from StreamingCommunity.Api.Player.Helper.Vixcloud.util import
|
|
15
|
+
from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
# Variable
|
|
@@ -22,7 +22,7 @@ max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
|
22
22
|
class GetSerieInfo:
|
|
23
23
|
def __init__(self, url):
|
|
24
24
|
"""
|
|
25
|
-
Initialize the
|
|
25
|
+
Initialize the GetSerieInfo class for scraping TV series information.
|
|
26
26
|
|
|
27
27
|
Args:
|
|
28
28
|
- url (str): The URL of the streaming site.
|
|
@@ -31,6 +31,9 @@ class GetSerieInfo:
|
|
|
31
31
|
self.headers = {'user-agent': get_userAgent()}
|
|
32
32
|
self.url = url
|
|
33
33
|
|
|
34
|
+
# Initialize the SeasonManager
|
|
35
|
+
self.seasons_manager = SeasonManager()
|
|
36
|
+
|
|
34
37
|
def setup(self, media_id: int = None, series_name: str = None):
|
|
35
38
|
"""
|
|
36
39
|
Set up the scraper with specific media details.
|
|
@@ -41,19 +44,17 @@ class GetSerieInfo:
|
|
|
41
44
|
"""
|
|
42
45
|
self.media_id = media_id
|
|
43
46
|
|
|
44
|
-
# If series name is provided, initialize series-specific
|
|
47
|
+
# If series name is provided, initialize series-specific properties
|
|
45
48
|
if series_name is not None:
|
|
46
49
|
self.is_series = True
|
|
47
50
|
self.series_name = series_name
|
|
48
|
-
self.season_manager = None
|
|
49
|
-
self.episode_manager: EpisodeManager = EpisodeManager()
|
|
50
51
|
|
|
51
52
|
def collect_info_title(self) -> None:
|
|
52
53
|
"""
|
|
53
|
-
Retrieve
|
|
54
|
+
Retrieve general information about the TV series from the streaming site.
|
|
54
55
|
|
|
55
56
|
Raises:
|
|
56
|
-
Exception: If there's an error fetching
|
|
57
|
+
Exception: If there's an error fetching series information
|
|
57
58
|
"""
|
|
58
59
|
try:
|
|
59
60
|
response = httpx.get(
|
|
@@ -63,16 +64,30 @@ class GetSerieInfo:
|
|
|
63
64
|
)
|
|
64
65
|
response.raise_for_status()
|
|
65
66
|
|
|
66
|
-
# Extract
|
|
67
|
+
# Extract series info from JSON response
|
|
67
68
|
soup = BeautifulSoup(response.text, "html.parser")
|
|
68
69
|
json_response = json.loads(soup.find("div", {"id": "app"}).get("data-page"))
|
|
69
70
|
self.version = json_response['version']
|
|
70
|
-
|
|
71
|
-
#
|
|
72
|
-
|
|
71
|
+
|
|
72
|
+
# Extract information about available seasons
|
|
73
|
+
title_data = json_response.get("props", {}).get("title", {})
|
|
74
|
+
|
|
75
|
+
# Save general series information
|
|
76
|
+
self.title_info = title_data
|
|
77
|
+
|
|
78
|
+
# Extract available seasons and add them to SeasonManager
|
|
79
|
+
seasons_data = title_data.get("seasons", [])
|
|
80
|
+
for season_data in seasons_data:
|
|
81
|
+
self.seasons_manager.add_season({
|
|
82
|
+
'id': season_data.get('id', 0),
|
|
83
|
+
'number': season_data.get('number', 0),
|
|
84
|
+
'name': f"Season {season_data.get('number', 0)}",
|
|
85
|
+
'slug': season_data.get('slug', ''),
|
|
86
|
+
'type': title_data.get('type', '')
|
|
87
|
+
})
|
|
73
88
|
|
|
74
89
|
except Exception as e:
|
|
75
|
-
logging.error(f"Error collecting
|
|
90
|
+
logging.error(f"Error collecting series info: {e}")
|
|
76
91
|
raise
|
|
77
92
|
|
|
78
93
|
def collect_info_season(self, number_season: int) -> None:
|
|
@@ -86,6 +101,12 @@ class GetSerieInfo:
|
|
|
86
101
|
Exception: If there's an error fetching episode information
|
|
87
102
|
"""
|
|
88
103
|
try:
|
|
104
|
+
# Get the season object from SeasonManager
|
|
105
|
+
season = self.seasons_manager.get_season_by_number(number_season)
|
|
106
|
+
if not season:
|
|
107
|
+
logging.error(f"Season {number_season} not found")
|
|
108
|
+
return
|
|
109
|
+
|
|
89
110
|
response = httpx.get(
|
|
90
111
|
url=f'{self.url}/titles/{self.media_id}-{self.series_name}/stagione-{number_season}',
|
|
91
112
|
headers={
|
|
@@ -98,12 +119,12 @@ class GetSerieInfo:
|
|
|
98
119
|
response.raise_for_status()
|
|
99
120
|
|
|
100
121
|
# Extract episodes from JSON response
|
|
101
|
-
json_response = response.json().get('props').get('loadedSeason').get('episodes')
|
|
122
|
+
json_response = response.json().get('props', {}).get('loadedSeason', {}).get('episodes', [])
|
|
102
123
|
|
|
103
|
-
# Add each episode to the episode manager
|
|
124
|
+
# Add each episode to the corresponding season's episode manager
|
|
104
125
|
for dict_episode in json_response:
|
|
105
|
-
|
|
126
|
+
season.episodes.add(dict_episode)
|
|
106
127
|
|
|
107
128
|
except Exception as e:
|
|
108
|
-
logging.error(f"Error collecting
|
|
109
|
-
raise
|
|
129
|
+
logging.error(f"Error collecting episodes for season {number_season}: {e}")
|
|
130
|
+
raise
|
|
@@ -22,24 +22,50 @@ console = Console()
|
|
|
22
22
|
MAP_EPISODE = config_manager.get('OUT_FOLDER', 'map_episode_name')
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def dynamic_format_number(
|
|
25
|
+
def dynamic_format_number(number_str: str) -> str:
|
|
26
26
|
"""
|
|
27
|
-
Formats
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
Formats an episode number string, intelligently handling both integer and decimal episode numbers.
|
|
28
|
+
|
|
29
|
+
This function is designed to handle various episode number formats commonly found in media series:
|
|
30
|
+
1. For integer episode numbers less than 10 (e.g., 1, 2, ..., 9), it adds a leading zero (e.g., 01, 02, ..., 09)
|
|
31
|
+
2. For integer episode numbers 10 and above, it preserves the original format without adding leading zeros
|
|
32
|
+
3. For decimal episode numbers (e.g., "7.5", "10.5"), it preserves the decimal format exactly as provided
|
|
33
|
+
|
|
34
|
+
The function is particularly useful for media file naming conventions where special episodes
|
|
35
|
+
or OVAs may have decimal notations (like episode 7.5) which should be preserved in their original format.
|
|
30
36
|
|
|
31
37
|
Parameters:
|
|
32
|
-
-
|
|
38
|
+
- number_str (str): The episode number as a string, which may contain integers or decimals.
|
|
33
39
|
|
|
34
40
|
Returns:
|
|
35
|
-
- str: The formatted number
|
|
41
|
+
- str: The formatted episode number string, with appropriate handling based on the input type.
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
44
|
+
>>> dynamic_format_number("7")
|
|
45
|
+
"07"
|
|
46
|
+
>>> dynamic_format_number("15")
|
|
47
|
+
"15"
|
|
48
|
+
>>> dynamic_format_number("7.5")
|
|
49
|
+
"7.5"
|
|
50
|
+
>>> dynamic_format_number("10.5")
|
|
51
|
+
"10.5"
|
|
36
52
|
"""
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
53
|
+
try:
|
|
54
|
+
if '.' in number_str:
|
|
55
|
+
return number_str
|
|
56
|
+
|
|
57
|
+
n = int(number_str)
|
|
58
|
+
|
|
59
|
+
if n < 10:
|
|
60
|
+
width = len(str(n)) + 1
|
|
61
|
+
else:
|
|
62
|
+
width = len(str(n))
|
|
41
63
|
|
|
42
|
-
|
|
64
|
+
return str(n).zfill(width)
|
|
65
|
+
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logging.warning(f"Could not format episode number '{number_str}': {str(e)}. Using original format.")
|
|
68
|
+
return number_str
|
|
43
69
|
|
|
44
70
|
|
|
45
71
|
def manage_selection(cmd_insert: str, max_count: int) -> List[int]:
|
|
@@ -103,14 +129,14 @@ def map_episode_title(tv_name: str, number_season: int, episode_number: int, epi
|
|
|
103
129
|
map_episode_temp = map_episode_temp.replace("%(tv_name)", os_manager.get_sanitize_file(tv_name))
|
|
104
130
|
|
|
105
131
|
if number_season != None:
|
|
106
|
-
map_episode_temp = map_episode_temp.replace("%(season)", number_season)
|
|
132
|
+
map_episode_temp = map_episode_temp.replace("%(season)", str(number_season))
|
|
107
133
|
else:
|
|
108
|
-
map_episode_temp = map_episode_temp.replace("%(season)", dynamic_format_number(0))
|
|
134
|
+
map_episode_temp = map_episode_temp.replace("%(season)", dynamic_format_number(str(0)))
|
|
109
135
|
|
|
110
136
|
if episode_number != None:
|
|
111
|
-
map_episode_temp = map_episode_temp.replace("%(episode)", dynamic_format_number(episode_number))
|
|
137
|
+
map_episode_temp = map_episode_temp.replace("%(episode)", dynamic_format_number(str(episode_number)))
|
|
112
138
|
else:
|
|
113
|
-
map_episode_temp = map_episode_temp.replace("%(episode)", dynamic_format_number(0))
|
|
139
|
+
map_episode_temp = map_episode_temp.replace("%(episode)", dynamic_format_number(str(0)))
|
|
114
140
|
|
|
115
141
|
if episode_name != None:
|
|
116
142
|
map_episode_temp = map_episode_temp.replace("%(episode_name)", os_manager.get_sanitize_file(episode_name))
|
|
@@ -192,6 +218,7 @@ def validate_episode_selection(list_episode_select: List[int], episodes_count: i
|
|
|
192
218
|
input_episodes = input(f"Enter valid episode numbers (1-{episodes_count}): ")
|
|
193
219
|
list_episode_select = list(map(int, input_episodes.split(',')))
|
|
194
220
|
|
|
221
|
+
|
|
195
222
|
def display_episodes_list(episodes_manager) -> str:
|
|
196
223
|
"""
|
|
197
224
|
Display episodes list and handle user input.
|
|
@@ -227,7 +254,7 @@ def display_episodes_list(episodes_manager) -> str:
|
|
|
227
254
|
last_command = table_show_manager.run()
|
|
228
255
|
|
|
229
256
|
if last_command in ("q", "quit"):
|
|
230
|
-
console.print("\n[red]Quit
|
|
257
|
+
console.print("\n[red]Quit ...")
|
|
231
258
|
sys.exit(0)
|
|
232
259
|
|
|
233
260
|
return last_command
|
|
@@ -31,10 +31,6 @@ class SiteConstant:
|
|
|
31
31
|
def ROOT_PATH(self):
|
|
32
32
|
return config_manager.get('OUT_FOLDER', 'root_path')
|
|
33
33
|
|
|
34
|
-
@property
|
|
35
|
-
def DOMAIN_NOW(self):
|
|
36
|
-
return config_manager.get_site(self.SITE_NAME, 'domain')
|
|
37
|
-
|
|
38
34
|
@property
|
|
39
35
|
def FULL_URL(self):
|
|
40
36
|
return config_manager.get_site(self.SITE_NAME, 'full_url').rstrip('/')
|
|
@@ -72,7 +72,7 @@ def get_select_title(table_show_manager, media_search_manager):
|
|
|
72
72
|
|
|
73
73
|
# Handle user's quit command
|
|
74
74
|
if last_command == "q" or last_command == "quit":
|
|
75
|
-
console.print("\n[red]Quit
|
|
75
|
+
console.print("\n[red]Quit ...")
|
|
76
76
|
sys.exit(0)
|
|
77
77
|
|
|
78
78
|
# Check if the selected index is within range
|
|
@@ -436,6 +436,18 @@ class HLS_Downloader:
|
|
|
436
436
|
if TELEGRAM_BOT:
|
|
437
437
|
bot.send_message(f"Contenuto già scaricato!", None)
|
|
438
438
|
return response
|
|
439
|
+
|
|
440
|
+
if GET_ONLY_LINK:
|
|
441
|
+
console.print(f"URL: [bold red]{self.m3u8_url}[/bold red]")
|
|
442
|
+
return {
|
|
443
|
+
'path': None,
|
|
444
|
+
'url': self.m3u8_url,
|
|
445
|
+
'is_master': getattr(self.m3u8_manager, 'is_master', None),
|
|
446
|
+
'msg': None,
|
|
447
|
+
'error': None,
|
|
448
|
+
'stopped': True
|
|
449
|
+
}
|
|
450
|
+
|
|
439
451
|
|
|
440
452
|
self.path_manager.setup_directories()
|
|
441
453
|
|
|
@@ -466,9 +478,8 @@ class HLS_Downloader:
|
|
|
466
478
|
|
|
467
479
|
final_file = self.merge_manager.merge()
|
|
468
480
|
self.path_manager.move_final_file(final_file)
|
|
469
|
-
self.path_manager.cleanup()
|
|
470
|
-
|
|
471
481
|
self._print_summary()
|
|
482
|
+
self.path_manager.cleanup()
|
|
472
483
|
|
|
473
484
|
return {
|
|
474
485
|
'path': self.path_manager.output_path,
|