StreamingCommunity 2.9.7__py3-none-any.whl → 2.9.9__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 (61) hide show
  1. StreamingCommunity/Api/Player/ddl.py +2 -10
  2. StreamingCommunity/Api/Player/mediapolisvod.py +64 -0
  3. StreamingCommunity/Api/Player/sweetpixel.py +3 -3
  4. StreamingCommunity/Api/Player/vixcloud.py +4 -9
  5. StreamingCommunity/Api/Site/1337xx/__init__.py +1 -1
  6. StreamingCommunity/Api/Site/altadefinizione/__init__.py +23 -7
  7. StreamingCommunity/Api/Site/altadefinizione/film.py +0 -1
  8. StreamingCommunity/Api/Site/altadefinizione/series.py +66 -70
  9. StreamingCommunity/Api/Site/altadefinizione/site.py +2 -1
  10. StreamingCommunity/Api/Site/altadefinizione/util/ScrapeSerie.py +37 -2
  11. StreamingCommunity/Api/Site/animeunity/__init__.py +29 -10
  12. StreamingCommunity/Api/Site/animeunity/film.py +40 -0
  13. StreamingCommunity/Api/Site/animeunity/serie.py +153 -0
  14. StreamingCommunity/Api/Site/animeunity/site.py +1 -2
  15. StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py +15 -0
  16. StreamingCommunity/Api/Site/animeworld/__init__.py +25 -12
  17. StreamingCommunity/Api/Site/animeworld/film.py +63 -0
  18. StreamingCommunity/Api/Site/animeworld/serie.py +25 -22
  19. StreamingCommunity/Api/Site/animeworld/site.py +2 -1
  20. StreamingCommunity/Api/Site/animeworld/util/ScrapeSerie.py +32 -5
  21. StreamingCommunity/Api/Site/cb01new/__init__.py +1 -1
  22. StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +1 -1
  23. StreamingCommunity/Api/Site/ddlstreamitaly/series.py +31 -32
  24. StreamingCommunity/Api/Site/ddlstreamitaly/site.py +2 -2
  25. StreamingCommunity/Api/Site/ddlstreamitaly/util/ScrapeSerie.py +30 -2
  26. StreamingCommunity/Api/Site/guardaserie/__init__.py +21 -7
  27. StreamingCommunity/Api/Site/guardaserie/series.py +55 -53
  28. StreamingCommunity/Api/Site/guardaserie/site.py +3 -2
  29. StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +27 -1
  30. StreamingCommunity/Api/Site/raiplay/__init__.py +93 -0
  31. StreamingCommunity/Api/Site/raiplay/film.py +65 -0
  32. StreamingCommunity/Api/Site/raiplay/series.py +162 -0
  33. StreamingCommunity/Api/Site/raiplay/site.py +166 -0
  34. StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py +127 -0
  35. StreamingCommunity/Api/Site/streamingcommunity/__init__.py +29 -22
  36. StreamingCommunity/Api/Site/streamingcommunity/film.py +1 -2
  37. StreamingCommunity/Api/Site/streamingcommunity/series.py +76 -90
  38. StreamingCommunity/Api/Site/streamingcommunity/site.py +1 -3
  39. StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +41 -15
  40. StreamingCommunity/Api/Template/site.py +2 -2
  41. StreamingCommunity/Lib/Downloader/HLS/downloader.py +1 -1
  42. StreamingCommunity/Lib/Downloader/HLS/segments.py +2 -3
  43. StreamingCommunity/Lib/Downloader/MP4/downloader.py +2 -1
  44. StreamingCommunity/Lib/FFmpeg/util.py +47 -17
  45. StreamingCommunity/Lib/M3U8/estimator.py +50 -21
  46. StreamingCommunity/Lib/M3U8/parser.py +26 -6
  47. StreamingCommunity/Upload/update.py +22 -3
  48. StreamingCommunity/Upload/version.py +1 -1
  49. StreamingCommunity/Util/config_json.py +425 -274
  50. StreamingCommunity/Util/table.py +4 -2
  51. StreamingCommunity/run.py +1 -1
  52. {streamingcommunity-2.9.7.dist-info → streamingcommunity-2.9.9.dist-info}/METADATA +1 -1
  53. streamingcommunity-2.9.9.dist-info/RECORD +91 -0
  54. {streamingcommunity-2.9.7.dist-info → streamingcommunity-2.9.9.dist-info}/WHEEL +1 -1
  55. StreamingCommunity/Api/Site/animeunity/film_serie.py +0 -181
  56. StreamingCommunity/Api/Site/mostraguarda/__init__.py +0 -73
  57. StreamingCommunity/Api/Site/mostraguarda/film.py +0 -93
  58. streamingcommunity-2.9.7.dist-info/RECORD +0 -85
  59. {streamingcommunity-2.9.7.dist-info → streamingcommunity-2.9.9.dist-info}/entry_points.txt +0 -0
  60. {streamingcommunity-2.9.7.dist-info → streamingcommunity-2.9.9.dist-info}/licenses/LICENSE +0 -0
  61. {streamingcommunity-2.9.7.dist-info → streamingcommunity-2.9.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,166 @@
1
+ # 10.12.23
2
+
3
+ import threading
4
+ import queue
5
+
6
+ # External libraries
7
+ import httpx
8
+ from rich.console import Console
9
+
10
+
11
+ # Internal utilities
12
+ from StreamingCommunity.Util.config_json import config_manager
13
+ from StreamingCommunity.Util.headers import get_userAgent
14
+ from StreamingCommunity.Util.table import TVShowManager
15
+ from StreamingCommunity.Lib.TMBD.tmdb import tmdb
16
+
17
+
18
+ # Logic class
19
+ from StreamingCommunity.Api.Template.config_loader import site_constant
20
+ from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
21
+
22
+
23
+ # Variable
24
+ console = Console()
25
+ media_search_manager = MediaManager()
26
+ table_show_manager = TVShowManager()
27
+ max_timeout = config_manager.get_int("REQUESTS", "timeout")
28
+ MAX_THREADS = 4
29
+
30
+
31
+ def determine_media_type(title):
32
+ """
33
+ Use TMDB to determine if a title is a movie or TV show.
34
+ """
35
+ try:
36
+ # First search as a movie
37
+ movie_results = tmdb._make_request("search/movie", {"query": title})
38
+ movie_count = len(movie_results.get("results", []))
39
+
40
+ # Then search as a TV show
41
+ tv_results = tmdb._make_request("search/tv", {"query": title})
42
+ tv_count = len(tv_results.get("results", []))
43
+
44
+ # If results found in only one category, use that
45
+ if movie_count > 0 and tv_count == 0:
46
+ return "film"
47
+ elif tv_count > 0 and movie_count == 0:
48
+ return "tv"
49
+
50
+ # If both have results, compare popularity
51
+ if movie_count > 0 and tv_count > 0:
52
+ top_movie = movie_results["results"][0]
53
+ top_tv = tv_results["results"][0]
54
+
55
+ return "film" if top_movie.get("popularity", 0) > top_tv.get("popularity", 0) else "tv"
56
+
57
+ return "film"
58
+
59
+ except Exception as e:
60
+ console.log(f"Error determining media type with TMDB: {e}")
61
+ return "film"
62
+
63
+
64
+ def worker_determine_type(work_queue, result_dict, worker_id):
65
+ """
66
+ Worker function to process items from queue and determine media types.
67
+
68
+ Parameters:
69
+ - work_queue: Queue containing items to process
70
+ - result_dict: Dictionary to store results
71
+ - worker_id: ID of the worker thread
72
+ """
73
+ while not work_queue.empty():
74
+ try:
75
+ index, item = work_queue.get(block=False)
76
+ title = item.get('titolo', '')
77
+ media_type = determine_media_type(title)
78
+
79
+ result_dict[index] = {
80
+ 'id': item.get('id', ''),
81
+ 'name': title,
82
+ 'type': media_type,
83
+ 'path_id': item.get('path_id', ''),
84
+ 'url': f"https://www.raiplay.it{item.get('url', '')}",
85
+ 'image': f"https://www.raiplay.it{item.get('immagine', '')}",
86
+ }
87
+
88
+ work_queue.task_done()
89
+
90
+ except queue.Empty:
91
+ break
92
+
93
+ except Exception as e:
94
+ console.log(f"Worker {worker_id} error: {e}")
95
+ work_queue.task_done()
96
+
97
+
98
+ def title_search(query: str) -> int:
99
+ """
100
+ Search for titles based on a search query.
101
+
102
+ Parameters:
103
+ - query (str): The query to search for.
104
+
105
+ Returns:
106
+ int: The number of titles found.
107
+ """
108
+ media_search_manager.clear()
109
+ table_show_manager.clear()
110
+
111
+ search_url = f"https://www.raiplay.it/atomatic/raiplay-search-service/api/v1/msearch"
112
+ console.print(f"[cyan]Search url: [yellow]{search_url}")
113
+
114
+ json_data = {
115
+ 'templateIn': '6470a982e4e0301afe1f81f1',
116
+ 'templateOut': '6516ac5d40da6c377b151642',
117
+ 'params': {
118
+ 'param': query,
119
+ 'from': None,
120
+ 'sort': 'relevance',
121
+ 'onlyVideoQuery': False,
122
+ },
123
+ }
124
+
125
+ try:
126
+ response = httpx.post(search_url, headers={'user-agent': get_userAgent()}, json=json_data, timeout=max_timeout, follow_redirects=True)
127
+ response.raise_for_status()
128
+
129
+ except Exception as e:
130
+ console.print(f"Site: {site_constant.SITE_NAME}, request search error: {e}")
131
+ return 0
132
+
133
+ # Limit to only 15 results for performance
134
+ data = response.json().get('agg').get('titoli').get('cards')
135
+ data = data[:15] if len(data) > 15 else data
136
+
137
+ # Use multithreading to determine media types in parallel
138
+ work_queue = queue.Queue()
139
+ result_dict = {}
140
+
141
+ # Add items to the work queue
142
+ for i, item in enumerate(data):
143
+ work_queue.put((i, item))
144
+
145
+ # Create and start worker threads
146
+ threads = []
147
+ for i in range(min(MAX_THREADS, len(data))):
148
+ thread = threading.Thread(
149
+ target=worker_determine_type,
150
+ args=(work_queue, result_dict, i),
151
+ daemon=True
152
+ )
153
+ threads.append(thread)
154
+ thread.start()
155
+
156
+ # Wait for all threads to complete
157
+ for thread in threads:
158
+ thread.join()
159
+
160
+ # Add all results to media manager in correct order
161
+ for i in range(len(data)):
162
+ if i in result_dict:
163
+ media_search_manager.add_media(result_dict[i])
164
+
165
+ # Return the number of titles found
166
+ return media_search_manager.get_length()
@@ -0,0 +1,127 @@
1
+ # 01.03.24
2
+
3
+ import logging
4
+
5
+
6
+ # External libraries
7
+ import httpx
8
+
9
+
10
+ # Internal utilities
11
+ from StreamingCommunity.Util.headers import get_headers
12
+ from StreamingCommunity.Util.config_json import config_manager
13
+ from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager
14
+
15
+
16
+ # Variable
17
+ max_timeout = config_manager.get_int("REQUESTS", "timeout")
18
+
19
+
20
+ class GetSerieInfo:
21
+ def __init__(self, program_name: str):
22
+ """Initialize the GetSerieInfo class."""
23
+ self.base_url = "https://www.raiplay.it"
24
+ self.program_name = program_name
25
+ self.series_name = program_name
26
+ self.seasons_manager = SeasonManager()
27
+
28
+ def collect_info_title(self) -> None:
29
+ """Get series info including seasons."""
30
+ try:
31
+ program_url = f"{self.base_url}/programmi/{self.program_name}.json"
32
+ response = httpx.get(url=program_url, headers=get_headers(), timeout=max_timeout)
33
+ response.raise_for_status()
34
+
35
+ json_data = response.json()
36
+
37
+ # Look for seasons in the 'blocks' property
38
+ for block in json_data.get('blocks'):
39
+ if block.get('type') == 'RaiPlay Multimedia Block' and block.get('name', '').lower() == 'episodi':
40
+ self.publishing_block_id = block.get('id')
41
+
42
+ # Extract seasons from sets array
43
+ for season_set in block.get('sets', []):
44
+ if 'stagione' in season_set.get('name', '').lower():
45
+ self.seasons_manager.add_season({
46
+ 'id': season_set.get('id', ''),
47
+ 'number': len(self.seasons_manager.seasons) + 1,
48
+ 'name': season_set.get('name', ''),
49
+ 'path': season_set.get('path_id', ''),
50
+ 'episodes_count': season_set.get('episode_size', {}).get('number', 0)
51
+ })
52
+
53
+ except Exception as e:
54
+ logging.error(f"Error collecting series info: {e}")
55
+
56
+ def collect_info_season(self, number_season: int) -> None:
57
+ """Get episodes for a specific season."""
58
+ try:
59
+ season = self.seasons_manager.get_season_by_number(number_season)
60
+
61
+ url = f"{self.base_url}/programmi/{self.program_name}/{self.publishing_block_id}/{season.id}/episodes.json"
62
+ response = httpx.get(url=url, headers=get_headers(), timeout=max_timeout)
63
+ response.raise_for_status()
64
+
65
+ episodes_data = response.json()
66
+ cards = []
67
+
68
+ # Extract episodes from different possible structures
69
+ if 'seasons' in episodes_data:
70
+ for season_data in episodes_data.get('seasons', []):
71
+ for episode_set in season_data.get('episodes', []):
72
+ cards.extend(episode_set.get('cards', []))
73
+
74
+ if not cards:
75
+ cards = episodes_data.get('cards', [])
76
+
77
+ # Add episodes to season
78
+ for ep in cards:
79
+ episode = {
80
+ 'id': ep.get('id', ''),
81
+ 'number': ep.get('episode', ''),
82
+ 'name': ep.get('episode_title', '') or ep.get('toptitle', ''),
83
+ 'duration': ep.get('duration', ''),
84
+ 'url': f"{self.base_url}{ep.get('weblink', '')}" if 'weblink' in ep else f"{self.base_url}{ep.get('url', '')}"
85
+ }
86
+ season.episodes.add(episode)
87
+
88
+ except Exception as e:
89
+ logging.error(f"Error collecting episodes for season {number_season}: {e}")
90
+ raise
91
+
92
+
93
+ # ------------- FOR GUI -------------
94
+ def getNumberSeason(self) -> int:
95
+ """
96
+ Get the total number of seasons available for the series.
97
+ """
98
+ if not self.seasons_manager.seasons:
99
+ self.collect_info_title()
100
+
101
+ return len(self.seasons_manager.seasons)
102
+
103
+ def getEpisodeSeasons(self, season_number: int) -> list:
104
+ """
105
+ Get all episodes for a specific season.
106
+ """
107
+ season = self.seasons_manager.get_season_by_number(season_number)
108
+
109
+ if not season:
110
+ logging.error(f"Season {season_number} not found")
111
+ return []
112
+
113
+ if not season.episodes.episodes:
114
+ self.collect_info_season(season_number)
115
+
116
+ return season.episodes.episodes
117
+
118
+ def selectEpisode(self, season_number: int, episode_index: int) -> dict:
119
+ """
120
+ Get information for a specific episode in a specific season.
121
+ """
122
+ episodes = self.getEpisodeSeasons(season_number)
123
+ if not episodes or episode_index < 0 or episode_index >= len(episodes):
124
+ logging.error(f"Episode index {episode_index} is out of range for season {season_number}")
125
+ return None
126
+
127
+ return episodes[episode_index]
@@ -58,52 +58,59 @@ def get_user_input(string_to_search: str = None):
58
58
 
59
59
  return string_to_search
60
60
 
61
- def process_search_result(select_title):
61
+ def process_search_result(select_title, selections=None):
62
62
  """
63
63
  Handles the search result and initiates the download for either a film or series.
64
+
65
+ Parameters:
66
+ select_title (MediaItem): The selected media item
67
+ selections (dict, optional): Dictionary containing selection inputs that bypass manual input
68
+ {'season': season_selection, 'episode': episode_selection}
64
69
  """
65
70
  if select_title.type == 'tv':
66
- download_series(select_title)
71
+ season_selection = None
72
+ episode_selection = None
73
+
74
+ if selections:
75
+ season_selection = selections.get('season')
76
+ episode_selection = selections.get('episode')
77
+
78
+ download_series(select_title, season_selection, episode_selection)
79
+
67
80
  else:
68
81
  download_film(select_title)
69
82
 
70
- def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
83
+ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
71
84
  """
72
- Main function of the application for search film, series and anime.
85
+ Main function of the application for search.
73
86
 
74
87
  Parameters:
75
88
  string_to_search (str, optional): String to search for
76
- get_onylDatabase (bool, optional): If True, return only the database object
89
+ get_onlyDatabase (bool, optional): If True, return only the database object
77
90
  direct_item (dict, optional): Direct item to process (bypass search)
91
+ selections (dict, optional): Dictionary containing selection inputs that bypass manual input
92
+ {'season': season_selection, 'episode': episode_selection}
78
93
  """
79
94
  if direct_item:
80
95
  select_title = MediaItem(**direct_item)
81
- process_search_result(select_title)
96
+ process_search_result(select_title, selections)
82
97
  return
83
-
84
- # Get the user input for the search term
85
- string_to_search = get_user_input(string_to_search)
86
-
87
- # Perform the database search
88
- len_database = title_search(quote_plus(string_to_search))
98
+
99
+ if string_to_search is None:
100
+ string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
101
+
102
+ # Search on database
103
+ len_database = title_search(string_to_search)
89
104
 
90
105
  # If only the database is needed, return the manager
91
106
  if get_onlyDatabase:
92
107
  return media_search_manager
93
108
 
94
- if site_constant.TELEGRAM_BOT:
95
- bot = get_bot_instance()
96
-
97
109
  if len_database > 0:
98
110
  select_title = get_select_title(table_show_manager, media_search_manager)
99
- process_search_result(select_title)
111
+ process_search_result(select_title, selections)
100
112
 
101
113
  else:
102
- console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
103
-
104
- if site_constant.TELEGRAM_BOT:
105
- bot.send_message(f"No results found, please try again", None)
106
-
107
114
  # If no results are found, ask again
108
- string_to_search = get_user_input()
115
+ console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
109
116
  search()
@@ -55,8 +55,7 @@ def download_film(select_title: MediaItem) -> str:
55
55
  console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{select_title.name}[/cyan] \n")
56
56
 
57
57
  # Init class
58
- video_source = VideoSource(site_constant.FULL_URL, False)
59
- video_source.setup(select_title.id)
58
+ video_source = VideoSource(site_constant.FULL_URL, False, select_title.id)
60
59
 
61
60
  # Retrieve scws and if available master playlist
62
61
  video_source.get_iframe(select_title.id)
@@ -39,28 +39,22 @@ console = Console()
39
39
 
40
40
  def download_video(index_season_selected: int, index_episode_selected: int, scrape_serie: GetSerieInfo, video_source: VideoSource) -> Tuple[str,bool]:
41
41
  """
42
- Download a single episode video.
42
+ Downloads a specific episode from the specified season.
43
43
 
44
44
  Parameters:
45
- - index_season_selected (int): Index of the selected season.
46
- - index_episode_selected (int): Index of the selected episode.
47
-
48
- Return:
49
- - str: output path
50
- - bool: kill handler status
45
+ - index_season_selected (int): Season number
46
+ - index_episode_selected (int): Episode index
47
+ - scrape_serie (GetSerieInfo): Scraper object with series information
48
+ - video_source (VideoSource): Video source handler
49
+
50
+ Returns:
51
+ - str: Path to downloaded file
52
+ - bool: Whether download was stopped
51
53
  """
52
54
  start_message()
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
61
55
 
62
- # Get info about episode
63
- obj_episode = season.episodes.get(index_episode_selected - 1)
56
+ # Get episode information
57
+ obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
64
58
  console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [bold magenta]{obj_episode.name}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
65
59
 
66
60
  if site_constant.TELEGRAM_BOT:
@@ -98,28 +92,28 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
98
92
 
99
93
  return r_proc['path'], r_proc['stopped']
100
94
 
101
- def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, video_source: VideoSource, download_all: bool = False) -> None:
95
+
96
+ def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, video_source: VideoSource, download_all: bool = False, episode_selection: str = None) -> None:
102
97
  """
103
- Download episodes of a selected season.
98
+ Handle downloading episodes for a specific season.
104
99
 
105
100
  Parameters:
106
- - index_season_selected (int): Index of the selected season.
107
- - download_all (bool): Download all episodes in the season.
101
+ - index_season_selected (int): Season number
102
+ - scrape_serie (GetSerieInfo): Scraper object with series information
103
+ - video_source (VideoSource): Video source object
104
+ - download_all (bool): Whether to download all episodes
105
+ - episode_selection (str, optional): Pre-defined episode selection that bypasses manual input
108
106
  """
