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,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
 
@@ -10,11 +11,15 @@ from rich.prompt import Prompt
10
11
 
11
12
 
12
13
  # Internal utilities
14
+ from StreamingCommunity.Util.headers import get_headers
15
+ from StreamingCommunity.Util.os import get_wvd_path
13
16
  from StreamingCommunity.Util.message import start_message
14
17
 
15
18
 
19
+
16
20
  # Logic class
17
21
  from .util.ScrapeSerie import GetSerieInfo
22
+ from .util.get_license import generate_license_url
18
23
  from StreamingCommunity.Api.Template.Util import (
19
24
  manage_selection,
20
25
  map_episode_title,
@@ -27,7 +32,7 @@ from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
27
32
 
28
33
 
29
34
  # Player
30
- from StreamingCommunity import HLS_Downloader
35
+ from StreamingCommunity import HLS_Downloader, DASH_Downloader
31
36
  from StreamingCommunity.Api.Player.mediapolisvod import VideoSource
32
37
 
33
38
 
@@ -55,22 +60,59 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
55
60
  obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
56
61
  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")
57
62
 
58
- # Get streaming URL
59
- master_playlist = VideoSource.extract_m3u8_url(obj_episode.url)
60
-
61
63
  # Define filename and path
62
64
  mp4_name = f"{map_episode_title(scrape_serie.series_name, index_season_selected, index_episode_selected, obj_episode.name)}.mp4"
63
65
  mp4_path = os.path.join(site_constant.SERIES_FOLDER, scrape_serie.series_name, f"S{index_season_selected}")
64
66
 
65
- # Download the episode
66
- r_proc = HLS_Downloader(
67
- m3u8_url=master_playlist,
68
- output_path=os.path.join(mp4_path, mp4_name)
69
- ).start()
67
+ # Get streaming URL
68
+ master_playlist = VideoSource.extract_m3u8_url(obj_episode.url)
69
+
70
+ # HLS
71
+ if ".mpd" not in master_playlist:
72
+ r_proc = HLS_Downloader(
73
+ m3u8_url=master_playlist,
74
+ output_path=os.path.join(mp4_path, mp4_name)
75
+ ).start()
76
+ # Get streaming URL
77
+ master_playlist = VideoSource.extract_m3u8_url(obj_episode.url)
78
+
79
+ # HLS
80
+ if ".mpd" not in master_playlist:
81
+ r_proc = HLS_Downloader(
82
+ m3u8_url=master_playlist,
83
+ output_path=os.path.join(mp4_path, mp4_name)
84
+ ).start()
85
+
86
+ # MPD
87
+ else:
88
+
89
+ # Check CDM file before usage
90
+ cdm_device_path = get_wvd_path()
91
+ if not cdm_device_path or not isinstance(cdm_device_path, (str, bytes, os.PathLike)) or not os.path.isfile(cdm_device_path):
92
+ console.print(f"[bold red] CDM file not found or invalid path: {cdm_device_path}[/bold red]")
93
+ sys.exit(0)
94
+
95
+ license_url = generate_license_url(obj_episode.mpd_id)
96
+
97
+ dash_process = DASH_Downloader(
98
+ cdm_device=cdm_device_path,
99
+ license_url=license_url,
100
+ mpd_url=master_playlist,
101
+ output_path=os.path.join(mp4_path, mp4_name),
102
+ )
103
+ dash_process.parse_manifest(custom_headers=get_headers())
104
+
105
+ if dash_process.download_and_decrypt():
106
+ dash_process.finalize_output()
107
+
108
+ # Get final output path and status
109
+ r_proc = dash_process.get_status()
70
110
 
71
111
  if r_proc['error'] is not None:
72
- try: os.remove(r_proc['path'])
73
- except: pass
112
+ try:
113
+ os.remove(r_proc['path'])
114
+ except Exception:
115
+ pass
74
116
 
75
117
  return r_proc['path'], r_proc['stopped']
76
118
 
@@ -11,6 +11,9 @@ from StreamingCommunity.Util.headers import get_userAgent
11
11
  from StreamingCommunity.Util.table import TVShowManager
12
12
  from StreamingCommunity.Api.Template.config_loader import site_constant
13
13
  from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
14
+
15
+
16
+ # Logic Import
14
17
  from .util.ScrapeSerie import GetSerieInfo
15
18
 
16
19
 
@@ -59,7 +62,7 @@ def title_search(query: str) -> int:
59
62
  media_search_manager.clear()
60
63
  table_show_manager.clear()
61
64
 
62
- search_url = f"https://www.raiplay.it/atomatic/raiplay-search-service/api/v1/msearch"
65
+ search_url = "https://www.raiplay.it/atomatic/raiplay-search-service/api/v1/msearch"
63
66
  console.print(f"[cyan]Search url: [yellow]{search_url}")
64
67
 
65
68
  json_data = {
@@ -101,7 +101,8 @@ class GetSerieInfo:
101
101
  'number': ep.get('episode', ''),
102
102
  'name': ep.get('episode_title', '') or ep.get('toptitle', ''),
103
103
  'duration': ep.get('duration', ''),
104
- 'url': f"{self.base_url}{ep.get('weblink', '')}" if 'weblink' in ep else f"{self.base_url}{ep.get('url', '')}"
104
+ 'url': f"{self.base_url}{ep.get('weblink', '')}" if 'weblink' in ep else f"{self.base_url}{ep.get('url', '')}",
105
+ 'mpd_id': ep.get('video_url').split("=")[1].strip()
105
106
  }
106
107
  season.episodes.add(episode)
107
108
 
@@ -0,0 +1,40 @@
1
+ # 16.03.25
2
+
3
+
4
+ # External library
5
+ import httpx
6
+
7
+
8
+ # Internal utilities
9
+ from StreamingCommunity.Util.config_json import config_manager
10
+ from StreamingCommunity.Util.headers import get_headers
11
+
12
+
13
+ # Variable
14
+ MAX_TIMEOUT = config_manager.get_int("REQUESTS", "timeout")
15
+
16
+
17
+
18
+ def generate_license_url(mpd_id: str):
19
+ """
20
+ Generates the URL to obtain the Widevine license.
21
+
22
+ Args:
23
+ mpd_id (str): The ID of the MPD (Media Presentation Description) file.
24
+
25
+ Returns:
26
+ str: The full license URL.
27
+ """
28
+ params = {
29
+ 'cont': mpd_id,
30
+ 'output': '62',
31
+ }
32
+
33
+ response = httpx.get('https://mediapolisvod.rai.it/relinker/relinkerServlet.htm', params=params, headers=get_headers(), timeout=MAX_TIMEOUT)
34
+ response.raise_for_status()
35
+
36
+ # Extract the license URL from the response in two lines
37
+ json_data = response.json()
38
+ license_url = json_data.get('licence_server_map').get('drmLicenseUrlValues')[0].get('licenceUrl')
39
+
40
+ return license_url
@@ -2,7 +2,6 @@
2
2
 
3
3
  import sys
4
4
  import subprocess
5
- from urllib.parse import quote_plus
6
5
 
7
6
 
8
7
  # External library
@@ -16,7 +15,6 @@ from StreamingCommunity.Api.Template.config_loader import site_constant
16
15
  from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
17
16
  from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
18
17
 
19
-
20
18
  # Logic class
21
19
  from .site import title_search, table_show_manager, media_search_manager
22
20
  from .film import download_film
@@ -25,7 +23,7 @@ from .series import download_series
25
23
 
26
24
  # Variable
27
25
  indice = 0
28
- _useFor = "Film_&_Serie" # "Movies_&_Series"
26
+ _useFor = "Film_&_Serie"
29
27
  _priority = 0
30
28
  _engineDownload = "hls"
31
29
  _deprecate = False
@@ -126,9 +124,9 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
126
124
  actual_search_query = get_user_input(string_to_search)
127
125
 
128
126
  # Handle cases where user input is empty, or 'back' was handled (sys.exit or None return)
129
- if not actual_search_query:
127
+ if not actual_search_query:
130
128
  if bot:
131
- if actual_search_query is None: # Specifically for timeout from bot.ask or failed restart
129
+ if actual_search_query is None: # Specifically for timeout from bot.ask or failed restart
132
130
  bot.send_message("Search term not provided or operation cancelled. Returning.", None)
133
131
  return
134
132
 
@@ -137,16 +135,15 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
137
135
 
138
136
  # If only the database object (media_search_manager populated by title_search) is needed
139
137
  if get_onlyDatabase:
140
- return media_search_manager
138
+ return media_search_manager
141
139
 
142
140
  if len_database > 0:
143
141
  select_title = get_select_title(table_show_manager, media_search_manager, len_database)
144
142
  process_search_result(select_title, selections)
145
143
 
146
144
  else:
147
- no_results_message = f"No results found for: '{actual_search_query}'"
148
145
  if bot:
149
- bot.send_message(no_results_message, None)
146
+ bot.send_message(f"No results found for: '{actual_search_query}'", None)
150
147
  else:
151
148
  console.print(f"\n[red]Nothing matching was found for[white]: [purple]{actual_search_query}")
152
149
 
@@ -71,7 +71,7 @@ def download_film(select_title: MediaItem) -> str:
71
71
  mp4_path = os.path.join(site_constant.MOVIE_FOLDER, title_name.replace(".mp4", ""))
72
72
 
73
73
  # Download the film using the m3u8 playlist, and output filename
74
- r_proc = HLS_Downloader(
74
+ hls_process = HLS_Downloader(
75
75
  m3u8_url=master_playlist,
76
76
  output_path=os.path.join(mp4_path, title_name)
77
77
  ).start()
@@ -83,8 +83,10 @@ def download_film(select_title: MediaItem) -> str:
83
83
  if script_id != "unknown":
84
84
  TelegramSession.deleteScriptId(script_id)
85
85
 
86
- if r_proc['error'] is not None:
87
- try: os.remove(r_proc['path'])
88
- except: pass
86
+ if hls_process['error'] is not None:
87
+ try:
88
+ os.remove(hls_process['path'])
89
+ except Exception:
90
+ pass
89
91
 
90
- return r_proc['path']
92
+ return hls_process['path']
@@ -55,7 +55,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
55
55
 
56
56
  # Get episode information
57
57
  obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
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")
58
+ console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{scrape_serie.series_name}[/cyan] \\ [bold magenta]{obj_episode.name}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
59
59
 
60
60
  if site_constant.TELEGRAM_BOT:
61
61
  bot = get_bot_instance()
@@ -81,16 +81,18 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
81
81
  master_playlist = video_source.get_playlist()
82
82
 
83
83
  # Download the episode
84
- r_proc = HLS_Downloader(
84
+ hls_process = HLS_Downloader(
85
85
  m3u8_url=master_playlist,
86
86
  output_path=os.path.join(mp4_path, mp4_name)
87
87
  ).start()
88
88
 
89
- if r_proc['error'] is not None:
90
- try: os.remove(r_proc['path'])
91
- except: pass
89
+ if hls_process['error'] is not None:
90
+ try:
91
+ os.remove(hls_process['path'])
92
+ except Exception:
93
+ pass
92
94
 
93
- return r_proc['path'], r_proc['stopped']
95
+ return hls_process['path'], hls_process['stopped']
94
96
 
95
97
 
96
98
  def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, video_source: VideoSource, download_all: bool = False, episode_selection: str = None) -> None:
@@ -214,7 +216,7 @@ def download_series(select_season: MediaItem, season_selection: str = None, epis
214
216
  download_episode(season_number, scrape_serie, video_source, download_all=False, episode_selection=episode_selection)
215
217
 
216
218
  if site_constant.TELEGRAM_BOT:
217
- bot.send_message(f"Finito di scaricare tutte le serie e episodi", None)
219
+ bot.send_message("Finito di scaricare tutte le serie e episodi", None)
218
220
 
219
221
  # Get script_id
220
222
  script_id = TelegramSession.get_session()
@@ -26,6 +26,7 @@ console = Console()
26
26
  media_search_manager = MediaManager()
27
27
  table_show_manager = TVShowManager()
28
28
  max_timeout = config_manager.get_int("REQUESTS", "timeout")
29
+ ssl_verify = config_manager.get_bool("REQUESTS", "verify")
29
30
 
30
31
 
31
32
  def title_search(query: str) -> int:
@@ -49,6 +50,7 @@ def title_search(query: str) -> int:
49
50
  f"{site_constant.FULL_URL}/it",
50
51
  headers={'user-agent': get_userAgent()},
51
52
  timeout=max_timeout,
53
+ verify=ssl_verify,
52
54
  follow_redirects=True
53
55
  )
54
56
  response.raise_for_status()
@@ -57,7 +59,9 @@ def title_search(query: str) -> int:
57
59
  version = json.loads(soup.find('div', {'id': "app"}).get("data-page"))['version']
58
60
 
59
61
  except Exception as e:
60
- if "WinError" in str(e) or "Errno" in str(e): console.print("\n[bold yellow]Please make sure you have enabled and configured a valid proxy.[/bold yellow]")
62
+ if "WinError" in str(e) or "Errno" in str(e):
63
+ console.print("\n[bold yellow]Please make sure you have enabled and configured a valid proxy.[/bold yellow]")
64
+
61
65
  console.print(f"[red]Site: {site_constant.SITE_NAME} version, request error: {e}")
62
66
  return 0
63
67
 
@@ -73,7 +77,8 @@ def title_search(query: str) -> int:
73
77
  'x-inertia': 'true',
74
78
  'x-inertia-version': version
75
79
  },
76
- timeout=max_timeout
80
+ timeout=max_timeout,
81
+ verify=ssl_verify
77
82
  )
