StreamingCommunity 3.3.8__py3-none-any.whl → 3.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of StreamingCommunity might be problematic. Click here for more details.

Files changed (64) hide show
  1. StreamingCommunity/Api/Player/hdplayer.py +0 -5
  2. StreamingCommunity/Api/Player/mediapolisvod.py +4 -13
  3. StreamingCommunity/Api/Player/supervideo.py +3 -8
  4. StreamingCommunity/Api/Player/sweetpixel.py +1 -9
  5. StreamingCommunity/Api/Player/vixcloud.py +5 -16
  6. StreamingCommunity/Api/Site/altadefinizione/film.py +4 -15
  7. StreamingCommunity/Api/Site/altadefinizione/site.py +2 -7
  8. StreamingCommunity/Api/Site/altadefinizione/util/ScrapeSerie.py +2 -7
  9. StreamingCommunity/Api/Site/animeunity/site.py +9 -24
  10. StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py +11 -27
  11. StreamingCommunity/Api/Site/animeworld/film.py +4 -2
  12. StreamingCommunity/Api/Site/animeworld/site.py +3 -11
  13. StreamingCommunity/Api/Site/animeworld/util/ScrapeSerie.py +1 -4
  14. StreamingCommunity/Api/Site/crunchyroll/film.py +17 -8
  15. StreamingCommunity/Api/Site/crunchyroll/series.py +8 -9
  16. StreamingCommunity/Api/Site/crunchyroll/site.py +14 -16
  17. StreamingCommunity/Api/Site/crunchyroll/util/ScrapeSerie.py +18 -65
  18. StreamingCommunity/Api/Site/crunchyroll/util/get_license.py +97 -106
  19. StreamingCommunity/Api/Site/guardaserie/site.py +4 -12
  20. StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +3 -10
  21. StreamingCommunity/Api/Site/mediasetinfinity/film.py +11 -12
  22. StreamingCommunity/Api/Site/mediasetinfinity/series.py +1 -2
  23. StreamingCommunity/Api/Site/mediasetinfinity/site.py +3 -11
  24. StreamingCommunity/Api/Site/mediasetinfinity/util/ScrapeSerie.py +39 -50
  25. StreamingCommunity/Api/Site/mediasetinfinity/util/fix_mpd.py +3 -3
  26. StreamingCommunity/Api/Site/mediasetinfinity/util/get_license.py +8 -26
  27. StreamingCommunity/Api/Site/raiplay/film.py +6 -7
  28. StreamingCommunity/Api/Site/raiplay/series.py +1 -12
  29. StreamingCommunity/Api/Site/raiplay/site.py +8 -24
  30. StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py +15 -22
  31. StreamingCommunity/Api/Site/raiplay/util/get_license.py +3 -12
  32. StreamingCommunity/Api/Site/streamingcommunity/film.py +5 -16
  33. StreamingCommunity/Api/Site/streamingcommunity/site.py +3 -22
  34. StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +11 -26
  35. StreamingCommunity/Api/Site/streamingwatch/__init__.py +1 -0
  36. StreamingCommunity/Api/Site/streamingwatch/film.py +4 -2
  37. StreamingCommunity/Api/Site/streamingwatch/series.py +1 -1
  38. StreamingCommunity/Api/Site/streamingwatch/site.py +4 -18
  39. StreamingCommunity/Api/Site/streamingwatch/util/ScrapeSerie.py +0 -3
  40. StreamingCommunity/Api/Template/config_loader.py +0 -7
  41. StreamingCommunity/Lib/Downloader/DASH/cdm_helpher.py +8 -3
  42. StreamingCommunity/Lib/Downloader/DASH/decrypt.py +55 -1
  43. StreamingCommunity/Lib/Downloader/DASH/downloader.py +139 -55
  44. StreamingCommunity/Lib/Downloader/DASH/parser.py +458 -101
  45. StreamingCommunity/Lib/Downloader/DASH/segments.py +131 -74
  46. StreamingCommunity/Lib/Downloader/HLS/downloader.py +31 -50
  47. StreamingCommunity/Lib/Downloader/HLS/segments.py +266 -365
  48. StreamingCommunity/Lib/Downloader/MP4/downloader.py +1 -1
  49. StreamingCommunity/Lib/FFmpeg/capture.py +37 -5
  50. StreamingCommunity/Lib/FFmpeg/command.py +35 -93
  51. StreamingCommunity/Lib/M3U8/estimator.py +0 -1
  52. StreamingCommunity/Lib/TMBD/tmdb.py +2 -4
  53. StreamingCommunity/TelegramHelp/config.json +0 -1
  54. StreamingCommunity/Upload/version.py +1 -1
  55. StreamingCommunity/Util/config_json.py +28 -21
  56. StreamingCommunity/Util/http_client.py +28 -0
  57. StreamingCommunity/Util/os.py +16 -6
  58. {streamingcommunity-3.3.8.dist-info → streamingcommunity-3.4.0.dist-info}/METADATA +1 -3
  59. streamingcommunity-3.4.0.dist-info/RECORD +111 -0
  60. streamingcommunity-3.3.8.dist-info/RECORD +0 -111
  61. {streamingcommunity-3.3.8.dist-info → streamingcommunity-3.4.0.dist-info}/WHEEL +0 -0
  62. {streamingcommunity-3.3.8.dist-info → streamingcommunity-3.4.0.dist-info}/entry_points.txt +0 -0
  63. {streamingcommunity-3.3.8.dist-info → streamingcommunity-3.4.0.dist-info}/licenses/LICENSE +0 -0
  64. {streamingcommunity-3.3.8.dist-info → streamingcommunity-3.4.0.dist-info}/top_level.txt +0 -0
