StreamingCommunity 2.9.8__py3-none-any.whl → 3.0.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 (67) 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 +2 -3
  6. StreamingCommunity/Api/Site/1337xx/site.py +6 -1
  7. StreamingCommunity/Api/Site/altadefinizione/__init__.py +24 -9
  8. StreamingCommunity/Api/Site/altadefinizione/film.py +0 -1
  9. StreamingCommunity/Api/Site/altadefinizione/series.py +66 -70
  10. StreamingCommunity/Api/Site/altadefinizione/site.py +8 -2
  11. StreamingCommunity/Api/Site/altadefinizione/util/ScrapeSerie.py +37 -2
  12. StreamingCommunity/Api/Site/animeunity/__init__.py +30 -12
  13. StreamingCommunity/Api/Site/animeunity/film.py +40 -0
  14. StreamingCommunity/Api/Site/animeunity/serie.py +153 -0
  15. StreamingCommunity/Api/Site/animeunity/site.py +64 -37
  16. StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py +41 -22
  17. StreamingCommunity/Api/Site/animeworld/__init__.py +26 -14
  18. StreamingCommunity/Api/Site/animeworld/film.py +63 -0
  19. StreamingCommunity/Api/Site/animeworld/serie.py +25 -22
  20. StreamingCommunity/Api/Site/animeworld/site.py +8 -2
  21. StreamingCommunity/Api/Site/animeworld/util/ScrapeSerie.py +32 -5
  22. StreamingCommunity/Api/Site/cb01new/__init__.py +2 -3
  23. StreamingCommunity/Api/Site/cb01new/site.py +7 -1
  24. StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +2 -3
  25. StreamingCommunity/Api/Site/ddlstreamitaly/series.py +31 -32
  26. StreamingCommunity/Api/Site/ddlstreamitaly/site.py +8 -3
  27. StreamingCommunity/Api/Site/ddlstreamitaly/util/ScrapeSerie.py +30 -2
  28. StreamingCommunity/Api/Site/guardaserie/__init__.py +22 -9
  29. StreamingCommunity/Api/Site/guardaserie/series.py +55 -53
  30. StreamingCommunity/Api/Site/guardaserie/site.py +10 -3
  31. StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +27 -1
  32. StreamingCommunity/Api/Site/raiplay/__init__.py +92 -0
  33. StreamingCommunity/Api/Site/raiplay/film.py +65 -0
  34. StreamingCommunity/Api/Site/raiplay/series.py +162 -0
  35. StreamingCommunity/Api/Site/raiplay/site.py +173 -0
  36. StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py +127 -0
  37. StreamingCommunity/Api/Site/streamingcommunity/__init__.py +30 -24
  38. StreamingCommunity/Api/Site/streamingcommunity/film.py +1 -2
  39. StreamingCommunity/Api/Site/streamingcommunity/series.py +76 -90
  40. StreamingCommunity/Api/Site/streamingcommunity/site.py +8 -4
  41. StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +41 -15
  42. StreamingCommunity/Api/Template/site.py +2 -2
  43. StreamingCommunity/Lib/Downloader/HLS/downloader.py +1 -1
  44. StreamingCommunity/Lib/Downloader/HLS/segments.py +9 -18
  45. StreamingCommunity/Lib/Downloader/MP4/downloader.py +2 -1
  46. StreamingCommunity/Lib/Downloader/TOR/downloader.py +7 -14
  47. StreamingCommunity/Lib/FFmpeg/capture.py +1 -5
  48. StreamingCommunity/Lib/FFmpeg/util.py +57 -19
  49. StreamingCommunity/Lib/M3U8/estimator.py +57 -41
  50. StreamingCommunity/Lib/M3U8/parser.py +26 -6
  51. StreamingCommunity/Upload/update.py +22 -3
  52. StreamingCommunity/Upload/version.py +1 -1
  53. StreamingCommunity/Util/ffmpeg_installer.py +26 -1
  54. StreamingCommunity/Util/os.py +13 -15
  55. StreamingCommunity/Util/table.py +4 -2
  56. StreamingCommunity/global_search.py +1 -4
  57. StreamingCommunity/run.py +1 -4
  58. {streamingcommunity-2.9.8.dist-info → streamingcommunity-3.0.0.dist-info}/METADATA +1 -1
  59. streamingcommunity-3.0.0.dist-info/RECORD +91 -0
  60. {streamingcommunity-2.9.8.dist-info → streamingcommunity-3.0.0.dist-info}/WHEEL +1 -1
  61. StreamingCommunity/Api/Site/animeunity/film_serie.py +0 -181
  62. StreamingCommunity/Api/Site/mostraguarda/__init__.py +0 -73
  63. StreamingCommunity/Api/Site/mostraguarda/film.py +0 -93
  64. streamingcommunity-2.9.8.dist-info/RECORD +0 -85
  65. {streamingcommunity-2.9.8.dist-info → streamingcommunity-3.0.0.dist-info}/entry_points.txt +0 -0
  66. {streamingcommunity-2.9.8.dist-info → streamingcommunity-3.0.0.dist-info}/licenses/LICENSE +0 -0
  67. {streamingcommunity-2.9.8.dist-info → streamingcommunity-3.0.0.dist-info}/top_level.txt +0 -0