109
- start_message()
110
- scrape_serie.collect_info_season(index_season_selected)
111
-
112
- # SPECIAL: Get season number
113
- season = None
114
- for s in scrape_serie.seasons_manager.seasons:
115
- if s.number == index_season_selected:
116
- season = s
117
- break
118
- episodes_count = len(season.episodes.episodes)
107
+ # Get episodes for the selected season
108
+ episodes = scrape_serie.getEpisodeSeasons(index_season_selected)
109
+ episodes_count = len(episodes)
119
110
 
120
- if download_all:
111
+ if episodes_count == 0:
112
+ console.print(f"[red]No episodes found for season {index_season_selected}")
113
+ return
121
114
 
122
- # Download all episodes without asking
115
+ if download_all:
116
+ # Download all episodes in the season
123
117
  for i_episode in range(1, episodes_count + 1):
124
118
  path, stopped = download_video(index_season_selected, i_episode, scrape_serie, video_source)
125
119
 
@@ -129,16 +123,16 @@ def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, vid
129
123
  console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.")
130
124
 
131
125
  else:
132
-
133
126
  # Display episodes list and manage user selection
134
- last_command = display_episodes_list(season.episodes.episodes)
127
+ if episode_selection is None:
128
+ last_command = display_episodes_list(episodes)
129
+ else:
130
+ last_command = episode_selection
131
+ console.print(f"\n[cyan]Using provided episode selection: [yellow]{episode_selection}")
132
+
133
+ # Validate the selection
135
134
  list_episode_select = manage_selection(last_command, episodes_count)