@@ -4,27 +4,25 @@ import sys
4
4
 
5
5
 
6
6
  # External libraries
7
- from curl_cffi import requests
8
7
  from rich.console import Console
9
8
 
10
9
 
11
10
  # Internal utilities
12
11
  from StreamingCommunity.Util.config_json import config_manager
13
- from StreamingCommunity.Util.headers import get_headers
12
+ from StreamingCommunity.Util.http_client import create_client_curl
14
13
  from StreamingCommunity.Util.table import TVShowManager
15
14
 
16
15
 
17
16
  # Logic class
18
17
  from StreamingCommunity.Api.Template.config_loader import site_constant
19
18
  from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
20
- from .util.get_license import get_auth_token, generate_device_id
19
+ from .util.get_license import CrunchyrollClient
21
20
 
22
21
 
23
22
  # Variable
24
23
  console = Console()
25
24
  media_search_manager = MediaManager()
26
25
  table_show_manager = TVShowManager()
27
- max_timeout = config_manager.get_int("REQUESTS", "timeout")
28
26
 
29
27
 
30
28
  def title_search(query: str) -> int:
@@ -40,9 +38,16 @@ def title_search(query: str) -> int:
40
38
  media_search_manager.clear()
41
39
  table_show_manager.clear()
42
40
 
43
- # Check if x_cr_tab_id or etp_rt is present
44
- if config_manager.get_dict("SITE_LOGIN", "crunchyroll")['x_cr_tab_id'] is None or config_manager.get_dict("SITE_LOGIN", "crunchyroll")['x_cr_tab_id'] == "" or config_manager.get_dict("SITE_LOGIN", "crunchyroll")['etp_rt'] is None or config_manager.get_dict("SITE_LOGIN", "crunchyroll")['etp_rt'] == "":
45
- console.print("[bold red] x_cr_tab_id or etp_rt is missing or empty.[/bold red]")
41
+ # Check if device_id or etp_rt is present
42
+ config = config_manager.get_dict("SITE_LOGIN", "crunchyroll")
43
+ if not config.get('device_id') or not config.get('etp_rt'):
44
+ console.print("[bold red] device_id or etp_rt is missing or empty in config.json.[/bold red]")
45
+ sys.exit(0)
46
+
47
+ # Initialize Crunchyroll client
48
+ client = CrunchyrollClient()
49
+ if not client.start():
50
+ console.print("[bold red] Failed to authenticate with Crunchyroll.[/bold red]")
46
51
  sys.exit(0)