@@ -26,8 +26,7 @@ from .series import download_series
26
26
  # Variable
27
27
  indice = 0
28
28
  _useFor = "film_serie"
29
- _deprecate = False
30
- _priority = 1
29
+ _priority = 0
31
30
  _engineDownload = "hls"
32
31
 
33
32
  msg = Prompt()
@@ -58,52 +57,59 @@ def get_user_input(string_to_search: str = None):
58
57
 
59
58
  return string_to_search
60
59
 
61
- def process_search_result(select_title):
60
+ def process_search_result(select_title, selections=None):
62
61
  """
63
62
  Handles the search result and initiates the download for either a film or series.
63
+
64
+ Parameters:
65
+ select_title (MediaItem): The selected media item
66
+ selections (dict, optional): Dictionary containing selection inputs that bypass manual input
67
+ {'season': season_selection, 'episode': episode_selection}
64
68
  """
65
69
  if select_title.type == 'tv':
66
- download_series(select_title)
70
+ season_selection = None
71
+ episode_selection = None
72
+
73
+ if selections:
74
+ season_selection = selections.get('season')
75
+ episode_selection = selections.get('episode')
76
+
77
+ download_series(select_title, season_selection, episode_selection)
78
+
67
79
  else:
68
80
  download_film(select_title)
69
81
 
70
- def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
82
+ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
71
83
  """
72
- Main function of the application for search film, series and anime.
84
+ Main function of the application for search.
73
85
 
74
86
  Parameters:
75
87
  string_to_search (str, optional): String to search for
76
- get_onylDatabase (bool, optional): If True, return only the database object
88
+ get_onlyDatabase (bool, optional): If True, return only the database object
77
89
  direct_item (dict, optional): Direct item to process (bypass search)
90
+ selections (dict, optional): Dictionary containing selection inputs that bypass manual input
91
+ {'season': season_selection, 'episode': episode_selection}
78
92
  """
79
93
  if direct_item:
80
94
  select_title = MediaItem(**direct_item)
81
- process_search_result(select_title)
95
+ process_search_result(select_title, selections)
82
96
  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))
97
+
98
+ if string_to_search is None:
99
+ string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
100
+
101
+ # Search on database
102
+ len_database = title_search(string_to_search)
89
103
 
90
104
  # If only the database is needed, return the manager
91
105
  if get_onlyDatabase:
92
106
  return media_search_manager
93
107
 
94
- if site_constant.TELEGRAM_BOT:
95
- bot = get_bot_instance()
96
-
97
108
  if len_database > 0:
98
109
  select_title = get_select_title(table_show_manager, media_search_manager)
99
- process_search_result(select_title)
110
+ process_search_result(select_title, selections)
100
111
 
101
112
  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
113
  # If no results are found, ask again
108
- string_to_search = get_user_input()
114
+ console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
109
115
  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
