StreamingCommunity 3.3.9__py3-none-any.whl → 3.4.2__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/hdplayer.py +0 -5
- StreamingCommunity/Api/Player/mediapolisvod.py +4 -13
- StreamingCommunity/Api/Player/supervideo.py +3 -8
- StreamingCommunity/Api/Player/sweetpixel.py +1 -9
- StreamingCommunity/Api/Player/vixcloud.py +5 -16
- StreamingCommunity/Api/Site/altadefinizione/film.py +4 -16
- StreamingCommunity/Api/Site/altadefinizione/series.py +3 -12
- StreamingCommunity/Api/Site/altadefinizione/site.py +2 -9
- StreamingCommunity/Api/Site/altadefinizione/util/ScrapeSerie.py +2 -7
- StreamingCommunity/Api/Site/animeunity/site.py +9 -24
- StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py +11 -27
- StreamingCommunity/Api/Site/animeworld/film.py +4 -2
- StreamingCommunity/Api/Site/animeworld/site.py +3 -11
- StreamingCommunity/Api/Site/animeworld/util/ScrapeSerie.py +1 -4
- StreamingCommunity/Api/Site/crunchyroll/film.py +4 -5
- StreamingCommunity/Api/Site/crunchyroll/series.py +5 -17
- StreamingCommunity/Api/Site/crunchyroll/site.py +4 -13
- StreamingCommunity/Api/Site/crunchyroll/util/ScrapeSerie.py +5 -27
- StreamingCommunity/Api/Site/crunchyroll/util/get_license.py +11 -26
- StreamingCommunity/Api/Site/guardaserie/series.py +3 -14
- StreamingCommunity/Api/Site/guardaserie/site.py +4 -12
- StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +3 -10
- StreamingCommunity/Api/Site/mediasetinfinity/film.py +11 -12
- StreamingCommunity/Api/Site/mediasetinfinity/series.py +4 -15
- StreamingCommunity/Api/Site/mediasetinfinity/site.py +16 -32
- StreamingCommunity/Api/Site/mediasetinfinity/util/ScrapeSerie.py +39 -50
- StreamingCommunity/Api/Site/mediasetinfinity/util/fix_mpd.py +3 -3
- StreamingCommunity/Api/Site/mediasetinfinity/util/get_license.py +7 -25
- StreamingCommunity/Api/Site/raiplay/film.py +6 -8
- StreamingCommunity/Api/Site/raiplay/series.py +5 -20
- StreamingCommunity/Api/Site/raiplay/site.py +45 -47
- StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py +91 -55
- StreamingCommunity/Api/Site/raiplay/util/get_license.py +3 -12
- StreamingCommunity/Api/Site/streamingcommunity/film.py +5 -16
- StreamingCommunity/Api/Site/streamingcommunity/series.py +5 -10
- StreamingCommunity/Api/Site/streamingcommunity/site.py +3 -22
- StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +11 -27
- StreamingCommunity/Api/Site/streamingwatch/__init__.py +1 -0
- StreamingCommunity/Api/Site/streamingwatch/film.py +4 -2
- StreamingCommunity/Api/Site/streamingwatch/series.py +4 -14
- StreamingCommunity/Api/Site/streamingwatch/site.py +4 -18
- StreamingCommunity/Api/Site/streamingwatch/util/ScrapeSerie.py +0 -3
- StreamingCommunity/Api/Template/Util/__init__.py +4 -2
- StreamingCommunity/Api/Template/Util/manage_ep.py +66 -0
- StreamingCommunity/Api/Template/config_loader.py +0 -7
- StreamingCommunity/Lib/Downloader/DASH/decrypt.py +54 -1
- StreamingCommunity/Lib/Downloader/DASH/downloader.py +186 -70
- StreamingCommunity/Lib/Downloader/DASH/parser.py +2 -3
- StreamingCommunity/Lib/Downloader/DASH/segments.py +109 -68
- StreamingCommunity/Lib/Downloader/HLS/downloader.py +100 -82
- StreamingCommunity/Lib/Downloader/HLS/segments.py +40 -28
- StreamingCommunity/Lib/Downloader/MP4/downloader.py +16 -4
- StreamingCommunity/Lib/FFmpeg/capture.py +37 -5
- StreamingCommunity/Lib/FFmpeg/command.py +32 -90
- StreamingCommunity/Lib/M3U8/estimator.py +47 -1
- StreamingCommunity/Lib/TMBD/tmdb.py +2 -4
- StreamingCommunity/TelegramHelp/config.json +0 -1
- StreamingCommunity/Upload/update.py +19 -6
- StreamingCommunity/Upload/version.py +1 -1
- StreamingCommunity/Util/config_json.py +28 -21
- StreamingCommunity/Util/http_client.py +28 -0
- StreamingCommunity/Util/os.py +16 -6
- StreamingCommunity/Util/table.py +50 -8
- {streamingcommunity-3.3.9.dist-info → streamingcommunity-3.4.2.dist-info}/METADATA +1 -3
- streamingcommunity-3.4.2.dist-info/RECORD +111 -0
- streamingcommunity-3.3.9.dist-info/RECORD +0 -111
- {streamingcommunity-3.3.9.dist-info → streamingcommunity-3.4.2.dist-info}/WHEEL +0 -0
- {streamingcommunity-3.3.9.dist-info → streamingcommunity-3.4.2.dist-info}/entry_points.txt +0 -0
- {streamingcommunity-3.3.9.dist-info → streamingcommunity-3.4.2.dist-info}/licenses/LICENSE +0 -0
- {streamingcommunity-3.3.9.dist-info → streamingcommunity-3.4.2.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# 16.03.25
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
-
from typing import Tuple
|
|
5
4
|
from urllib.parse import urlparse, parse_qs
|
|
5
|
+
from typing import Tuple
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
# External library
|
|
@@ -12,7 +12,7 @@ from rich.prompt import Prompt
|
|
|
12
12
|
|
|
13
13
|
# Internal utilities
|
|
14
14
|
from StreamingCommunity.Util.message import start_message
|
|
15
|
-
from StreamingCommunity.Util.os import os_manager
|
|
15
|
+
from StreamingCommunity.Util.os import os_manager
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
# Logic class
|
|
@@ -22,7 +22,8 @@ from StreamingCommunity.Api.Template.Util import (
|
|
|
22
22
|
map_episode_title,
|
|
23
23
|
validate_selection,
|
|
24
24
|
validate_episode_selection,
|
|
25
|
-
display_episodes_list
|
|
25
|
+
display_episodes_list,
|
|
26
|
+
display_seasons_list
|
|
26
27
|
)
|
|
27
28
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
28
29
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
|
@@ -72,7 +73,6 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
72
73
|
|
|
73
74
|
# Download the episode
|
|
74
75
|
dash_process = DASH_Downloader(
|
|
75
|
-
cdm_device=get_wvd_path(),
|
|
76
76
|
license_url='https://www.crunchyroll.com/license/v1/license/widevine',
|
|
77
77
|
mpd_url=mpd_url,
|
|
78
78
|
mpd_sub_list=mpd_list_sub,
|
|
@@ -161,20 +161,11 @@ def download_series(select_season: MediaItem, season_selection: str = None, epis
|
|
|
161
161
|
- episode_selection (str, optional): Pre-defined episode selection that bypasses manual input
|
|
162
162
|
"""
|
|
163
163
|
scrape_serie = GetSerieInfo(select_season.url.split("/")[-1])
|
|
164
|
-
|
|
165
|
-
# Get total number of seasons
|
|
166
164
|
seasons_count = scrape_serie.getNumberSeason()
|
|
167
165
|
|
|
168
|
-
# Prompt user for season selection and download episodes
|
|
169
|
-
console.print(f"\n[green]Seasons found: [red]{seasons_count}")
|
|
170
|
-
|
|
171
166
|
# If season_selection is provided, use it instead of asking for input
|
|
172
167
|
if season_selection is None:
|
|
173
|
-
index_season_selected =
|
|
174
|
-
"\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
|
|
175
|
-
"[yellow](e.g., 1-2) [cyan]for a range of seasons, or [yellow](e.g., 3-*) [cyan]to download from a specific season to the end"
|
|
176
|
-
)
|
|
177
|
-
|
|
168
|
+
index_season_selected = display_seasons_list(scrape_serie.seasons_manager)
|
|
178
169
|
else:
|
|
179
170
|
index_season_selected = season_selection
|
|
180
171
|
console.print(f"\n[cyan]Using provided season selection: [yellow]{season_selection}")
|
|
@@ -186,9 +177,6 @@ def download_series(select_season: MediaItem, season_selection: str = None, epis
|
|
|
186
177
|
# Loop through the selected seasons and download episodes
|
|
187
178
|
for i_season in list_season_select:
|
|
188
179
|
if len(list_season_select) > 1 or index_season_selected == "*":
|
|
189
|
-
# Download all episodes if multiple seasons are selected or if '*' is used
|
|
190
180
|
download_episode(i_season, scrape_serie, download_all=True)
|
|
191
|
-
|
|
192
181
|
else:
|
|
193
|
-
# Otherwise, let the user select specific episodes for the single season
|
|
194
182
|
download_episode(i_season, scrape_serie, download_all=False, episode_selection=episode_selection)
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
# 16.03.25
|
|
2
2
|
|
|
3
|
-
import sys
|
|
4
|
-
|
|
5
3
|
|
|
6
4
|
# External libraries
|
|
7
|
-
from curl_cffi import requests
|
|
8
5
|
from rich.console import Console
|
|
9
6
|
|
|
10
7
|
|
|
11
8
|
# Internal utilities
|
|
12
9
|
from StreamingCommunity.Util.config_json import config_manager
|
|
10
|
+
from StreamingCommunity.Util.http_client import create_client_curl
|
|
13
11
|
from StreamingCommunity.Util.table import TVShowManager
|
|
14
12
|
|
|
15
13
|
|
|
@@ -23,7 +21,6 @@ from .util.get_license import CrunchyrollClient
|
|
|
23
21
|
console = Console()
|
|
24
22
|
media_search_manager = MediaManager()
|
|
25
23
|
table_show_manager = TVShowManager()
|
|
26
|
-
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
27
24
|
|
|
28
25
|
|
|
29
26
|
def title_search(query: str) -> int:
|
|
@@ -43,13 +40,13 @@ def title_search(query: str) -> int:
|
|
|
43
40
|
config = config_manager.get_dict("SITE_LOGIN", "crunchyroll")
|
|
44
41
|
if not config.get('device_id') or not config.get('etp_rt'):
|
|
45
42
|
console.print("[bold red] device_id or etp_rt is missing or empty in config.json.[/bold red]")
|
|
46
|
-
|
|
43
|
+
raise Exception("device_id or etp_rt is missing or empty in config.json.")
|
|
47
44
|
|
|
48
45
|
# Initialize Crunchyroll client
|
|
49
46
|
client = CrunchyrollClient()
|
|
50
47
|
if not client.start():
|
|
51
48
|
console.print("[bold red] Failed to authenticate with Crunchyroll.[/bold red]")
|
|
52
|
-
|
|
49
|
+
raise Exception("Failed to authenticate with Crunchyroll.")
|
|
53
50
|
|
|
54
51
|
# Build new Crunchyroll API search URL
|
|
55
52
|
api_url = "https://www.crunchyroll.com/content/v2/discover/search"
|
|
@@ -68,13 +65,7 @@ def title_search(query: str) -> int:
|
|
|
68
65
|
console.print(f"[cyan]Search url: [yellow]{api_url}")
|
|
69
66
|
|
|
70
67
|
try:
|
|
71
|
-
response =
|
|
72
|
-
api_url,
|
|
73
|
-
params=params,
|
|
74
|
-
headers=headers,
|
|
75
|
-
timeout=max_timeout,
|
|
76
|
-
impersonate="chrome136"
|
|
77
|
-
)
|
|
68
|
+
response = create_client_curl(headers=headers).get(api_url, params=params)
|
|
78
69
|
response.raise_for_status()
|
|
79
70
|
|
|
80
71
|
except Exception as e:
|
|
@@ -3,31 +3,18 @@
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
# External libraries
|
|
7
|
-
from curl_cffi import requests
|
|
8
|
-
|
|
9
|
-
|
|
10
6
|
# Internal utilities
|
|
11
|
-
from StreamingCommunity.Util.config_json import config_manager
|
|
12
7
|
from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager
|
|
8
|
+
from StreamingCommunity.Util.http_client import create_client_curl
|
|
13
9
|
from .get_license import CrunchyrollClient
|
|
14
10
|
|
|
15
11
|
|
|
16
|
-
# Variable
|
|
17
|
-
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
18
|
-
|
|
19
|
-
|
|
20
12
|
def get_series_seasons(series_id, headers, params):
|
|
21
13
|
"""
|
|
22
14
|
Fetches the seasons for a given series ID from Crunchyroll.
|
|
23
15
|
"""
|
|
24
16
|
url = f'https://www.crunchyroll.com/content/v2/cms/series/{series_id}/seasons'
|
|
25
|
-
response =
|
|
26
|
-
url,
|
|
27
|
-
params=params,
|
|
28
|
-
headers=headers,
|
|
29
|
-
impersonate="chrome136"
|
|
30
|
-
)
|
|
17
|
+
response = create_client_curl(headers=headers).get(url, params=params)
|
|
31
18
|
return response
|
|
32
19
|
|
|
33
20
|
|
|
@@ -36,12 +23,7 @@ def get_season_episodes(season_id, headers, params):
|
|
|
36
23
|
Fetches the episodes for a given season ID from Crunchyroll.
|
|
37
24
|
"""
|
|
38
25
|
url = f'https://www.crunchyroll.com/content/v2/cms/seasons/{season_id}/episodes'
|
|
39
|
-
response =
|
|
40
|
-
url,
|
|
41
|
-
params=params,
|
|
42
|
-
headers=headers,
|
|
43
|
-
impersonate="chrome136"
|
|
44
|
-
)
|
|
26
|
+
response = create_client_curl(headers=headers).get(url, params=params)
|
|
45
27
|
return response
|
|
46
28
|
|
|
47
29
|
|
|
@@ -137,12 +119,8 @@ class GetSerieInfo:
|
|
|
137
119
|
'ratings': 'true',
|
|
138
120
|
'locale': 'it-IT',
|
|
139
121
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
params=params,
|
|
143
|
-
headers=headers,
|
|
144
|
-
impersonate="chrome136"
|
|
145
|
-
)
|
|
122
|
+
|
|
123
|
+
response = create_client_curl(headers=headers).get(url, params=params)
|
|
146
124
|
|
|
147
125
|
if response.status_code != 200:
|
|
148
126
|
logging.warning(f"Failed to fetch audio locales for episode {episode_id}")
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# 28.07.25
|
|
2
2
|
|
|
3
3
|
from typing import Tuple, List, Dict
|
|
4
|
-
from curl_cffi import requests
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
# Internal utilities
|
|
8
7
|
from StreamingCommunity.Util.config_json import config_manager
|
|
8
|
+
from StreamingCommunity.Util.http_client import create_client_curl
|
|
9
9
|
from StreamingCommunity.Util.headers import get_userAgent
|
|
10
10
|
|
|
11
11
|
|
|
@@ -44,17 +44,13 @@ class CrunchyrollClient:
|
|
|
44
44
|
headers['authorization'] = f'Basic {PUBLIC_TOKEN}'
|
|
45
45
|
headers['content-type'] = 'application/x-www-form-urlencoded'
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
'
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
'grant_type': 'etp_rt_cookie',
|
|
55
|
-
},
|
|
56
|
-
impersonate="chrome136"
|
|
57
|
-
)
|
|
47
|
+
data = {
|
|
48
|
+
'device_id': self.device_id,
|
|
49
|
+
'device_type': 'Chrome on Windows',
|
|
50
|
+
'grant_type': 'etp_rt_cookie',
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
response = create_client_curl(headers=headers).post('https://www.crunchyroll.com/auth/v1/token', cookies=self._get_cookies(), data=data)
|
|
58
54
|
|
|
59
55
|
if response.status_code == 400:
|
|
60
56
|
print("Error 400: Please enter a correct 'etp_rt' value in config.json. You can find the value in the request headers.")
|
|
@@ -69,14 +65,8 @@ class CrunchyrollClient:
|
|
|
69
65
|
|
|
70
66
|
def get_streams(self, media_id: str) -> Dict:
|
|
71
67
|
"""Ottieni gli stream disponibili"""
|
|
72
|
-
response =
|
|
73
|
-
|
|
74
|
-
cookies=self._get_cookies(),
|
|
75
|
-
headers=self._get_headers(),
|
|
76
|
-
params={'locale': self.locale},
|
|
77
|
-
impersonate="chrome136"
|
|
78
|
-
)
|
|
79
|
-
|
|
68
|
+
response = create_client_curl(headers=self._get_headers()).get(f'https://www.crunchyroll.com/playback/v3/{media_id}/web/chrome/play', cookies=self._get_cookies(), params={'locale': self.locale})
|
|
69
|
+
|
|
80
70
|
if response.status_code == 403:
|
|
81
71
|
raise Exception("Playback is Rejected: The current subscription does not have access to this content")
|
|
82
72
|
|
|
@@ -94,12 +84,7 @@ class CrunchyrollClient:
|
|
|
94
84
|
|
|
95
85
|
def delete_active_stream(self, media_id: str, token: str) -> bool:
|
|
96
86
|
"""Elimina uno stream attivo"""
|
|
97
|
-
response =
|
|
98
|
-
f'https://www.crunchyroll.com/playback/v1/token/{media_id}/{token}',
|
|
99
|
-
cookies=self._get_cookies(),
|
|
100
|
-
headers=self._get_headers(),
|
|
101
|
-
impersonate="chrome136"
|
|
102
|
-
)
|
|
87
|
+
response = create_client_curl(headers=self._get_headers()).delete(f'https://www.crunchyroll.com/playback/v1/token/{media_id}/{token}', cookies=self._get_cookies())
|
|
103
88
|
response.raise_for_status()
|
|
104
89
|
return response.status_code in [200, 204]
|
|
105
90
|
|
|
@@ -20,7 +20,8 @@ from StreamingCommunity.Api.Template.Util import (
|
|
|
20
20
|
dynamic_format_number,
|
|
21
21
|
validate_selection,
|
|
22
22
|
validate_episode_selection,
|
|
23
|
-
display_episodes_list
|
|
23
|
+
display_episodes_list,
|
|
24
|
+
display_seasons_list
|
|
24
25
|
)
|
|
25
26
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
26
27
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
|
@@ -145,19 +146,11 @@ def download_series(dict_serie: MediaItem, season_selection: str = None, episode
|
|
|
145
146
|
|
|
146
147
|
# Create class
|
|
147
148
|
scrape_serie = GetSerieInfo(dict_serie)
|
|
148
|
-
|
|
149
|
-
# Get season count
|
|
150
149
|
seasons_count = scrape_serie.get_seasons_number()
|
|
151
|
-
|
|
152
|
-
# Prompt user for season selection and download episodes
|
|
153
|
-
console.print(f"\n[green]Seasons found: [red]{seasons_count}")
|
|
154
150
|
|
|
155
151
|
# If season_selection is provided, use it instead of asking for input
|
|
156
152
|
if season_selection is None:
|
|
157
|
-
index_season_selected =
|
|
158
|
-
"\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
|
|
159
|
-
"[yellow](e.g., 1-2) [cyan]for a range of seasons, or [yellow](e.g., 3-*) [cyan]to download from a specific season to the end"
|
|
160
|
-
)
|
|
153
|
+
index_season_selected = display_seasons_list(scrape_serie.seasons_manager)
|
|
161
154
|
else:
|
|
162
155
|
index_season_selected = season_selection
|
|
163
156
|
console.print(f"\n[cyan]Using provided season selection: [yellow]{season_selection}")
|
|
@@ -169,10 +162,6 @@ def download_series(dict_serie: MediaItem, season_selection: str = None, episode
|
|
|
169
162
|
# Loop through the selected seasons and download episodes
|
|
170
163
|
for i_season in list_season_select:
|
|
171
164
|
if len(list_season_select) > 1 or index_season_selected == "*":
|
|
172
|
-
|
|
173
|
-
# Download all episodes if multiple seasons are selected or if '*' is used
|
|
174
165
|
download_episode(scrape_serie, i_season, download_all=True)
|
|
175
166
|
else:
|
|
176
|
-
|
|
177
|
-
# Otherwise, let the user select specific episodes for the single season
|
|
178
167
|
download_episode(scrape_serie, i_season, download_all=False, episode_selection=episode_selection)
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# 09.06.24
|
|
2
2
|
|
|
3
|
+
|
|
3
4
|
# External libraries
|
|
4
|
-
import httpx
|
|
5
5
|
from bs4 import BeautifulSoup
|
|
6
6
|
from rich.console import Console
|
|
7
7
|
|
|
8
|
+
|
|
8
9
|
# Internal utilities
|
|
9
|
-
from StreamingCommunity.Util.config_json import config_manager
|
|
10
10
|
from StreamingCommunity.Util.headers import get_userAgent
|
|
11
|
+
from StreamingCommunity.Util.http_client import create_client
|
|
11
12
|
from StreamingCommunity.Util.table import TVShowManager
|
|
12
13
|
|
|
13
14
|
|
|
@@ -20,8 +21,6 @@ from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
|
|
20
21
|
console = Console()
|
|
21
22
|
media_search_manager = MediaManager()
|
|
22
23
|
table_show_manager = TVShowManager()
|
|
23
|
-
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
24
|
-
|
|
25
24
|
|
|
26
25
|
|
|
27
26
|
def title_search(query: str) -> int:
|
|
@@ -41,15 +40,8 @@ def title_search(query: str) -> int:
|
|
|
41
40
|
console.print(f"[cyan]Search url: [yellow]{search_url}")
|
|
42
41
|
|
|
43
42
|
try:
|
|
44
|
-
response =
|
|
45
|
-
search_url,
|
|
46
|
-
headers={'user-agent': get_userAgent()},
|
|
47
|
-
timeout=max_timeout,
|
|
48
|
-
follow_redirects=True,
|
|
49
|
-
verify=False
|
|
50
|
-
)
|
|
43
|
+
response = create_client(headers={'user-agent': get_userAgent()}).get(search_url)
|
|
51
44
|
response.raise_for_status()
|
|
52
|
-
|
|
53
45
|
except Exception as e:
|
|
54
46
|
console.print(f"[red]Site: {site_constant.SITE_NAME}, request search error: {e}")
|
|
55
47
|
return 0
|
|
@@ -5,22 +5,18 @@ from typing import List, Dict
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
# External libraries
|
|
8
|
-
import httpx
|
|
9
8
|
from bs4 import BeautifulSoup
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
# Internal utilities
|
|
13
12
|
from StreamingCommunity.Util.headers import get_userAgent
|
|
14
|
-
from StreamingCommunity.Util.
|
|
13
|
+
from StreamingCommunity.Util.http_client import create_client
|
|
15
14
|
|
|
16
15
|
|
|
17
16
|
# Logic class
|
|
18
17
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
|
19
18
|
|
|
20
19
|
|
|
21
|
-
# Variable
|
|
22
|
-
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
23
|
-
|
|
24
20
|
|
|
25
21
|
class GetSerieInfo:
|
|
26
22
|
def __init__(self, dict_serie: MediaItem) -> None:
|
|
@@ -43,8 +39,7 @@ class GetSerieInfo:
|
|
|
43
39
|
int: Number of seasons of the TV series. Returns -1 if parsing fails.
|
|
44
40
|
"""
|
|
45
41
|
try:
|
|
46
|
-
|
|
47
|
-
response = httpx.get(self.url, headers=self.headers, timeout=max_timeout, follow_redirects=True)
|
|
42
|
+
response = create_client(headers=self.headers).get(self.url)
|
|
48
43
|
response.raise_for_status()
|
|
49
44
|
|
|
50
45
|
# Find the seasons container
|
|
@@ -72,9 +67,7 @@ class GetSerieInfo:
|
|
|
72
67
|
List[Dict[str, str]]: List of dictionaries containing episode information.
|
|
73
68
|
"""
|
|
74
69
|
try:
|
|
75
|
-
|
|
76
|
-
# Make an HTTP request to the series URL
|
|
77
|
-
response = httpx.get(self.url, headers=self.headers, timeout=max_timeout, follow_redirects=True)
|
|
70
|
+
response = create_client(headers=self.headers).get(self.url)
|
|
78
71
|
response.raise_for_status()
|
|
79
72
|
|
|
80
73
|
# Parse HTML content of the page
|
|
@@ -9,7 +9,8 @@ from rich.console import Console
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
# Internal utilities
|
|
12
|
-
from StreamingCommunity.Util.
|
|
12
|
+
from StreamingCommunity.Util.config_json import config_manager
|
|
13
|
+
from StreamingCommunity.Util.os import os_manager
|
|
13
14
|
from StreamingCommunity.Util.message import start_message
|
|
14
15
|
from StreamingCommunity.Util.headers import get_headers
|
|
15
16
|
|
|
@@ -22,11 +23,12 @@ from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
|
|
22
23
|
# Player
|
|
23
24
|
from .util.fix_mpd import get_manifest
|
|
24
25
|
from StreamingCommunity import DASH_Downloader
|
|
25
|
-
from .util.get_license import
|
|
26
|
+
from .util.get_license import get_playback_url, get_tracking_info, generate_license_url
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
# Variable
|
|
29
30
|
console = Console()
|
|
31
|
+
extension_output = config_manager.get("M3U8_CONVERSION", "extension")
|
|
30
32
|
|
|
31
33
|
|
|
32
34
|
def download_film(select_title: MediaItem) -> Tuple[str, bool]:
|
|
@@ -43,21 +45,18 @@ def download_film(select_title: MediaItem) -> Tuple[str, bool]:
|
|
|
43
45
|
console.print(f"\n[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{select_title.name}[/cyan] \n")
|
|
44
46
|
|
|
45
47
|
# Define the filename and path for the downloaded film
|
|
46
|
-
title_name = os_manager.get_sanitize_file(select_title.name) +
|
|
47
|
-
mp4_path = os.path.join(site_constant.MOVIE_FOLDER, title_name.replace(
|
|
48
|
+
title_name = os_manager.get_sanitize_file(select_title.name, select_title.date) + extension_output
|
|
49
|
+
mp4_path = os.path.join(site_constant.MOVIE_FOLDER, title_name.replace(extension_output, ""))
|
|
48
50
|
|
|
49
|
-
#
|
|
50
|
-
|
|
51
|
+
# Get playback URL and tracking info
|
|
52
|
+
playback_json = get_playback_url(select_title.id)
|
|
53
|
+
tracking_info = get_tracking_info(playback_json)['videos'][0]
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
license_url = generate_license_url(bearer, tracking_info)
|
|
56
|
-
mpd_url = get_manifest(tracking_info['video_src'])
|
|
55
|
+
license_url = generate_license_url(tracking_info)
|
|
56
|
+
mpd_url = get_manifest(tracking_info['url'])
|
|
57
57
|
|
|
58
58
|
# Download the episode
|
|
59
59
|
dash_process = DASH_Downloader(
|
|
60
|
-
cdm_device=get_wvd_path(),
|
|
61
60
|
license_url=license_url,
|
|
62
61
|
mpd_url=mpd_url,
|
|
63
62
|
output_path=os.path.join(mp4_path, title_name),
|
|
@@ -12,7 +12,7 @@ from rich.prompt import Prompt
|
|
|
12
12
|
# Internal utilities
|
|
13
13
|
from StreamingCommunity.Util.headers import get_headers
|
|
14
14
|
from StreamingCommunity.Util.message import start_message
|
|
15
|
-
from StreamingCommunity.Util.os import os_manager
|
|
15
|
+
from StreamingCommunity.Util.os import os_manager
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
# Logic class
|
|
@@ -22,7 +22,8 @@ from StreamingCommunity.Api.Template.Util import (
|
|
|
22
22
|
map_episode_title,
|
|
23
23
|
validate_selection,
|
|
24
24
|
validate_episode_selection,
|
|
25
|
-
display_episodes_list
|
|
25
|
+
display_episodes_list,
|
|
26
|
+
display_seasons_list
|
|
26
27
|
)
|
|
27
28
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
28
29
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
|
@@ -70,7 +71,6 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
70
71
|
|
|
71
72
|
# Download the episode
|
|
72
73
|
dash_process = DASH_Downloader(
|
|
73
|
-
cdm_device=get_wvd_path(),
|
|
74
74
|
license_url=license_url,
|
|
75
75
|
mpd_url=mpd_url,
|
|
76
76
|
mpd_sub_list=tracking_info['subtitles'],
|
|
@@ -149,20 +149,11 @@ def download_series(select_season: MediaItem, season_selection: str = None, epis
|
|
|
149
149
|
- episode_selection (str, optional): Pre-defined episode selection that bypasses manual input
|
|
150
150
|
"""
|
|
151
151
|
scrape_serie = GetSerieInfo(select_season.url)
|
|
152
|
-
|
|
153
|
-
# Get total number of seasons
|
|
154
152
|
seasons_count = scrape_serie.getNumberSeason()
|
|
155
153
|
|
|
156
|
-
# Prompt user for season selection and download episodes
|
|
157
|
-
console.print(f"\n[green]Seasons found: [red]{seasons_count}")
|
|
158
|
-
|
|
159
154
|
# If season_selection is provided, use it instead of asking for input
|
|
160
155
|
if season_selection is None:
|
|
161
|
-
index_season_selected =
|
|
162
|
-
"\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
|
|
163
|
-
"[yellow](e.g., 1-2) [cyan]for a range of seasons, or [yellow](e.g., 3-*) [cyan]to download from a specific season to the end"
|
|
164
|
-
)
|
|
165
|
-
|
|
156
|
+
index_season_selected = display_seasons_list(scrape_serie.seasons_manager)
|
|
166
157
|
else:
|
|
167
158
|
index_season_selected = season_selection
|
|
168
159
|
console.print(f"\n[cyan]Using provided season selection: [yellow]{season_selection}")
|
|
@@ -174,8 +165,6 @@ def download_series(select_season: MediaItem, season_selection: str = None, epis
|
|
|
174
165
|
# Loop through the selected seasons and download episodes
|
|
175
166
|
for i_season in list_season_select:
|
|
176
167
|
if len(list_season_select) > 1 or index_season_selected == "*":
|
|
177
|
-
# Download all episodes if multiple seasons are selected or if '*' is used
|
|
178
168
|
download_episode(i_season, scrape_serie, download_all=True)
|
|
179
169
|
else:
|
|
180
|
-
# Otherwise, let the user select specific episodes for the single season
|
|
181
170
|
download_episode(i_season, scrape_serie, download_all=False, episode_selection=episode_selection)
|
|
@@ -4,12 +4,11 @@ from datetime import datetime
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
# External libraries
|
|
7
|
-
import httpx
|
|
8
7
|
from rich.console import Console
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
# Internal utilities
|
|
12
|
-
from StreamingCommunity.Util.
|
|
11
|
+
from StreamingCommunity.Util.http_client import create_client
|
|
13
12
|
from StreamingCommunity.Util.table import TVShowManager
|
|
14
13
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
15
14
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
|
@@ -23,7 +22,6 @@ from .util.get_license import get_bearer_token
|
|
|
23
22
|
console = Console()
|
|
24
23
|
media_search_manager = MediaManager()
|
|
25
24
|
table_show_manager = TVShowManager()
|
|
26
|
-
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
27
25
|
|
|
28
26
|
|
|
29
27
|
def title_search(query: str) -> int:
|
|
@@ -46,15 +44,9 @@ def title_search(query: str) -> int:
|
|
|
46
44
|
'extensions': f'{{"persistedQuery":{{"version":1,"sha256Hash":"{class_mediaset_api.getHash256()}"}}}}',
|
|
47
45
|
'variables': f'{{"first":10,"property":"search","query":"{query}","uxReference":"filteredSearch"}}',
|
|
48
46
|
}
|
|
47
|
+
|
|
49
48
|
try:
|
|
50
|
-
response =
|
|
51
|
-
search_url,
|
|
52
|
-
headers=class_mediaset_api.generate_request_headers(),
|
|
53
|
-
params=params,
|
|
54
|
-
timeout=max_timeout,
|
|
55
|
-
follow_redirects=True
|
|
56
|
-
)
|
|
57
|
-
|
|
49
|
+
response = create_client(headers=class_mediaset_api.generate_request_headers()).get(search_url, params=params)
|
|
58
50
|
response.raise_for_status()
|
|
59
51
|
except Exception as e:
|
|
60
52
|
console.print(f"[red]Site: {site_constant.SITE_NAME}, request search error: {e}")
|
|
@@ -66,33 +58,25 @@ def title_search(query: str) -> int:
|
|
|
66
58
|
|
|
67
59
|
# Process items
|
|
68
60
|
for item in items:
|
|
69
|
-
|
|
61
|
+
is_series = (
|
|
62
|
+
item.get("__typename") == "SeriesItem"
|
|
63
|
+
or item.get("cardLink", {}).get("referenceType") == "series"
|
|
64
|
+
or bool(item.get("seasons"))
|
|
65
|
+
)
|
|
66
|
+
item_type = "tv" if is_series else "film"
|
|
70
67
|
|
|
71
|
-
#
|
|
72
|
-
date = item.get("year")
|
|
68
|
+
# Get date
|
|
69
|
+
date = item.get("year") or ''
|
|
73
70
|
if not date:
|
|
74
|
-
updated = item.get("updated")
|
|
71
|
+
updated = item.get("updated") or item.get("r") or ''
|
|
75
72
|
if updated:
|
|
76
73
|
try:
|
|
77
|
-
date = datetime.fromisoformat(updated.replace("Z", "+00:00")).year
|
|
74
|
+
date = datetime.fromisoformat(str(updated).replace("Z", "+00:00")).year
|
|
78
75
|
except Exception:
|
|
79
|
-
|
|
80
|
-
timestamp_ms = int(updated)
|
|
81
|
-
date = datetime.fromtimestamp(timestamp_ms / 1000).year
|
|
82
|
-
except Exception:
|
|
83
|
-
date = ""
|
|
84
|
-
|
|
85
|
-
date = item.get('year', '')
|
|
86
|
-
if not date and item.get('updated'):
|
|
87
|
-
try:
|
|
88
|
-
|
|
89
|
-
timestamp_ms = int(item.get('updated', 0))
|
|
90
|
-
date = datetime.fromtimestamp(timestamp_ms / 1000).year
|
|
91
|
-
except (ValueError, TypeError):
|
|
92
|
-
date = ''
|
|
76
|
+
date = ''
|
|
93
77
|
|
|
94
78
|
media_search_manager.add_media({
|
|
95
|
-
"url": item.get("cardLink",
|
|
79
|
+
"url": item.get("cardLink", {}).get("value", ""),
|
|
96
80
|
"id": item.get("guid", ""),
|
|
97
81
|
"name": item.get("cardTitle", "No Title"),
|
|
98
82
|
"type": item_type,
|
|
@@ -100,4 +84,4 @@ def title_search(query: str) -> int:
|
|
|
100
84
|
"date": date,
|
|
101
85
|
})
|
|
102
86
|
|
|
103
|
-
|
|
87
|
+
return media_search_manager.get_length()
|