StreamingCommunity 3.2.8__py3-none-any.whl → 3.3.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 (86) hide show
  1. StreamingCommunity/Api/Player/Helper/Vixcloud/util.py +2 -1
  2. StreamingCommunity/Api/Player/hdplayer.py +2 -2
  3. StreamingCommunity/Api/Player/sweetpixel.py +5 -8
  4. StreamingCommunity/Api/Site/altadefinizione/__init__.py +32 -15
  5. StreamingCommunity/Api/Site/altadefinizione/film.py +10 -8
  6. StreamingCommunity/Api/Site/altadefinizione/series.py +9 -7
  7. StreamingCommunity/Api/Site/altadefinizione/site.py +1 -1
  8. StreamingCommunity/Api/Site/animeunity/__init__.py +31 -15
  9. StreamingCommunity/Api/Site/animeunity/serie.py +2 -2
  10. StreamingCommunity/Api/Site/animeworld/__init__.py +33 -7
  11. StreamingCommunity/Api/Site/animeworld/site.py +3 -5
  12. StreamingCommunity/Api/Site/animeworld/util/ScrapeSerie.py +8 -10
  13. StreamingCommunity/Api/Site/crunchyroll/__init__.py +44 -12
  14. StreamingCommunity/Api/Site/crunchyroll/film.py +9 -7
  15. StreamingCommunity/Api/Site/crunchyroll/series.py +9 -7
  16. StreamingCommunity/Api/Site/crunchyroll/site.py +16 -1
  17. StreamingCommunity/Api/Site/guardaserie/__init__.py +36 -10
  18. StreamingCommunity/Api/Site/guardaserie/series.py +8 -6
  19. StreamingCommunity/Api/Site/guardaserie/site.py +0 -3
  20. StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +1 -2
  21. StreamingCommunity/Api/Site/mediasetinfinity/__init__.py +37 -12
  22. StreamingCommunity/Api/Site/mediasetinfinity/film.py +10 -16
  23. StreamingCommunity/Api/Site/mediasetinfinity/series.py +12 -18
  24. StreamingCommunity/Api/Site/mediasetinfinity/site.py +18 -3
  25. StreamingCommunity/Api/Site/mediasetinfinity/util/ScrapeSerie.py +214 -180
  26. StreamingCommunity/Api/Site/mediasetinfinity/util/get_license.py +2 -31
  27. StreamingCommunity/Api/Site/raiplay/__init__.py +47 -12
  28. StreamingCommunity/Api/Site/raiplay/film.py +42 -10
  29. StreamingCommunity/Api/Site/raiplay/series.py +53 -11
  30. StreamingCommunity/Api/Site/raiplay/site.py +4 -1
  31. StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py +2 -1
  32. StreamingCommunity/Api/Site/raiplay/util/get_license.py +40 -0
  33. StreamingCommunity/Api/Site/streamingcommunity/__init__.py +5 -8
  34. StreamingCommunity/Api/Site/streamingcommunity/film.py +7 -5
  35. StreamingCommunity/Api/Site/streamingcommunity/series.py +9 -7
  36. StreamingCommunity/Api/Site/streamingcommunity/site.py +8 -3
  37. StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +5 -2
  38. StreamingCommunity/Api/Site/streamingwatch/__init__.py +43 -9
  39. StreamingCommunity/Api/Site/streamingwatch/film.py +7 -5
  40. StreamingCommunity/Api/Site/streamingwatch/series.py +8 -6
  41. StreamingCommunity/Api/Site/streamingwatch/site.py +3 -1
  42. StreamingCommunity/Api/Site/streamingwatch/util/ScrapeSerie.py +3 -3
  43. StreamingCommunity/Api/Template/Util/__init__.py +10 -1
  44. StreamingCommunity/Api/Template/Util/manage_ep.py +4 -4
  45. StreamingCommunity/Api/Template/__init__.py +5 -1
  46. StreamingCommunity/Api/Template/site.py +10 -6
  47. StreamingCommunity/Lib/Downloader/DASH/cdm_helpher.py +13 -12
  48. StreamingCommunity/Lib/Downloader/DASH/decrypt.py +1 -1
  49. StreamingCommunity/Lib/Downloader/DASH/downloader.py +24 -22
  50. StreamingCommunity/Lib/Downloader/DASH/parser.py +1 -1
  51. StreamingCommunity/Lib/Downloader/DASH/segments.py +4 -3
  52. StreamingCommunity/Lib/Downloader/HLS/downloader.py +11 -9
  53. StreamingCommunity/Lib/Downloader/HLS/segments.py +4 -9
  54. StreamingCommunity/Lib/Downloader/MP4/downloader.py +25 -6
  55. StreamingCommunity/Lib/Downloader/TOR/downloader.py +3 -5
  56. StreamingCommunity/Lib/Downloader/__init__.py +9 -1
  57. StreamingCommunity/Lib/FFmpeg/__init__.py +10 -1
  58. StreamingCommunity/Lib/FFmpeg/command.py +4 -6
  59. StreamingCommunity/Lib/FFmpeg/util.py +1 -1
  60. StreamingCommunity/Lib/M3U8/__init__.py +9 -1
  61. StreamingCommunity/Lib/M3U8/decryptor.py +8 -4
  62. StreamingCommunity/Lib/M3U8/estimator.py +0 -6
  63. StreamingCommunity/Lib/M3U8/parser.py +1 -1
  64. StreamingCommunity/Lib/M3U8/url_fixer.py +1 -1
  65. StreamingCommunity/Lib/TMBD/__init__.py +6 -1
  66. StreamingCommunity/TelegramHelp/config.json +1 -5
  67. StreamingCommunity/TelegramHelp/telegram_bot.py +9 -10
  68. StreamingCommunity/Upload/update.py +2 -2
  69. StreamingCommunity/Upload/version.py +1 -1
  70. StreamingCommunity/Util/config_json.py +139 -59
  71. StreamingCommunity/Util/http_client.py +201 -0
  72. StreamingCommunity/Util/message.py +1 -1
  73. StreamingCommunity/Util/os.py +8 -5
  74. StreamingCommunity/Util/table.py +3 -3
  75. StreamingCommunity/__init__.py +9 -1
  76. StreamingCommunity/run.py +394 -258
  77. {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/METADATA +147 -45
  78. streamingcommunity-3.3.0.dist-info/RECORD +110 -0
  79. StreamingCommunity/Api/Site/cb01new/__init__.py +0 -72
  80. StreamingCommunity/Api/Site/cb01new/film.py +0 -62
  81. StreamingCommunity/Api/Site/cb01new/site.py +0 -78
  82. streamingcommunity-3.2.8.dist-info/RECORD +0 -111
  83. {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/WHEEL +0 -0
  84. {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/entry_points.txt +0 -0
  85. {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/licenses/LICENSE +0 -0
  86. {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,12 @@
1
1
  # 16.03.25
2
2
 
3
- import re
3
+
4
4
  import logging
5
+ from urllib.parse import urlparse
5
6
 
6
7
 
7
8
  # External libraries
8
- import httpx
9
+ from curl_cffi import requests
9
10
  from bs4 import BeautifulSoup
10
11
 
11
12
 
@@ -16,7 +17,7 @@ from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager
16
17
 
17
18
 
18
19
  # Logic class
19
- from .get_license import get_bearer_token, get_playback_url
20
+ from .get_license import get_bearer_token
20
21
 
21
22
 
22
23
  # Variable
@@ -34,197 +35,230 @@ class GetSerieInfo:
34
35
  self.headers = get_headers()
35
36
  self.url = url
36
37
  self.seasons_manager = SeasonManager()
37
- self.subBrandId = None
38
- self.id_media = None
39
- self.current_url = None
40
-
41
- def _extract_subbrand_id(self, soup):
42
- """
43
- Extract subBrandId from the chapter link in the main page.
44
- Searches all <a> tags to see if one has 'capitoli_' in the href.
45
- """
46
- for a_tag in soup.find_all("a", href=True):
47
- href = a_tag["href"]
48
-
49
- if "capitoli_" in href:
50
- match = re.search(r"sb(\d+)", href)
51
- if match:
52
- return match.group(1)
53
- match = re.search(r",sb(\d+)", href)
54
- if match:
55
- return match.group(1)
56
-
57
- return None
58
-
59
- def _find_video_href_and_id(self, soup):
60
- """
61
- Search for the first <a> with href containing '/video/' and return (current_url, id_media).
62
- Always builds the absolute URL.
63
- """
64
- for a_tag in soup.find_all("a", href=True):
65
- href = a_tag["href"]
66
- if "/video/" in href:
67
- if href.startswith("http"):
68
- current_url = href
69
- else:
70
- current_url = "https://mediasetinfinity.mediaset.it" + href
71
-
72
- bearer = get_bearer_token()
73
- playback_json = get_playback_url(bearer, current_url.split('_')[-1])
74
- id_media = str(playback_json['url']).split("/s/")[1].split("/")[0]
75
-
76
- return current_url, id_media
77
- return None, None
78
-
79
- def _parse_entries(self, entries, single_season=False):
80
- """
81
- Populate seasons and episodes from the JSON entries.
82
- If single_season=True, creates only one season and adds all episodes there.
83
- """
84
- if not entries:
85
- self.series_name = ""
86
- return
87
-
88
- self.series_name = entries[0].get("mediasetprogram$auditelBrandName", "")
89
-
90
- if single_season:
91
- logging.info("Single season mode enabled.")
92
- season_num = 1
93
- season_name = "Stagione 1"
94
- current_season = self.seasons_manager.add_season({
95
- 'number': season_num,
96
- 'name': season_name
97
- })
38
+ self.serie_id = None
39
+ self.public_id = None
40
+ self.series_name = ""
41
+ self.stagioni_disponibili = []
42
+
43
+ def _extract_serie_id(self):
44
+ """Estrae l'ID della serie dall'URL di partenza"""
45
+ self.serie_id = f"SE{self.url.split('SE')[1]}"
46
+ print(f"Serie ID: {self.serie_id}")
47
+ return self.serie_id
48
+
49
+ def _get_public_id(self):
50
+ """Ottiene il public ID tramite l'API watchlist"""
51
+ bearer_token = get_bearer_token()
52
+ headers = {
53
+ 'authorization': f'Bearer {bearer_token}',
54
+ 'user-agent': get_userAgent(),
55
+ }
98
56
 
99
- for idx, entry in enumerate(entries, 1):
100
- title = entry.get("title", "")
101
- video_page_url = entry.get("mediasetprogram$videoPageUrl", "")
102
-
103
- if video_page_url.startswith("//"):
104
- episode_url = "https:" + video_page_url
105
- else:
106
- episode_url = video_page_url
107
-
108
- if current_season:
109
- current_season.episodes.add({
110
- 'number': idx,
111
- 'name': title,
112
- 'url': episode_url,
113
- 'duration': int(entry.get("mediasetprogram$duration", 0) / 60)
114
- })
57
+ response = requests.get(
58
+ 'https://api-ott-prod-fe.mediaset.net/PROD/play/userlist/watchlist/v2.0',
59
+ headers=headers,
60
+ impersonate="chrome",
61
+ allow_redirects=True
62
+ )
63
+
64
+ if response.status_code == 401:
65
+ print("Token scaduto, rinnovare il token")
66
+
67
+ if response.status_code == 200:
68
+ data = response.json()
69
+ self.public_id = data['response']['entries'][0]['media'][0]['publicUrl'].split("/")[4]
70
+ print(f"Public id: {self.public_id}")
71
+ return self.public_id
72
+
115
73
  else:
116
- seasons_dict = {}
117
-
118
- logging.info("Multi season mode")
119
- for entry in entries:
120
-
121
- # Use JSON fields directly instead of regex
122
- season_num = entry.get("tvSeasonNumber")
123
- ep_num = entry.get("tvSeasonEpisodeNumber")
124
-
125
- # Extract numbers from title if season_num or ep_num are None
126
- if season_num is None or ep_num is None:
127
- title = entry.get("title", "")
128
-
129
- # Find all numbers in the title
130
- numbers = [int(n) for n in re.findall(r"\d+", title)]
131
- if len(numbers) == 2:
132
- season_num, ep_num = numbers
74
+ logging.error(f"Failed to get public ID: {response.status_code}")
75
+ return None
133
76
 
134
- elif len(numbers) == 1:
135
- # If only one, use it as episode
136
- ep_num = numbers[0]
77
+ def _get_series_data(self):
78
+ """Ottiene i dati della serie tramite l'API"""
79
+ headers = {
80
+ 'User-Agent': get_userAgent(),
81
+ }
82
+ params = {'byGuid': self.serie_id}
83
+
84
+ response = requests.get(
85
+ f'https://feed.entertainment.tv.theplatform.eu/f/{self.public_id}/mediaset-prod-all-series-v2',
86
+ params=params,
87
+ headers=headers,
88
+ impersonate="chrome",
89
+ allow_redirects=True
90
+ )
91
+ print("Risposta per _get_series_data:", response.status_code)
92
+
93
+ if response.status_code == 200:
94
+ return response.json()
95
+ else:
96
+ logging.error(f"Failed to get series data: {response.status_code}")
97
+ return None
137
98
 
138
- if season_num is None or ep_num is None:
139
- continue
99
+ def _process_available_seasons(self, data):
100
+ """Processa le stagioni disponibili dai dati della serie"""
101
+ if not data or not data.get('entries'):
102
+ logging.error("No series data found")
103
+ return []
140
104
 
141
- season_name = entry.get("mediasetprogram$brandTitle") or f"Stagione {season_num}"
105
+ entry = data['entries'][0]
106
+ self.series_name = entry.get('title', '')
107
+
108
+ seriesTvSeasons = entry.get('seriesTvSeasons', [])
109
+ availableTvSeasonIds = entry.get('availableTvSeasonIds', [])
110
+
111
+ stagioni_disponibili = []
112
+
113
+ for url in availableTvSeasonIds:
114
+ season = next((s for s in seriesTvSeasons if s['id'] == url), None)
115
+ if season:
116
+ stagioni_disponibili.append({
117
+ 'tvSeasonNumber': season['tvSeasonNumber'],
118
+ 'url': url,
119
+ 'id': str(url).split("/")[-1],
120
+ 'guid': season['guid']
121
+ })
122
+ else:
123
+ logging.warning(f"Season URL not found: {url}")
142
124
 
143
- if season_num not in seasons_dict:
144
- current_season = self.seasons_manager.add_season({
145
- 'number': season_num,
146
- 'name': season_name
147
- })
148
- seasons_dict[season_num] = current_season
149
-
150
- else:
151
- current_season = seasons_dict[season_num]
152
-
153
- video_page_url = entry.get("mediasetprogram$videoPageUrl", "")
154
- if video_page_url.startswith("//"):
155
- episode_url = "https:" + video_page_url
156
- else:
157
- episode_url = video_page_url
158
-
159
- if current_season:
160
- current_season.episodes.add({
161
- 'number': ep_num,
162
- 'name': entry.get("title", ""),
163
- 'url': episode_url,
164
- 'duration': entry.get("mediasetprogram$duration")
165
- })
125
+ # Ordina le stagioni dalla più vecchia alla più nuova
126
+ stagioni_disponibili.sort(key=lambda s: s['tvSeasonNumber'])
127
+
128
+ return stagioni_disponibili
129
+
130
+ def _build_season_page_urls(self, stagioni_disponibili):
131
+ """Costruisce gli URL delle pagine delle stagioni"""
132
+ parsed_url = urlparse(self.url)
133
+ base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
134
+ series_slug = parsed_url.path.strip('/').split('/')[-1].split('_')[0]
135
+
136
+ for season in stagioni_disponibili:
137
+ page_url = f"{base_url}/fiction/{series_slug}/{series_slug}{season['tvSeasonNumber']}_{self.serie_id},{season['guid']}"
138
+ season['page_url'] = page_url
139
+
140
+ def _extract_season_sb_ids(self, stagioni_disponibili):
141
+ """Estrae gli ID sb dalle pagine delle stagioni"""
142
+ for season in stagioni_disponibili:
143
+ response_page = requests.get(
144
+ season['page_url'],
145
+ headers={'User-Agent': get_userAgent()},
146
+ impersonate="chrome",
147
+ allow_redirects=True
148
+ )
149
+ print("Risposta per _extract_season_sb_ids:", response_page.status_code)
150
+
151
+ soup = BeautifulSoup(response_page.text, 'html.parser')
152
+
153
+ # Prova prima con 'Episodi', poi con 'Puntate intere'
154
+ link = soup.find('a', string='Episodi')
155
+ if not link:
156
+ print("Using word: Puntate intere")
157
+ link = soup.find('a', string='Puntate intere')
158
+
159
+ if link and link.has_attr('href'):
160
+ if not link.string == 'Puntate intere':
161
+ print("Using word: Episodi")
162
+ season['sb'] = link['href'].split(',')[-1]
163
+ else:
164
+ logging.warning(f"Link 'Episodi' o 'Puntate intere' non trovato per stagione {season['tvSeasonNumber']}")
166
165
 
167
- def collect_season(self) -> None:
168
- """
169
- Retrieve all episodes for all seasons using the Mediaset Infinity API.
170
- """
171
- response = httpx.get(self.url, headers=self.headers, follow_redirects=True, timeout=max_timeout)
172
- soup = BeautifulSoup(response.text, "html.parser")
173
-
174
- # Find current_url and id_media from the first <a> with /video/
175
- self.current_url, found_id_media = self._find_video_href_and_id(soup)
176
- if found_id_media:
177
- self.id_media = found_id_media
178
-
179
- self.subBrandId = self._extract_subbrand_id(soup)
180
- single_season = False
181
- if self.subBrandId is None:
182
- episodi_link = None
183
- for h2_tag in soup.find_all("h2", class_=True):
184
- a_tag = h2_tag.find("a", href=True)
185
- if a_tag and "/episodi_" in a_tag["href"]:
186
- episodi_link = a_tag["href"]
187
- break
188
-
189
- if episodi_link:
190
- match = re.search(r"sb(\d+)", episodi_link)
191
- if match:
192
- self.subBrandId = match.group(1)
193
-
194
- single_season = True
166
+ def _get_season_episodes(self, season):
167
+ """Ottiene gli episodi per una stagione specifica"""
168
+ if not season.get('sb'):
169
+ return
195
170
 
196
- else:
197
- puntate_link = None
198
- for a_tag in soup.find_all("a", href=True):
199
- href = a_tag["href"]
200
- if "puntateintere" in href and "sb" in href:
201
- puntate_link = href
202
- break
203
-
204
- if puntate_link:
205
- match = re.search(r"sb(\d+)", puntate_link)
206
- if match:
207
- self.subBrandId = match.group(1)
208
-
209
- single_season = True
210
- else:
211
- print("No /episodi_ or puntateintere link found.")
212
-
213
- # Step 2: JSON request
171
+ episode_headers = {
172
+ 'origin': 'https://mediasetinfinity.mediaset.it',
173
+ 'referer': 'https://mediasetinfinity.mediaset.it/',
174
+ 'user-agent': get_userAgent(),
175
+ }
214
176
  params = {
215
- 'byCustomValue': "{subBrandId}{" + str(self.subBrandId) + "}",
177
+ 'byCustomValue': "{subBrandId}{" + str(season["sb"].replace('sb', '')) + "}",
216
178
  'sort': ':publishInfo_lastPublished|asc,tvSeasonEpisodeNumber|asc',
217
179
  'range': '0-100',
218
180
  }
181
+ episode_url = f"https://feed.entertainment.tv.theplatform.eu/f/{self.public_id}/mediaset-prod-all-programs-v2"
219
182
 
220
- json_url = f'https://feed.entertainment.tv.theplatform.eu/f/{self.id_media}/mediaset-prod-all-programs-v2'
221
- json_resp = httpx.get(json_url, headers={'user-agent': get_userAgent()}, params=params, timeout=max_timeout, follow_redirects=True)
222
-
223
- data = json_resp.json()
224
- entries = data.get("entries", [])
183
+ episode_response = requests.get(episode_url, headers=episode_headers, params=params, impersonate="chrome"
184
+ , allow_redirects=True)
185
+ print("Risposta per _get_season_episodes:", episode_response.status_code)
186
+
187
+ if episode_response.status_code == 200:
188
+ episode_data = episode_response.json()
189
+ season['episodes'] = []
190
+
191
+ for entry in episode_data.get('entries', []):
192
+ episode_info = {
193
+ 'id': entry.get('guid'),
194
+ 'title': entry.get('title'),
195
+ 'duration': int(entry.get('mediasetprogram$duration', 0) / 60) if entry.get('mediasetprogram$duration') else 0,
196
+ 'url': entry.get('media', [{}])[0].get('publicUrl') if entry.get('media') else None
197
+ }
198
+ season['episodes'].append(episode_info)
199
+
200
+ print(f"Found {len(season['episodes'])} episodes for season {season['tvSeasonNumber']}")
201
+ else:
202
+ logging.error(f"Failed to get episodes for season {season['tvSeasonNumber']}: {episode_response.status_code}")
225
203
 
226
- # Use the unified parsing function
227
- self._parse_entries(entries, single_season=single_season)
204
+ def collect_season(self) -> None:
205
+ """
206
+ Retrieve all episodes for all seasons using the new Mediaset Infinity API.
207
+ """
208
+ try:
209
+ # Step 1: Extract serie ID from URL
210
+ self._extract_serie_id()
211
+
212
+ # Step 2: Get public ID
213
+ if not self._get_public_id():
214
+ logging.error("Failed to get public ID")
215
+ return
216
+
217
+ # Step 3: Get series data
218
+ data = self._get_series_data()
219
+ if not data:
220
+ logging.error("Failed to get series data")
221
+ return
222
+
223
+ # Step 4: Process available seasons
224
+ self.stagioni_disponibili = self._process_available_seasons(data)
225
+ if not self.stagioni_disponibili:
226
+ logging.error("No seasons found")
227
+ return
228
+
229
+ # Step 5: Build season page URLs
230
+ self._build_season_page_urls(self.stagioni_disponibili)
231
+
232
+ # Step 6: Extract sb IDs from season pages
233
+ self._extract_season_sb_ids(self.stagioni_disponibili)
234
+
235
+ # Step 7: Get episodes for each season
236
+ for season in self.stagioni_disponibili:
237
+ self._get_season_episodes(season)
238
+
239
+ # Step 8: Populate seasons manager
240
+ self._populate_seasons_manager()
241
+
242
+ except Exception as e:
243
+ logging.error(f"Error in collect_season: {str(e)}")
244
+
245
+ def _populate_seasons_manager(self):
246
+ """Popola il seasons_manager con i dati raccolti"""
247
+ for season_data in self.stagioni_disponibili:
248
+ season_obj = self.seasons_manager.add_season({
249
+ 'number': season_data['tvSeasonNumber'],
250
+ 'name': f"Stagione {season_data['tvSeasonNumber']}"
251
+ })
252
+
253
+ if season_obj and season_data.get('episodes'):
254
+ for idx, episode in enumerate(season_data['episodes'], 1):
255
+ season_obj.episodes.add({
256
+ 'id': episode['id'],
257
+ 'number': idx,
258
+ 'name': episode['title'],
259
+ 'url': episode['url'],
260
+ 'duration': episode['duration']
261
+ })
228
262
 
229
263
  # ------------- FOR GUI -------------
230
264
  def getNumberSeason(self) -> int:
@@ -15,8 +15,6 @@ from StreamingCommunity.Util.headers import get_headers, get_userAgent
15
15
 
16
16
  # Variable
17
17
  MAX_TIMEOUT = config_manager.get_int("REQUESTS", "timeout")
18
- bearer_token = None
19
- playback_json = None
20
18
 
21
19
 
22
20
  def get_bearer_token():
@@ -26,29 +24,7 @@ def get_bearer_token():
26
24
  Returns:
27
25
  str: The bearer token string.
28
26
  """
29
- global bearer_token
30
-
31
- if bearer_token:
32
- return bearer_token
33
-
34
- LOGIN_URL = "https://api-ott-prod-fe.mediaset.net/PROD/play/idm/anonymous/login/v2.0"
35
-
36
- try:
37
- response = httpx.post(
38
- LOGIN_URL,
39
- json={'client_id': 'client_id', 'appName': 'embed//mediasetplay-embed'},
40
- follow_redirects=True,
41
- timeout=MAX_TIMEOUT
42
- )
43
- response.raise_for_status()
44
-
45
- # Extract the bearer token from the response
46
- data = response.json()
47
- bearer_token = data["response"]["beToken"]
48
- return bearer_token
49
-
50
- except Exception as e:
51
- raise RuntimeError(f"Failed to get bearer token: {e}")
27
+ return config_manager.get_dict("SITE_LOGIN", "mediasetinfinity")["beToken"]
52
28
 
53
29
  def get_playback_url(BEARER_TOKEN, CONTENT_ID):
54
30
  """
@@ -61,11 +37,6 @@ def get_playback_url(BEARER_TOKEN, CONTENT_ID):
61
37
  Returns:
62
38
  dict: The playback JSON object.
63
39
  """
64
- global playback_json
65
-
66
- if playback_json is not None:
67
- return playback_json
68
-
69
40
  headers = get_headers()
70
41
  headers['authorization'] = f'Bearer {BEARER_TOKEN}'
71
42
 
@@ -190,7 +161,7 @@ def get_tracking_info(BEARER_TOKEN, PLAYBACK_JSON):
190
161
  results = parse_smil_for_tracking_and_video(smil_xml)
191
162
  return results
192
163
 
193
- except Exception as e:
164
+ except Exception:
194
165
  return None
195
166
 
196
167
  def generate_license_url(BEARER_TOKEN, tracking_info):
@@ -10,6 +10,7 @@ from rich.prompt import Prompt
10
10
  from StreamingCommunity.Api.Template import get_select_title
11
11
  from StreamingCommunity.Api.Template.config_loader import site_constant
12
12
  from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
13
+ from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
13
14
 
14
15
 
15
16
  # Logic class
@@ -22,7 +23,7 @@ from .film import download_film
22
23
  indice = 5
23
24
  _useFor = "Film_&_Serie"
24
25
  _priority = 0
25
- _engineDownload = "hls"
26
+ _engineDownload = "hls_dash"
26
27
  _deprecate = False
27
28
 
28
29
  msg = Prompt()
@@ -44,6 +45,14 @@ def process_search_result(select_title, selections=None):
44
45
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
45
46
  {'season': season_selection, 'episode': episode_selection}
46
47
  """
48
+ if not select_title:
49
+ if site_constant.TELEGRAM_BOT:
50
+ bot = get_bot_instance()
51
+ bot.send_message("No title selected or selection cancelled.", None)
52
+ else:
53
+ console.print("[yellow]No title selected or selection cancelled.")
54
+ return
55
+
47
56
  if select_title.type == 'tv':
48
57
  season_selection = None
49
58
  episode_selection = None
@@ -68,26 +77,52 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
68
77
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
69
78
  {'season': season_selection, 'episode': episode_selection}
70
79
  """
80
+ """
81
+ Main function of the application for search.
82
+
83
+ Parameters:
84
+ string_to_search (str, optional): String to search for
85
+ get_onlyDatabase (bool, optional): If True, return only the database object
86
+ direct_item (dict, optional): Direct item to process (bypass search)
87
+ selections (dict, optional): Dictionary containing selection inputs that bypass manual input
88
+ {'season': season_selection, 'episode': episode_selection}
89
+ """
90
+ bot = None
91
+ if site_constant.TELEGRAM_BOT:
92
+ bot = get_bot_instance()
93
+
71
94
  if direct_item:
72
95
  select_title = MediaItem(**direct_item)
73
96
  process_search_result(select_title, selections)
74
97
  return
75
-
76
- if string_to_search is None:
77
- string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
78
-
79
- # Search on database
80
- len_database = title_search(string_to_search)
98
+
99
+ # Get the user input for the search term
100
+ actual_search_query = get_user_input(string_to_search)
101
+
102
+ # Handle cases where user input is empty, or 'back' was handled (sys.exit or None return)
103
+ if not actual_search_query:
104
+ if bot:
105
+ if actual_search_query is None: # Specifically for timeout from bot.ask or failed restart
106
+ bot.send_message("Search term not provided or operation cancelled. Returning.", None)
107
+ return
108
+
109
+ # Perform the database search
110
+ len_database = title_search(actual_search_query)
81
111
 
82
112
  # If only the database is needed, return the manager
83
113
  if get_onlyDatabase:
84
114
  return media_search_manager
85
-
115
+
86
116
  if len_database > 0:
87
- select_title = get_select_title(table_show_manager, media_search_manager,len_database)
117
+ select_title = get_select_title(table_show_manager, media_search_manager, len_database)
88
118
  process_search_result(select_title, selections)
89
119
 
90
120
  else:
91
- # If no results are found, ask again
92
- console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
93
- search()
121
+ if bot:
122
+ bot.send_message(f"No results found for: '{actual_search_query}'", None)
123
+ else:
124
+ console.print(f"\n[red]Nothing matching was found for[white]: [purple]{actual_search_query}")
125
+
126
+ # Do not call search() recursively here to avoid infinite loops on no results.
127
+ # The flow should return to the caller (e.g., main menu in run.py).
128
+ return
@@ -1,6 +1,7 @@
1
1
  # 21.05.24
2
2
 
3
3
  import os
4
+ import sys
4
5
  from typing import Tuple
5
6
 
6
7
 
@@ -12,16 +13,18 @@ from rich.console import Console
12
13
  # Internal utilities
13
14
  from StreamingCommunity.Util.os import os_manager
14
15
  from StreamingCommunity.Util.headers import get_headers
16
+ from StreamingCommunity.Util.os import get_wvd_path
15
17
  from StreamingCommunity.Util.message import start_message
16
18
 
17
19
 
18
20
  # Logic class
21
+ from .util.get_license import generate_license_url
19
22
  from StreamingCommunity.Api.Template.config_loader import site_constant
20
23
  from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
21
24
 
22
25
 
23
26
  # Player
24
- from StreamingCommunity import HLS_Downloader
27
+ from StreamingCommunity import HLS_Downloader, DASH_Downloader
25
28
  from StreamingCommunity.Api.Player.mediapolisvod import VideoSource
26
29
 
27
30
 
@@ -49,17 +52,46 @@ def download_film(select_title: MediaItem) -> Tuple[str, bool]:
49
52
  master_playlist = VideoSource.extract_m3u8_url(first_item_path)
50
53
 
51
54
  # Define the filename and path for the downloaded film
52
- title_name = os_manager.get_sanitize_file(select_title.name) + ".mp4"
53
- mp4_path = os.path.join(site_constant.MOVIE_FOLDER, title_name.replace(".mp4", ""))
55
+ mp4_name = os_manager.get_sanitize_file(select_title.name) + ".mp4"
56
+ mp4_path = os.path.join(site_constant.MOVIE_FOLDER, mp4_name.replace(".mp4", ""))
57
+
58
+ # HLS
59
+ if ".mpd" not in master_playlist:
60
+ r_proc = HLS_Downloader(
61
+ m3u8_url=master_playlist,
62
+ output_path=os.path.join(mp4_path, mp4_name)
63
+ ).start()
64
+
65
+ # MPD
66
+ else:
67
+
68
+ # Check CDM file before usage
69
+ cdm_device_path = get_wvd_path()
70
+ if not cdm_device_path or not isinstance(cdm_device_path, (str, bytes, os.PathLike)) or not os.path.isfile(cdm_device_path):
71
+ console.print(f"[bold red] CDM file not found or invalid path: {cdm_device_path}[/bold red]")
72
+ sys.exit(0)
73
+
74
+ license_url = generate_license_url(select_title.mpd_id)
75
+
76
+ dash_process = DASH_Downloader(
77
+ cdm_device=cdm_device_path,
78
+ license_url=license_url,
79
+ mpd_url=master_playlist,
80
+ output_path=os.path.join(mp4_path, mp4_name),
81
+ )
82
+ dash_process.parse_manifest(custom_headers=get_headers())
83
+
84
+ if dash_process.download_and_decrypt():
85
+ dash_process.finalize_output()
86
+
87
+ # Get final output path and status
88
+ r_proc = dash_process.get_status()
54
89
 
55
- # Download the film using the m3u8 playlist, and output filename
56
- r_proc = HLS_Downloader(
57
- m3u8_url=master_playlist,
58
- output_path=os.path.join(mp4_path, title_name)
59
- ).start()
60
90
 
61
91
  if r_proc['error'] is not None:
62
- try: os.remove(r_proc['path'])
63
- except: pass
92
+ try:
93
+ os.remove(r_proc['path'])
94
+ except Exception:
95
+ pass
64
96
 
65
97
  return r_proc['path'], r_proc['stopped']