@@ -47,7 +45,13 @@ def title_search(query: str) -> int:
47
45
  console.print(f"[cyan]Search url: [yellow]{search_url}")
48
46
 
49
47
  try:
50
- response = httpx.get(search_url, headers={'user-agent': get_userAgent()}, timeout=max_timeout, follow_redirects=True)
48
+ response = httpx.get(
49
+ search_url,
50
+ headers={'user-agent': get_userAgent()},
51
+ timeout=max_timeout,
52
+ follow_redirects=True,
53
+ verify=False
54
+ )
51
55
  response.raise_for_status()
52
56
 
53
57
  except Exception as e:
@@ -75,7 +79,7 @@ def title_search(query: str) -> int:
75
79
  'name': dict_title.get('name'),
76
80
  'type': dict_title.get('type'),
77
81
  'date': dict_title.get('last_air_date'),
78
- 'score': dict_title.get('score')
82
+ 'image': f"{site_constant.FULL_URL.replace('stream', 'cdn.stream')}/images/{dict_title.get('images')[0].get('filename')}"
79
83
  })
80
84
 
81
85
  if site_constant.TELEGRAM_BOT:
@@ -20,31 +20,21 @@ max_timeout = config_manager.get_int("REQUESTS", "timeout")
20
20
 
21
21
 
22
22
  class GetSerieInfo:
23
- def __init__(self, url):
23
+ def __init__(self, url, media_id: int = None, series_name: str = None):
24
24
  """
25
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.
29
+ - media_id (int, optional): Unique identifier for the media
30
+ - series_name (str, optional): Name of the TV series
29
31
  """
30
32
  self.is_series = False
31
33
  self.headers = {'user-agent': get_userAgent()}
32
34
  self.url = url
33
-
34
- # Initialize the SeasonManager
35
- self.seasons_manager = SeasonManager()
36
-
37
- def setup(self, media_id: int = None, series_name: str = None):
38
- """
39
- Set up the scraper with specific media details.
40
-
41
- Args:
42
- media_id (int, optional): Unique identifier for the media
43
- series_name (str, optional): Name of the TV series
44
- """
45
35
  self.media_id = media_id
36
+ self.seasons_manager = SeasonManager()
46
37
 
47
- # If series name is provided, initialize series-specific properties
48
38
  if series_name is not None:
49
39
  self.is_series = True
50
40
  self.series_name = series_name
@@ -127,4 +117,40 @@ class GetSerieInfo:
127
117
 
128
118
  except Exception as e:
129
119
  logging.error(f"Error collecting episodes for season {number_season}: {e}")
130
- raise
120
+ raise
121
+
122
+ # ------------- FOR GUI -------------
123
+ def getNumberSeason(self) -> int:
124
+ """
125
+ Get the total number of seasons available for the series.
126
+ """
127
+ if not self.seasons_manager.seasons:
128
+ self.collect_info_title()
129
+
130
+ return len(self.seasons_manager.seasons)
131
+
132
+ def getEpisodeSeasons(self, season_number: int) -> list:
133
+ """
134
+ Get all episodes for a specific season.
135
+ """
136
+ season = self.seasons_manager.get_season_by_number(season_number)
137
+
138
+ if not season:
139
+ logging.error(f"Season {season_number} not found")
140
+ return []
141
+
142
+ if not season.episodes.episodes:
143
+ self.collect_info_season(season_number)
144
+
145
+ return season.episodes.episodes
146
+
147
+ def selectEpisode(self, season_number: int, episode_index: int) -> dict:
148
+ """
149
+ Get information for a specific episode in a specific season.
150
+ """
151
+ episodes = self.getEpisodeSeasons(season_number)
152
+ if not episodes or episode_index < 0 or episode_index >= len(episodes):
153
+ logging.error(f"Episode index {episode_index} is out of range for season {season_number}")
154
+ return None
155
+
156
+ return episodes[episode_index]
@@ -10,7 +10,7 @@ from rich.console import Console
10
10
  # Variable
11
11
  console = Console()
12
12
  available_colors = ['red', 'magenta', 'yellow', 'cyan', 'green', 'blue', 'white']