47
52
 
48
53
  # Build new Crunchyroll API search URL
@@ -57,19 +62,12 @@ def title_search(query: str) -> int:
57
62
  "locale": "it-IT"
58
63
  }
59
64
 
60
- headers = get_headers()
61
- headers['authorization'] = f"Bearer {get_auth_token(generate_device_id()).access_token}"
65
+ headers = client._get_headers()
62
66
 
63
67
  console.print(f"[cyan]Search url: [yellow]{api_url}")
64
68
 
65
69
  try:
66
- response = requests.get(
67
- api_url,
68
- params=params,
69
- headers=headers,
70
- timeout=max_timeout,
71
- impersonate="chrome110"
72
- )
70
+ response = create_client_curl(headers=headers).get(api_url, params=params)
73
71
  response.raise_for_status()
74
72
 
75
73
  except Exception as e:
@@ -3,19 +3,10 @@
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.headers import get_headers
12
- from StreamingCommunity.Util.config_json import config_manager
13
7
  from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager
14
- from .get_license import get_auth_token, generate_device_id
15
-
16
-
17
- # Variable
18
- max_timeout = config_manager.get_int("REQUESTS", "timeout")
8
+ from StreamingCommunity.Util.http_client import create_client_curl
9
+ from .get_license import CrunchyrollClient
19
10
 
20
11
 
21
12
  def get_series_seasons(series_id, headers, params):
@@ -23,12 +14,7 @@ def get_series_seasons(series_id, headers, params):
23
14
  Fetches the seasons for a given series ID from Crunchyroll.
24
15
  """
25
16
  url = f'https://www.crunchyroll.com/content/v2/cms/series/{series_id}/seasons'
26
- response = requests.get(
27
- url,
28
- params=params,
29
- headers=headers,
30
- impersonate="chrome110"
31
- )
17
+ response = create_client_curl(headers=headers).get(url, params=params)
32
18
  return response
33
19
 
34
20
 
@@ -37,34 +23,9 @@ def get_season_episodes(season_id, headers, params):
37
23
  Fetches the episodes for a given season ID from Crunchyroll.
38
24
  """
39
25
  url = f'https://www.crunchyroll.com/content/v2/cms/seasons/{season_id}/episodes'
40
- response = requests.get(
41
- url,
42
- params=params,
43
- headers=headers,
44
- impersonate="chrome110"
45
- )
26
+ response = create_client_curl(headers=headers).get(url, params=params)
46
27
  return response
47
28
 
48
- def delete_stream_episode(episode_id, stream_id, headers):
49
- """
50
- Deletes a specific stream episode by episode ID and stream ID.
51
- """
52
- url = f'https://www.crunchyroll.com/playback/v1/token/{episode_id}/{stream_id}'
53
- headers = get_headers()
54
-
55
- response = requests.delete(
56
- url,
57
- headers=headers,
58
- impersonate="chrome110"
59
- )
60
-
61
- if response.status_code == 204:
62
- return True
63
-
64
- else:
65
- logging.error(f"Failed to delete stream episode: {response.status_code} - {response.text}")
66
- return False
67
-
68
29
 
69
30
  class GetSerieInfo:
70
31
  def __init__(self, series_id):