78
83
  response.raise_for_status()
79
84
 
@@ -116,7 +121,7 @@ def title_search(query: str) -> int:
116
121
 
117
122
  if site_constant.TELEGRAM_BOT:
118
123
  if choices:
119
- bot.send_message(f"Lista dei risultati:", choices)
124
+ bot.send_message("Lista dei risultati:", choices)
120
125
 
121
126
  # Return the number of titles found
122
127
  return media_search_manager.get_length()
@@ -17,6 +17,7 @@ from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager
17
17
 
18
18
  # Variable
19
19
  max_timeout = config_manager.get_int("REQUESTS", "timeout")
20
+ ssl_verify = config_manager.get_bool("REQUESTS", "verify")
20
21
 
21
22
 
22
23
  class GetSerieInfo:
@@ -50,7 +51,8 @@ class GetSerieInfo:
50
51
  response = httpx.get(
51
52
  url=f"{self.url}/titles/{self.media_id}-{self.series_name}",
52
53
  headers=self.headers,
53
- timeout=max_timeout
54
+ timeout=max_timeout,
55
+ verify=ssl_verify
54
56
  )
55
57
  response.raise_for_status()
56
58
 
@@ -104,7 +106,8 @@ class GetSerieInfo:
104
106
  'x-inertia': 'true',