13
- column_to_hide = ['Slug', 'Sub_ita', 'Last_air_date', 'Seasons_count', 'Url']
13
+ column_to_hide = ['Slug', 'Sub_ita', 'Last_air_date', 'Seasons_count', 'Url', 'Image', 'Path_id']
14
14
 
15
15
 
16
16
  def get_select_title(table_show_manager, media_search_manager):
@@ -81,4 +81,4 @@ def get_select_title(table_show_manager, media_search_manager):
81
81
 
82
82
  else:
83
83
  console.print("\n[red]Wrong index")
84
- sys.exit(0)
84
+ sys.exit(0)
@@ -514,7 +514,7 @@ class HLS_Downloader:
514
514
  for item in self.download_manager.missing_segments:
515
515
  if int(item['nFailed']) >= 1:
516
516
  missing_ts = True
517
- missing_info += f"[red]TS Failed: {item['nFailed']} {item['type']} tracks[/red]\n"
517
+ missing_info += f"[red]TS Failed: {item['nFailed']} {item['type']} tracks[/red]"
518
518
 
519
519
  file_size = internet_manager.format_file_size(os.path.getsize(self.path_manager.output_path))
520
520
  duration = print_duration_table(self.path_manager.output_path, description=False, return_string=True)
@@ -23,7 +23,7 @@ from rich.console import Console
23
23
  # Internal utilities
24
24
  from StreamingCommunity.Util.color import Colors
25
25
  from StreamingCommunity.Util.headers import get_userAgent
26
- from StreamingCommunity.Util.config_json import config_manager, get_use_large_bar
26
+ from StreamingCommunity.Util.config_json import config_manager
27
27
 
28
28
 
29
29
  # Logic class
@@ -41,10 +41,9 @@ REQUEST_VERIFY = config_manager.get_bool('REQUESTS', 'verify')
41
41
  DEFAULT_VIDEO_WORKERS = config_manager.get_int('M3U8_DOWNLOAD', 'default_video_workser')
42
42
  DEFAULT_AUDIO_WORKERS = config_manager.get_int('M3U8_DOWNLOAD', 'default_audio_workser')
43
43
  MAX_TIMEOOUT = config_manager.get_int("REQUESTS", "timeout")
44
- MAX_INTERRUPT_COUNT = 3
45
44
  SEGMENT_MAX_TIMEOUT = config_manager.get_int("M3U8_DOWNLOAD", "segment_timeout")
46
45
  TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
47
-
46
+ MAX_INTERRUPT_COUNT = 3
48
47
 
49
48
  # Variable
50
49
  console = Console()
@@ -160,7 +159,7 @@ class M3U8_Segments:
160
159
  if self.is_index_url:
161
160
  try:
162
161
  client_params = {'headers': {'User-Agent': get_userAgent()}, 'timeout': MAX_TIMEOOUT}
163
- response = httpx.get(self.url, **client_params)
162
+ response = httpx.get(self.url, **client_params, follow_redirects=True)
164
163
  response.raise_for_status()
165
164
 
166
165
  self.parse_data(response.text)
@@ -408,20 +407,12 @@ class M3U8_Segments:
408
407
  """
409
408
  Generate platform-appropriate progress bar format.
410
409
  """
411
- if not get_use_large_bar():
412
- return (
413
- f"{Colors.YELLOW}Proc{Colors.WHITE}: "
414
- f"{Colors.RED}{{percentage:.2f}}% "
415
- f"{Colors.CYAN}{{remaining}}{{postfix}} {Colors.WHITE}]"
416
- )
417
-
418
- else:
419
- return (
420
- f"{Colors.YELLOW}[HLS] {Colors.WHITE}({Colors.CYAN}{description}{Colors.WHITE}): "
421
- f"{Colors.RED}{{percentage:.2f}}% "
422
- f"{Colors.MAGENTA}{{bar}} "
423
- f"{Colors.YELLOW}{{elapsed}}{Colors.WHITE} < {Colors.CYAN}{{remaining}}{Colors.WHITE}{{postfix}}{Colors.WHITE}"
424
- )
410
+ return (
411
+ f"{Colors.YELLOW}[HLS] {Colors.WHITE}({Colors.CYAN}{description}{Colors.WHITE}): "
412
+ f"{Colors.RED}{{percentage:.2f}}% "
413
+ f"{Colors.MAGENTA}{{bar}} "
414
+ f"{Colors.YELLOW}{{elapsed}}{Colors.WHITE} < {Colors.CYAN}{{remaining}}{Colors.WHITE}{{postfix}}{Colors.WHITE}"
415
+ )
425
416
 