136
-
137
- try:
138
- list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
139
- except ValueError as e:
140
- console.print(f"[red]{str(e)}")
141
- return
135
+ list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
142
136
 
143
137
  # Download selected episodes if not stopped
144
138
  for i_episode in list_episode_select:
@@ -147,70 +141,65 @@ def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, vid
147
141
  if stopped:
148
142
  break
149
143
 
150
- def download_series(select_season: MediaItem) -> None:
144
+
145
+ def download_series(select_season: MediaItem, season_selection: str = None, episode_selection: str = None) -> None:
151
146
  """
152
- Download episodes of a TV series based on user selection.
147
+ Handle downloading a complete series.
153
148
 
154
149
  Parameters:
155
- - select_season (MediaItem): Selected media item (TV series).
156
- - domain (str): Domain from which to download.
150
+ - select_season (MediaItem): Series metadata from search
151
+ - season_selection (str, optional): Pre-defined season selection that bypasses manual input
152
+ - episode_selection (str, optional): Pre-defined episode selection that bypasses manual input
157
153
  """
158
- if site_constant.TELEGRAM_BOT:
159
- bot = get_bot_instance()
160
-
161
- # Start message and set up video source
162
154
  start_message()
163
155
 
164
156
  # Init class
165
- scrape_serie = GetSerieInfo(site_constant.FULL_URL)
166
- video_source = VideoSource(site_constant.FULL_URL, True)
167
-
168
- # Setup video source
169
- scrape_serie.setup(select_season.id, select_season.slug)
170
- video_source.setup(select_season.id)
157
+ video_source = VideoSource(site_constant.FULL_URL, True, select_season.id)
158
+ scrape_serie = GetSerieInfo(site_constant.FULL_URL, select_season.id, select_season.name)
171
159
 