105
107
  'x-inertia-version': self.version,
106
108
  },
107
- timeout=max_timeout
109
+ timeout=max_timeout,
110
+ verify=ssl_verify
108
111
  )
109
112
 
110
113
  # Extract episodes from JSON response
@@ -9,6 +9,7 @@ from rich.prompt import Prompt
9
9
  from StreamingCommunity.Api.Template import get_select_title
10
10
  from StreamingCommunity.Api.Template.config_loader import site_constant
11
11
  from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
12
+ from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
12
13
 
13
14
 
14
15
  # Logic class
@@ -45,6 +46,13 @@ def process_search_result(select_title, selections=None):
45
46
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
46
47
  {'season': season_selection, 'episode': episode_selection}
47
48
  """
49
+ if not select_title:
50
+ if site_constant.TELEGRAM_BOT:
51
+ bot = get_bot_instance()
52
+ bot.send_message("No title selected or selection cancelled.", None)
53
+ else:
54
+ console.print("[yellow]No title selected or selection cancelled.")
55
+ return
48
56
  if select_title.type == 'tv':
49
57
  season_selection = None
50
58
  episode_selection = None
@@ -69,26 +77,52 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
69
77
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
70
78
  {'season': season_selection, 'episode': episode_selection}
71
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
+
72
94
  if direct_item:
73
95
  select_title = MediaItem(**direct_item)
74
96
  process_search_result(select_title, selections)
75
97
  return
76
98
 
77
- if string_to_search is None:
78
- string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
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
79
108
 
80
- # Perform search on the database using the obtained query
81
- len_database = title_search(string_to_search)
109
+ # Perform the database search
110
+ len_database = title_search(actual_search_query)
82
111
 
83
112
  # If only the database is needed, return the manager
84
113
  if get_onlyDatabase:
85
114
  return media_search_manager
86
-
115
+
87
116
  if len_database > 0:
88
- 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)
89
118
  process_search_result(select_title, selections)
90
119
 
91
120
  else:
92
- # If no results are found, ask again
93
- console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
94
- 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
@@ -49,13 +49,15 @@ def download_film(select_title: MediaItem) -> str:
49
49
  mp4_path = os.path.join(site_constant.MOVIE_FOLDER, title_name.replace(".mp4", ""))
50
50
 
51
51
  # Download the film using the m3u8 playlist, and output filename
52
- r_proc = HLS_Downloader(
52
+ hls_process = HLS_Downloader(
53
53
  m3u8_url=master_playlist,
54
54
  output_path=os.path.join(mp4_path, title_name)
55
55
  ).start()
56
56
 
57
- if r_proc['error'] is not None:
58
- try: os.remove(r_proc['path'])
59
- except: pass
57
+ if hls_process['error'] is not None:
58
+ try:
59
+ os.remove(hls_process['path'])
60
+ except Exception:
61
+ pass
60
62
 
61
- return r_proc['path']
63
+ return hls_process['path']
@@ -53,7 +53,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
53
53
 
54
54
  # Get episode information
55
55
  obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
56
- 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")
56
+ console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{scrape_serie.series_name}[/cyan] \\ [bold magenta]{obj_episode.name}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
57
57
 
58
58
  # Define filename and path for the downloaded video
59
59
  mp4_name = f"{map_episode_title(scrape_serie.series_name, index_season_selected, index_episode_selected, obj_episode.name)}.mp4"
@@ -64,16 +64,18 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
64
64
  master_playlist = video_source.get_m3u8_url(obj_episode.url)
65
65
 
66
66
  # Download the episode
67
- r_proc = HLS_Downloader(
67
+ hls_process = HLS_Downloader(
68
68
  m3u8_url=master_playlist,
69
69
  output_path=os.path.join(mp4_path, mp4_name)
70
70
  ).start()
71
71
 
72
- if r_proc['error'] is not None:
73
- try: os.remove(r_proc['path'])
74
- except: pass
72
+ if hls_process['error'] is not None:
73
+ try:
74
+ os.remove(hls_process['path'])
75
+ except Exception:
76
+ pass
75
77
 
76
- return r_proc['path'], r_proc['stopped']
78
+ return hls_process['path'], hls_process['stopped']
77
79
 
78
80
 
79
81
  def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, download_all: bool = False, episode_selection: str = None) -> None:
@@ -86,7 +86,9 @@ def title_search(query: str) -> int:
86
86
  soup = BeautifulSoup(response.text, 'html.parser')
87
87
 
88
88
  except Exception as e:
89
- if "WinError" in str(e) or "Errno" in str(e): console.print("\n[bold yellow]Please make sure you have enabled and configured a valid proxy.[/bold yellow]")
89
+ if "WinError" in str(e) or "Errno" in str(e):
90
+ console.print("\n[bold yellow]Please make sure you have enabled and configured a valid proxy.[/bold yellow]")
91
+
90
92
  console.print(f"[red]Site: {site_constant.SITE_NAME}, request search error: {e}")
91
93
  return 0
92
94
 
@@ -5,11 +5,12 @@ import logging
5
5
 
6
6
 
7
7
  # External libraries
8
- import httpx
9
8
  from bs4 import BeautifulSoup
10
9
 
10
+
11
11
  # Internal utilities
12
12
  from StreamingCommunity.Util.headers import get_userAgent
13
+ from StreamingCommunity.Util.http_client import create_client
13
14
  from StreamingCommunity.Util.config_json import config_manager
14
15
  from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager, Episode
15
16
 
@@ -24,8 +25,7 @@ class GetSerieInfo:
24
25
  self.url = url
25
26
  self.seasons_manager = SeasonManager()
26
27
  self.series_name = None
27
-
28
- self.client = httpx.Client(headers=self.headers, timeout=max_timeout)
28
+ self.client = create_client(headers=self.headers)
29
29
 
30
30
  def collect_info_season(self) -> None:
31
31
  """