@@ -76,8 +37,13 @@ class GetSerieInfo:
76
37
  """
77
38
  self.series_id = series_id
78
39
  self.seasons_manager = SeasonManager()
79
- self.headers = get_headers()
80
- self.headers['authorization'] = f"Bearer {get_auth_token(generate_device_id()).access_token}"
40
+
41
+ # Initialize Crunchyroll client
42
+ self.client = CrunchyrollClient()
43
+ if not self.client.start():
44
+ raise Exception("Failed to authenticate with Crunchyroll")
45
+
46
+ self.headers = self.client._get_headers()
81
47
  self.params = {
82
48
  'force_locale': '',
83
49
  'preferred_audio_language': 'it-IT',
@@ -104,13 +70,10 @@ class GetSerieInfo:
104
70
  if seasons:
105
71
  self.series_name = seasons[0].get("series_title") or seasons[0].get("title")
106
72
 
107
- for season in seasons:
108
- season_num = season.get("season_number", 0)
109
- season_name = season.get("title", f"Season {season_num}")
110
-
73
+ for i, season in enumerate(seasons, start=1):
111
74
  self.seasons_manager.add_season({
112
- 'number': season_num,
113
- 'name': season_name,
75
+ 'number': i,
76
+ 'name': season.get("title", f"Season {i}"),
114
77
  'id': season.get('id')
115
78
  })
116
79
 
@@ -119,15 +82,10 @@ class GetSerieInfo:
119
82
  Fetch and cache episodes for a specific season number.
120
83
  """
121
84
  season = self.seasons_manager.get_season_by_number(season_number)
85
+ ep_response = get_season_episodes(season.id, self.headers, self.params)
122
86
 
123
- if not season or getattr(season, 'id', None) is None:
124
- logging.error(f"Season {season_number} not found or missing id.")
125
- return []
126
-
127
- season_id = season.id
128
- ep_response = get_season_episodes(season_id, self.headers, self.params)
129
87
  if ep_response.status_code != 200:
130
- logging.error(f"Failed to fetch episodes for season {season_id}")
88
+ logging.error(f"Failed to fetch episodes for season {season.id}")
131
89
  return []
132
90
 
133
91
  ep_data = ep_response.json()
@@ -161,12 +119,8 @@ class GetSerieInfo:
161
119
  'ratings': 'true',
162
120
  'locale': 'it-IT',
163
121
  }
164
- response = requests.get(
165
- url,
166
- params=params,
167
- headers=headers,
168
- impersonate="chrome110"
169
- )
122
+
123
+ response = create_client_curl(headers=headers).get(url, params=params)
170
124
 
171
125
  if response.status_code != 200:
172
126
  logging.warning(f"Failed to fetch audio locales for episode {episode_id}")
@@ -186,7 +140,6 @@ class GetSerieInfo:
186
140
  if locale and guid:
187
141
  audio_locales.append(locale)
188
142
  urls_by_locale[locale] = f"https://www.crunchyroll.com/it/watch/{guid}"
189
- #print(f"Locale: {locale}, URL: {urls_by_locale[locale]}")
190
143
 
191
144
  return audio_locales, urls_by_locale
192
145
 
@@ -1,133 +1,124 @@
1
1
  # 28.07.25
2
2
 
3
- import uuid
4
- from dataclasses import dataclass, field
5
- from typing import Optional, Dict, Any
6
-
7
-
8
- # External library
9
- from curl_cffi.requests import Session
3
+ from typing import Tuple, List, Dict
10
4
 
11
5
 
12
6
  # Internal utilities
13
7
  from StreamingCommunity.Util.config_json import config_manager
8
+ from StreamingCommunity.Util.http_client import create_client_curl
14
9
  from StreamingCommunity.Util.headers import get_userAgent
15
10
 
16
11
 
17
12
  # Variable
18
- device_id = None
19
- auth_basic = 'bm9haWhkZXZtXzZpeWcwYThsMHE6'
20
- etp_rt = config_manager.get_dict("SITE_LOGIN", "crunchyroll")['etp_rt']
21
- x_cr_tab_id = config_manager.get_dict("SITE_LOGIN", "crunchyroll")['x_cr_tab_id']
22
-
23
-
24
- @dataclass
25
- class Token:
26
- access_token: Optional[str] = None
27
- refresh_token: Optional[str] = None
28
- expires_in: Optional[int] = None
29
- token_type: Optional[str] = None
30
- scope: Optional[str] = None
31
- country: Optional[str] = None
32
- account_id: Optional[str] = None
33
- profile_id: Optional[str] = None
34
- extra: Dict[str, Any] = field(default_factory=dict)
35
-
13
+ PUBLIC_TOKEN = "bm9haWhkZXZtXzZpeWcwYThsMHE6"
36
14
 