172
- # Collect information about seasons
173
- scrape_serie.collect_info_title()
160
+ # Collect information about season
161
+ scrape_serie.getNumberSeason()
174
162
  seasons_count = len(scrape_serie.seasons_manager)
175
163
 
164
+ if site_constant.TELEGRAM_BOT:
165
+ bot = get_bot_instance()
166
+
176
167
  # Prompt user for season selection and download episodes
177
168
  console.print(f"\n[green]Seasons found: [red]{seasons_count}")
178
169
 
179
- if site_constant.TELEGRAM_BOT:
180
- console.print("\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
181
- "[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")
182
-
183
- bot.send_message(f"Stagioni trovate: {seasons_count}", None)
184
-
185
- index_season_selected = bot.ask(
186
- "select_title_episode",
187
- "Menu di selezione delle stagioni\n\n"
188
- "- Inserisci il numero della stagione (ad esempio, 1)\n"
189
- "- Inserisci * per scaricare tutte le stagioni\n"
190
- "- Inserisci un intervallo di stagioni (ad esempio, 1-2) per scaricare da una stagione all'altra\n"
191
- "- Inserisci (ad esempio, 3-*) per scaricare dalla stagione specificata fino alla fine della serie",
192
- None
193
- )
170
+ # If season_selection is provided, use it instead of asking for input
171
+ if season_selection is None:
172
+ if site_constant.TELEGRAM_BOT:
173
+ console.print("\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
174
+ "[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")
175
+
176
+ bot.send_message(f"Stagioni trovate: {seasons_count}", None)
177
+
178
+ index_season_selected = bot.ask(
179
+ "select_title_episode",
180
+ "Menu di selezione delle stagioni\n\n"
181
+ "- Inserisci il numero della stagione (ad esempio, 1)\n"
182
+ "- Inserisci * per scaricare tutte le stagioni\n"
183
+ "- Inserisci un intervallo di stagioni (ad esempio, 1-2) per scaricare da una stagione all'altra\n"
184
+ "- Inserisci (ad esempio, 3-*) per scaricare dalla stagione specificata fino alla fine della serie",
185
+ None
186
+ )
194
187
 