426
417
  def _get_worker_count(self, stream_type: str) -> int:
427
418
  """
@@ -21,7 +21,7 @@ from rich.panel import Panel
21
21
  from StreamingCommunity.Util.headers import get_userAgent
22
22
  from StreamingCommunity.Util.color import Colors
23
23
  from StreamingCommunity.Util.config_json import config_manager
24
- from StreamingCommunity.Util.os import internet_manager
24
+ from StreamingCommunity.Util.os import internet_manager, os_manager
25
25
  from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
26
26
 
27
27
 
@@ -80,6 +80,7 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
80
80
  bot = get_bot_instance()
81
81
  console.log("####")
82
82
 
83
+ path = os_manager.get_sanitize_path(path)
83
84
  if os.path.exists(path):
84
85
  console.log("[red]Output file already exists.")
85
86
  if TELEGRAM_BOT:
@@ -18,7 +18,7 @@ import qbittorrentapi
18
18
  # Internal utilities
19
19
  from StreamingCommunity.Util.color import Colors
20
20
  from StreamingCommunity.Util.os import internet_manager
21
- from StreamingCommunity.Util.config_json import config_manager, get_use_large_bar
21
+ from StreamingCommunity.Util.config_json import config_manager
22
22
 
23
23
 
24
24
  # Configuration
@@ -316,19 +316,12 @@ class TOR_downloader:
316
316
  # Ensure the torrent is started
317
317
  self.qb.torrents_resume(torrent_hashes=self.latest_torrent_hash)
318
318
 
319
- # Configure progress bar display format based on device
320
- if get_use_large_bar():
321
- bar_format = (
322
- f"{Colors.YELLOW}[TOR] {Colors.WHITE}({Colors.CYAN}video{Colors.WHITE}): "
323
- f"{Colors.RED}{{percentage:.2f}}% {Colors.MAGENTA}{{bar}} {Colors.WHITE}[ "
324
- f"{Colors.YELLOW}{{elapsed}} {Colors.WHITE}< {Colors.CYAN}{{remaining}}{{postfix}} {Colors.WHITE}]"
325
- )
326
- else:
327
- bar_format = (
328
- f"{Colors.YELLOW}Proc{Colors.WHITE}: "
329
- f"{Colors.RED}{{percentage:.2f}}% {Colors.WHITE}| "
330
- f"{Colors.CYAN}{{remaining}}{{postfix}} {Colors.WHITE}]"
331
- )
319
+ # Configure progress bar display format
320
+ bar_format = (
321
+ f"{Colors.YELLOW}[TOR] {Colors.WHITE}({Colors.CYAN}video{Colors.WHITE}): "
322
+ f"{Colors.RED}{{percentage:.2f}}% {Colors.MAGENTA}{{bar}} {Colors.WHITE}[ "
323
+ f"{Colors.YELLOW}{{elapsed}} {Colors.WHITE}< {Colors.CYAN}{{remaining}}{{postfix}} {Colors.WHITE}]"
324
+ )
332
325
 
333
326
  # Initialize progress bar
334
327
  with tqdm(
@@ -36,11 +36,7 @@ def capture_output(process: subprocess.Popen, description: str) -> None:
36
36
  if not line:
37
37
  continue
38
38
 
39
- logging.info(f"FFMPEG line: {line}")
40
-
41
- # Capture only error
42
- if "rror" in str(line):
43
- console.log(f"[red]FFMPEG: {str(line).strip()}")
39
+ logging.info(f"CAPTURE ffmpeg line: {line}")
44
40
 
45
41
  # Check if termination is requested
46
42
  if terminate_flag.is_set():