37
15
 
38
- def generate_device_id():
39
- global device_id
16
+ class CrunchyrollClient:
17
+ def __init__(self) -> None:
18
+ config = config_manager.get_dict("SITE_LOGIN", "crunchyroll")
19
+ self.device_id = config.get('device_id')
20
+ self.etp_rt = config.get('etp_rt')
21
+ self.locale = "it-IT"
22
+
23
+ self.access_token = None
24
+ self.refresh_token = None
25
+ self.account_id = None
40
26
 
41
- if device_id is not None:
42
- return device_id
27
+ def _get_headers(self) -> Dict:
28
+ headers = {
29
+ 'user-agent': get_userAgent(),
30
+ }
31
+ if self.access_token:
32
+ headers['authorization'] = f'Bearer {self.access_token}'
33
+ return headers
43
34
 
44
- device_id = str(uuid.uuid4())
45
- return device_id
46
-
47
-
48
- def get_auth_token(device_id):
49
- with Session(impersonate="chrome110") as session:
50
- cookies = {
51
- 'etp_rt': etp_rt,
35
+ def _get_cookies(self) -> Dict:
36
+ cookies = {'device_id': self.device_id}
37
+ if self.etp_rt:
38
+ cookies['etp_rt'] = self.etp_rt
39
+ return cookies
40
+
41
+ def start(self) -> bool:
42
+ """Autorizza il client"""
43
+ headers = self._get_headers()
44
+ headers['authorization'] = f'Basic {PUBLIC_TOKEN}'
45
+ headers['content-type'] = 'application/x-www-form-urlencoded'
46
+
47
+ data = {
48
+ 'device_id': self.device_id,
49
+ 'device_type': 'Chrome on Windows',
50
+ 'grant_type': 'etp_rt_cookie',
52
51
  }
53
- response = session.post(
54
- 'https://www.crunchyroll.com/auth/v1/token',
55
- headers={
56
- 'authorization': f'Basic {auth_basic}',
57
- 'user-agent': get_userAgent(),
58
- },
59
- data={
60
- 'device_id': device_id,
61
- 'device_type': 'Chrome on Windows',
62
- 'grant_type': 'etp_rt_cookie',
63
- },
64
- cookies=cookies
65
- )
52
+
53
+ response = create_client_curl(headers=headers).post('https://www.crunchyroll.com/auth/v1/token', cookies=self._get_cookies(), data=data)
54
+
66
55
  if response.status_code == 400:
67
56
  print("Error 400: Please enter a correct 'etp_rt' value in config.json. You can find the value in the request headers.")
57
+ return False
58
+
59
+ result = response.json()
60
+ self.access_token = result.get('access_token')
61
+ self.refresh_token = result.get('refresh_token')
62
+ self.account_id = result.get('account_id')
63
+
64
+ return True
68
65
 
69
- # Get the JSON response
70
- data = response.json()
71
- known = {
72
- 'access_token', 'refresh_token', 'expires_in', 'token_type', 'scope',
73
- 'country', 'account_id', 'profile_id'
74
- }
75
- extra = {k: v for k, v in data.items() if k not in known}
76
- return Token(
77
- access_token=data.get('access_token'),
78
- refresh_token=data.get('refresh_token'),
79
- expires_in=data.get('expires_in'),
80
- token_type=data.get('token_type'),
81
- scope=data.get('scope'),
82
- country=data.get('country'),
83
- account_id=data.get('account_id'),
84
- profile_id=data.get('profile_id'),
85
- extra=extra
86
- )
87
-
88
-
89
- def get_playback_session(token: Token, device_id: str, url_id: str):
90
- """
91
- Crea una sessione per ottenere i dati di playback e sottotitoli da Crunchyroll.
92
- """
93
- cookies = {
94
- 'device_id': device_id,
95
- 'etp_rt': etp_rt
96
- }
97
- headers = {
98
- 'authorization': f'Bearer {token.access_token}',
99
- 'user-agent': get_userAgent(),
100
- 'x-cr-tab-id': x_cr_tab_id
101
- }
102
-
103
- with Session(impersonate="chrome110") as session:
104
- response = session.get(
105
- f'https://www.crunchyroll.com/playback/v3/{url_id}/web/chrome/play',
106
- cookies=cookies,
107
- headers=headers
108
- )
109
-
110
- if (response.status_code == 403):
66
+ def get_streams(self, media_id: str) -> Dict:
67
+ """Ottieni gli stream disponibili"""
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
+
70
+ if response.status_code == 403:
111
71
  raise Exception("Playback is Rejected: The current subscription does not have access to this content")
112
72
 
113
- if (response.status_code == 420):
73
+ if response.status_code == 420:
114
74
  raise Exception("TOO_MANY_ACTIVE_STREAMS. Wait a few minutes and try again.")
115
-
75
+
116
76
  response.raise_for_status()
117
-
118
- # Get the JSON response
77
+
119
78
  data = response.json()
120
79
 
121
80
  if data.get('error') == 'Playback is Rejected':
122
81
  raise Exception("Playback is Rejected: Premium required")
123
82
 
124
- url = data.get('url')
125
-
126
- subtitles = []
127
- if 'subtitles' in data:
128
- subtitles = [
129
- {'language': lang, 'url': info['url'], 'format': info.get('format')}
130
- for lang, info in data['subtitles'].items()
131
- ]
132
-
133
- return url, headers, subtitles
83
+ return data
84
+
85
+ def delete_active_stream(self, media_id: str, token: str) -> bool:
86
+ """Elimina uno stream attivo"""
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())
88
+ response.raise_for_status()
89
+ return response.status_code in [200, 204]
90
+
91
+
92
+ def get_playback_session(client: CrunchyrollClient, url_id: str) -> Tuple[str, Dict, List[Dict]]:
93
+ """
94
+ Return the playback session details including the MPD URL, headers, and subtitles.
95
+
96
+ Parameters:
97
+ - client: Instance of CrunchyrollClient
98
+ - url_id: ID of the media to fetch
99
+ """
100
+ data = client.get_streams(url_id)
101
+ url = data.get('url')
102
+
103
+ # Collect subtitles if available
104
+ subtitles = []
105
+ if 'subtitles' in data:
106
+ collected = []
107
+ for lang, info in data['subtitles'].items():
108
+ sub_url = info.get('url')
109
+
110
+ if not sub_url:
111
+ continue
112
+
113
+ collected.append({
114
+ 'language': lang,
115
+ 'url': sub_url,
116
+ 'format': info.get('format')
117
+ })
118
+
119
+ if collected:
120
+ subtitles = collected
121
+
122
+ # Return the MPD URL, headers, and subtitles
123
+ headers = client._get_headers()
124
+ return url, headers, subtitles
@@ -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 = httpx.get(
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.config_json import config_manager
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
- # Make an HTTP request to the series URL
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.os import os_manager, get_wvd_path
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 get_bearer_token, get_playback_url, get_tracking_info, generate_license_url
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) + ".mp4"
47
- mp4_path = os.path.join(site_constant.MOVIE_FOLDER, title_name.replace(".mp4", ""))
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
- # Generate mpd and license URLs
50
- bearer = get_bearer_token()
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
- playback_json = get_playback_url(bearer, select_title.id)
53
- tracking_info = get_tracking_info(bearer, playback_json)[0]
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, get_wvd_path
15
+ from StreamingCommunity.Util.os import os_manager
16
16
 
17
17
 
18
18
  # Logic class
@@ -70,7 +70,6 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
70
70
 
71
71
  # Download the episode
72
72
  dash_process = DASH_Downloader(
73
- cdm_device=get_wvd_path(),
74
73
  license_url=license_url,
75
74
  mpd_url=mpd_url,
76
75
  mpd_sub_list=tracking_info['subtitles'],
@@ -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.config_json import config_manager
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 = httpx.get(
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}")