@@ -7,4 +7,13 @@ from .manage_ep import (
7
7
  validate_selection,
8
8
  dynamic_format_number,
9
9
  display_episodes_list
10
- )
10
+ )
11
+
12
+ __all__ = [
13
+ "manage_selection",
14
+ "map_episode_title",
15
+ "validate_episode_selection",
16
+ "validate_selection",
17
+ "dynamic_format_number",
18
+ "display_episodes_list"
19
+ ]
@@ -125,20 +125,20 @@ def map_episode_title(tv_name: str, number_season: int, episode_number: int, epi
125
125
  """
126
126
  map_episode_temp = MAP_EPISODE
127
127
 
128
- if tv_name != None:
128
+ if tv_name is not None:
129
129
  map_episode_temp = map_episode_temp.replace("%(tv_name)", os_manager.get_sanitize_file(tv_name))
130
130
 
131
- if number_season != None:
131
+ if number_season is not None:
132
132
  map_episode_temp = map_episode_temp.replace("%(season)", str(number_season))
133
133
  else:
134
134
  map_episode_temp = map_episode_temp.replace("%(season)", dynamic_format_number(str(0)))
135
135
 
136
- if episode_number != None:
136
+ if episode_number is not None:
137
137
  map_episode_temp = map_episode_temp.replace("%(episode)", dynamic_format_number(str(episode_number)))
138
138
  else:
139
139
  map_episode_temp = map_episode_temp.replace("%(episode)", dynamic_format_number(str(0)))
140
140
 
141
- if episode_name != None:
141
+ if episode_name is not None:
142
142
  map_episode_temp = map_episode_temp.replace("%(episode_name)", os_manager.get_sanitize_file(episode_name))
143
143
 
144
144
  logging.info(f"Map episode string return: {map_episode_temp}")
@@ -1,3 +1,7 @@
1
1
  # 19.06.24
2
2
 
3
- from .site import get_select_title
3
+ from .site import get_select_title
4
+
5
+ __all__ = [
6
+ "get_select_title"
7
+ ]
@@ -1,8 +1,5 @@
1
1
  # 19.06.24
2
2
 
3
- import sys
4
-
5
-
6
3
  # External library
7
4
  from rich.console import Console
8
5
 
@@ -83,12 +80,19 @@ def get_select_title(table_show_manager, media_search_manager, num_results_avail
83
80
 
84
81
  color_index = 1
85
82
  for key in first_media_item.__dict__.keys():
83
+
86
84
  if key.capitalize() in column_to_hide:
87
85
  continue
86
+
88
87
  if key in ('id', 'type', 'name', 'score'):
89
- if key == 'type': column_info["Type"] = {'color': 'yellow'}
90
- elif key == 'name': column_info["Name"] = {'color': 'magenta'}
91
- elif key == 'score': column_info["Score"] = {'color': 'cyan'}
88
+ if key == 'type':
89
+ column_info["Type"] = {'color': 'yellow'}
90
+
91
+ elif key == 'name':
92
+ column_info["Name"] = {'color': 'magenta'}
93
+ elif key == 'score':
94
+ column_info["Score"] = {'color': 'cyan'}
95
+
92
96
  else:
93
97
  column_info[key.capitalize()] = {'color': available_colors[color_index % len(available_colors)]}
94
98
  color_index += 1