188
+ else:
189
+ index_season_selected = msg.ask(
190
+ "\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
191
+ "[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"
192
+ )
195
193
  else:
196
- index_season_selected = msg.ask(
197
- "\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
198
- "[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"
199
- )
194
+ index_season_selected = season_selection
195
+ console.print(f"\n[cyan]Using provided season selection: [yellow]{season_selection}")
200
196
 
201
- # Manage and validate the selection
197
+ # Validate the selection
202
198
  list_season_select = manage_selection(index_season_selected, seasons_count)
203
-
204
- try:
205
- list_season_select = validate_selection(list_season_select, seasons_count)
206
- except ValueError as e:
207
- console.print(f"[red]{str(e)}")
208
- return
199
+ list_season_select = validate_selection(list_season_select, seasons_count)
209
200
 
210
201
  # Loop through the selected seasons and download episodes
211
202
  for i_season in list_season_select:
212
-
213
- # SPECIAL: Get season number
214
203
  season = None
215
204
  for s in scrape_serie.seasons_manager.seasons:
216
205
  if s.number == i_season:
@@ -219,13 +208,10 @@ def download_series(select_season: MediaItem) -> None:
219
208
  season_number = season.number
220
209
 
221
210
  if len(list_season_select) > 1 or index_season_selected == "*":
