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.

Files changed (40) hide show
  1. StreamingCommunity/Api/Player/Helper/Vixcloud/util.py +40 -38
  2. StreamingCommunity/Api/Player/maxstream.py +6 -11
  3. StreamingCommunity/Api/Player/supervideo.py +4 -0
  4. StreamingCommunity/Api/Site/1337xx/site.py +1 -9
  5. StreamingCommunity/Api/Site/altadefinizione/__init__.py +61 -0
  6. StreamingCommunity/Api/Site/altadefinizione/film.py +98 -0
  7. StreamingCommunity/Api/Site/altadefinizione/series.py +164 -0
  8. StreamingCommunity/Api/Site/altadefinizione/site.py +75 -0
  9. StreamingCommunity/Api/Site/altadefinizione/util/ScrapeSerie.py +72 -0
  10. StreamingCommunity/Api/Site/animeunity/film_serie.py +2 -2
  11. StreamingCommunity/Api/Site/animeunity/site.py +15 -41
  12. StreamingCommunity/Api/Site/cb01new/site.py +5 -16
  13. StreamingCommunity/Api/Site/ddlstreamitaly/site.py +1 -9
  14. StreamingCommunity/Api/Site/guardaserie/series.py +1 -1
  15. StreamingCommunity/Api/Site/guardaserie/site.py +1 -9
  16. StreamingCommunity/Api/Site/streamingcommunity/series.py +30 -12
  17. StreamingCommunity/Api/Site/streamingcommunity/site.py +10 -10
  18. StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +38 -17
  19. StreamingCommunity/Api/Template/Class/SearchType.py +1 -1
  20. StreamingCommunity/Api/Template/Util/__init__.py +0 -1
  21. StreamingCommunity/Api/Template/Util/manage_ep.py +43 -16
  22. StreamingCommunity/Api/Template/config_loader.py +0 -4
  23. StreamingCommunity/Api/Template/site.py +1 -1
  24. StreamingCommunity/Lib/Downloader/HLS/downloader.py +13 -2
  25. StreamingCommunity/Lib/Downloader/HLS/segments.py +37 -11
  26. StreamingCommunity/Lib/Downloader/MP4/downloader.py +5 -3
  27. StreamingCommunity/Lib/FFmpeg/command.py +2 -2
  28. StreamingCommunity/Lib/FFmpeg/util.py +11 -15
  29. StreamingCommunity/Lib/M3U8/estimator.py +4 -4
  30. StreamingCommunity/Lib/TMBD/tmdb.py +1 -1
  31. StreamingCommunity/Upload/version.py +1 -1
  32. StreamingCommunity/Util/config_json.py +0 -3
  33. StreamingCommunity/__init__.py +6 -0
  34. {StreamingCommunity-2.9.2.dist-info → streamingcommunity-2.9.4.dist-info}/METADATA +91 -7
  35. {StreamingCommunity-2.9.2.dist-info → streamingcommunity-2.9.4.dist-info}/RECORD +39 -35
  36. {StreamingCommunity-2.9.2.dist-info → streamingcommunity-2.9.4.dist-info}/WHEEL +1 -1
  37. StreamingCommunity/Api/Template/Util/get_domain.py +0 -100
  38. {StreamingCommunity-2.9.2.dist-info → streamingcommunity-2.9.4.dist-info}/LICENSE +0 -0
  39. {StreamingCommunity-2.9.2.dist-info → streamingcommunity-2.9.4.dist-info}/entry_points.txt +0 -0
  40. {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(site_name: str, domain: str) -> dict:
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
- # Check if domain is working
114
- domain_to_use, base_url = search_domain(site_constant.FULL_URL)
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
- 'accept': 'application/json, text/plain, */*',
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
- 'score': dict_title.get('score'),
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
- # 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
- 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_ = "card-content"):
57
+ for div in soup.find_all("div", class_ = "short-main"):
66
58
  try:
67
-
68
- url = div.find("h3").find("a").get("href")
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 = scrape_serie.episode_manager.get(index_episode_selected - 1)
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
- episodes_count = scrape_serie.episode_manager.length()
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(scrape_serie.episode_manager.episodes)
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.season_manager.seasons_count
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(scrape_serie.season_manager.seasonsData.get_season_by_number(i_season-1).number, scrape_serie, video_source, download_all=True)
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(scrape_serie.season_manager.seasonsData.get_season_by_number(i_season-1).number, scrape_serie, video_source, download_all=False)
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
- for i, dict_title in enumerate(response.json()['data']):
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 Season, EpisodeManager
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 ScrapeSerie class for scraping TV series information.
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 managers
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 season information for a TV series from the streaming site.
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 season information
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 seasons from JSON response
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
- # Collect info about season
72
- self.season_manager = Season(json_response.get("props").get("title"))
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 season info: {e}")
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
- self.episode_manager.add(dict_episode)
126
+ season.episodes.add(dict_episode)
106
127
 
107
128
  except Exception as e:
108
- logging.error(f"Error collecting title season info: {e}")
109
- raise
129
+ logging.error(f"Error collecting episodes for season {number_season}: {e}")
130
+ raise
@@ -98,4 +98,4 @@ class MediaManager:
98
98
  self.media_list.clear()
99
99
 
100
100
  def __str__(self):
101
- return f"MediaManager(num_media={len(self.media_list)})"
101
+ return f"MediaManager(num_media={len(self.media_list)})"
@@ -1,6 +1,5 @@
1
1
  # 23.11.24
2
2
 
3
- from .get_domain import search_domain
4
3
  from .manage_ep import (
5
4
  manage_selection,
6
5
  map_episode_title,
@@ -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(n: int) -> str:
25
+ def dynamic_format_number(number_str: str) -> str:
26
26
  """
27
- Formats a number by adding a leading zero if it is less than 9.
28
- The width of the resulting string is dynamic, calculated as the number of digits in the number plus one
29
- for numbers less than 9, otherwise the width remains the same.
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
- - n (int): The number to format.
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 as a string with a leading zero if the number is less than 9.
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
- if n < 10:
38
- width = len(str(n)) + 1
39
- else:
40
- width = len(str(n))
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
- return str(n).zfill(width)
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 [white]...")
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 [white]...")
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,