222
-
223
- # Download all episodes if multiple seasons are selected or if '*' is used
224
211
  download_episode(season_number, scrape_serie, video_source, download_all=True)
212
+
225
213
  else:
226
-
227
- # Otherwise, let the user select specific episodes for the single season
228
- download_episode(season_number, scrape_serie, video_source, download_all=False)
214
+ download_episode(season_number, scrape_serie, video_source, download_all=False, episode_selection=episode_selection)
229
215
 
230
216
  if site_constant.TELEGRAM_BOT:
231
217
  bot.send_message(f"Finito di scaricare tutte le serie e episodi", None)
@@ -1,7 +1,5 @@
1
1
  # 10.12.23
2
2
 
3
- import sys
4
-
5
3
 
6
4
  # External libraries
7
5
  import httpx
@@ -75,7 +73,7 @@ def title_search(query: str) -> int:
75
73
  'name': dict_title.get('name'),
76
74
  'type': dict_title.get('type'),
77
75
  'date': dict_title.get('last_air_date'),
78
- 'score': dict_title.get('score')
76
+ 'image': f"{site_constant.FULL_URL.replace('stream', 'cdn.stream')}/images/{dict_title.get('images')[0].get('filename')}"
79
77
  })
80
78
 
81
79
  if site_constant.TELEGRAM_BOT: