StreamingCommunity 3.2.0__tar.gz → 3.2.5__tar.gz

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 (121) hide show
  1. {streamingcommunity-3.2.0/StreamingCommunity.egg-info → streamingcommunity-3.2.5}/PKG-INFO +4 -2
  2. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/README.md +2 -0
  3. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Player/Helper/Vixcloud/util.py +4 -0
  4. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Player/hdplayer.py +2 -2
  5. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Player/mixdrop.py +1 -1
  6. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Player/vixcloud.py +4 -5
  7. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/animeunity/__init__.py +2 -2
  8. streamingcommunity-3.2.5/StreamingCommunity/Api/Site/crunchyroll/__init__.py +103 -0
  9. streamingcommunity-3.2.5/StreamingCommunity/Api/Site/crunchyroll/film.py +83 -0
  10. streamingcommunity-3.2.5/StreamingCommunity/Api/Site/crunchyroll/series.py +182 -0
  11. streamingcommunity-3.2.5/StreamingCommunity/Api/Site/crunchyroll/site.py +113 -0
  12. streamingcommunity-3.2.5/StreamingCommunity/Api/Site/crunchyroll/util/ScrapeSerie.py +218 -0
  13. streamingcommunity-3.2.5/StreamingCommunity/Api/Site/crunchyroll/util/get_license.py +227 -0
  14. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/guardaserie/site.py +1 -2
  15. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +9 -8
  16. streamingcommunity-3.2.5/StreamingCommunity/Api/Site/mediasetinfinity/__init__.py +96 -0
  17. streamingcommunity-3.2.5/StreamingCommunity/Api/Site/mediasetinfinity/film.py +76 -0
  18. streamingcommunity-3.2.5/StreamingCommunity/Api/Site/mediasetinfinity/series.py +177 -0
  19. streamingcommunity-3.2.5/StreamingCommunity/Api/Site/mediasetinfinity/site.py +112 -0
  20. streamingcommunity-3.2.5/StreamingCommunity/Api/Site/mediasetinfinity/util/ScrapeSerie.py +259 -0
  21. streamingcommunity-3.2.5/StreamingCommunity/Api/Site/mediasetinfinity/util/fix_mpd.py +64 -0
  22. streamingcommunity-3.2.5/StreamingCommunity/Api/Site/mediasetinfinity/util/get_license.py +217 -0
  23. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/streamingcommunity/__init__.py +6 -17
  24. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/streamingcommunity/film.py +2 -2
  25. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/streamingcommunity/series.py +9 -9
  26. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/streamingcommunity/site.py +3 -4
  27. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +3 -6
  28. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/streamingwatch/__init__.py +6 -14
  29. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/streamingwatch/film.py +2 -2
  30. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/streamingwatch/series.py +9 -9
  31. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/streamingwatch/site.py +5 -7
  32. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/streamingwatch/util/ScrapeSerie.py +2 -2
  33. streamingcommunity-3.2.5/StreamingCommunity/Lib/Downloader/DASH/cdm_helpher.py +131 -0
  34. streamingcommunity-3.2.5/StreamingCommunity/Lib/Downloader/DASH/decrypt.py +79 -0
  35. streamingcommunity-3.2.5/StreamingCommunity/Lib/Downloader/DASH/downloader.py +220 -0
  36. streamingcommunity-3.2.5/StreamingCommunity/Lib/Downloader/DASH/parser.py +249 -0
  37. streamingcommunity-3.2.5/StreamingCommunity/Lib/Downloader/DASH/segments.py +332 -0
  38. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/Downloader/HLS/downloader.py +1 -14
  39. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/Downloader/HLS/segments.py +3 -3
  40. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/Downloader/MP4/downloader.py +0 -5
  41. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/FFmpeg/capture.py +3 -3
  42. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/FFmpeg/command.py +1 -1
  43. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/TelegramHelp/config.json +3 -5
  44. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Upload/version.py +2 -2
  45. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Util/os.py +21 -0
  46. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/run.py +1 -1
  47. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5/StreamingCommunity.egg-info}/PKG-INFO +4 -2
  48. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity.egg-info/SOURCES.txt +18 -4
  49. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity.egg-info/requires.txt +1 -1
  50. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/requirements.txt +1 -1
  51. streamingcommunity-3.2.0/StreamingCommunity/Api/Site/1337xx/__init__.py +0 -72
  52. streamingcommunity-3.2.0/StreamingCommunity/Api/Site/1337xx/site.py +0 -82
  53. streamingcommunity-3.2.0/StreamingCommunity/Api/Site/1337xx/title.py +0 -61
  54. streamingcommunity-3.2.0/StreamingCommunity/Lib/Proxies/proxy.py +0 -72
  55. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/LICENSE +0 -0
  56. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/MANIFEST.in +0 -0
  57. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Player/Helper/Vixcloud/js_parser.py +0 -0
  58. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Player/ddl.py +0 -0
  59. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Player/maxstream.py +0 -0
  60. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Player/mediapolisvod.py +0 -0
  61. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Player/supervideo.py +0 -0
  62. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Player/sweetpixel.py +0 -0
  63. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/altadefinizione/__init__.py +0 -0
  64. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/altadefinizione/film.py +0 -0
  65. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/altadefinizione/series.py +0 -0
  66. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/altadefinizione/site.py +0 -0
  67. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/altadefinizione/util/ScrapeSerie.py +0 -0
  68. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/animeunity/film.py +0 -0
  69. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/animeunity/serie.py +0 -0
  70. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/animeunity/site.py +0 -0
  71. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py +0 -0
  72. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/animeworld/__init__.py +0 -0
  73. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/animeworld/film.py +0 -0
  74. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/animeworld/serie.py +0 -0
  75. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/animeworld/site.py +0 -0
  76. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/animeworld/util/ScrapeSerie.py +0 -0
  77. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/cb01new/__init__.py +0 -0
  78. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/cb01new/film.py +0 -0
  79. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/cb01new/site.py +0 -0
  80. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/guardaserie/__init__.py +0 -0
  81. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/guardaserie/series.py +0 -0
  82. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/raiplay/__init__.py +0 -0
  83. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/raiplay/film.py +0 -0
  84. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/raiplay/series.py +0 -0
  85. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/raiplay/site.py +0 -0
  86. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py +0 -0
  87. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Template/Class/SearchType.py +0 -0
  88. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Template/Util/__init__.py +0 -0
  89. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Template/Util/manage_ep.py +0 -0
  90. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Template/__init__.py +0 -0
  91. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Template/config_loader.py +0 -0
  92. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Api/Template/site.py +0 -0
  93. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/Downloader/TOR/downloader.py +0 -0
  94. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/Downloader/__init__.py +0 -0
  95. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/FFmpeg/__init__.py +0 -0
  96. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/FFmpeg/util.py +0 -0
  97. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/M3U8/__init__.py +0 -0
  98. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/M3U8/decryptor.py +0 -0
  99. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/M3U8/estimator.py +0 -0
  100. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/M3U8/parser.py +0 -0
  101. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/M3U8/url_fixer.py +0 -0
  102. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/TMBD/__init__.py +0 -0
  103. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/TMBD/obj_tmbd.py +0 -0
  104. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Lib/TMBD/tmdb.py +0 -0
  105. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/TelegramHelp/__init__.py +0 -0
  106. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/TelegramHelp/telegram_bot.py +0 -0
  107. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Upload/update.py +0 -0
  108. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Util/color.py +0 -0
  109. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Util/config_json.py +0 -0
  110. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Util/ffmpeg_installer.py +0 -0
  111. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Util/headers.py +0 -0
  112. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Util/logger.py +0 -0
  113. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Util/message.py +0 -0
  114. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/Util/table.py +0 -0
  115. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/__init__.py +0 -0
  116. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity/global_search.py +0 -0
  117. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity.egg-info/dependency_links.txt +0 -0
  118. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity.egg-info/entry_points.txt +0 -0
  119. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/StreamingCommunity.egg-info/top_level.txt +0 -0
  120. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/setup.cfg +0 -0
  121. {streamingcommunity-3.2.0 → streamingcommunity-3.2.5}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: StreamingCommunity
3
- Version: 3.2.0
3
+ Version: 3.2.5
4
4
  Home-page: https://github.com/Lovi-0/StreamingCommunity
5
5
  Author: Lovi-0
6
6
  Project-URL: Bug Reports, https://github.com/Lovi-0/StreamingCommunity/issues
@@ -25,7 +25,7 @@ Requires-Dist: pycryptodomex
25
25
  Requires-Dist: ua-generator
26
26
  Requires-Dist: qbittorrent-api
27
27
  Requires-Dist: pyTelegramBotAPI
28
- Requires-Dist: beautifulsoup4
28
+ Requires-Dist: pywidevine
29
29
  Dynamic: author
30
30
  Dynamic: description
31
31
  Dynamic: description-content-type
@@ -839,3 +839,5 @@ API non ufficiale per accedere ai contenuti del sito italiano StreamingCommunity
839
839
  # Disclaimer
840
840
 
841
841
  This software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software.
842
+
843
+ > **Note:** DASH downloads require a valid L3 CDM (Content Decryption Module) to proceed. This project does not provide, include, or facilitate obtaining any CDM. Users are responsible for ensuring compliance with all applicable laws and requirements regarding DRM and decryption modules.
@@ -801,3 +801,5 @@ API non ufficiale per accedere ai contenuti del sito italiano StreamingCommunity
801
801
  # Disclaimer
802
802
 
803
803
  This software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software.
804
+
805
+ > **Note:** DASH downloads require a valid L3 CDM (Content Decryption Module) to proceed. This project does not provide, include, or facilitate obtaining any CDM. Users are responsible for ensuring compliance with all applicable laws and requirements regarding DRM and decryption modules.
@@ -90,9 +90,13 @@ class SeasonManager:
90
90
  Parameters:
91
91
  - number (int): The season number (1-based index)
92
92
  """
93
+ if len(self.seasons) == 1:
94
+ return self.seasons[0]
95
+
93
96
  for season in self.seasons:
94
97
  if season.number == number:
95
98
  return season
99
+
96
100
  return None
97
101
 
98
102
  def __len__(self) -> int:
@@ -18,8 +18,8 @@ REQUEST_VERIFY = config_manager.get_bool('REQUESTS', 'verify')
18
18
 
19
19
 
20
20
  class VideoSource:
21
- def __init__(self, proxy=None):
22
- self.client = httpx.Client(headers={'user-agent': get_userAgent()}, timeout=MAX_TIMEOUT, proxy=proxy, verify=REQUEST_VERIFY)
21
+ def __init__(self):
22
+ self.client = httpx.Client(headers={'user-agent': get_userAgent()}, timeout=MAX_TIMEOUT, verify=REQUEST_VERIFY)
23
23
 
24
24
  def extractLinkHdPlayer(self, response):
25
25
  """Extract iframe source from the page."""
@@ -25,7 +25,7 @@ class VideoSource:
25
25
 
26
26
  def __init__(self, url: str):
27
27
  self.url = url
28
- self.redirect_url: str | None = None
28
+ self.redirect_url: str = None
29
29
  self._init_headers()
30
30
 
31
31
  def _init_headers(self) -> None:
@@ -25,7 +25,7 @@ console = Console()
25
25
 
26
26
 
27
27
  class VideoSource:
28
- def __init__(self, url: str, is_series: bool, media_id: int = None, proxy: str = None):
28
+ def __init__(self, url: str, is_series: bool, media_id: int = None):
29
29
  """
30
30
  Initialize video source for streaming site.
31
31
 
@@ -36,7 +36,6 @@ class VideoSource:
36
36
  """
37
37
  self.headers = {'user-agent': get_userAgent()}
38
38
  self.url = url
39
- self.proxy = proxy
40
39
  self.is_series = is_series
41
40
  self.media_id = media_id
42
41
  self.iframe_src = None
@@ -58,7 +57,7 @@ class VideoSource:
58
57
  }
59
58
 
60
59
  try:
61
- response = httpx.get(f"{self.url}/iframe/{self.media_id}", headers=self.headers, params=params, timeout=MAX_TIMEOUT, proxy=self.proxy, verify=REQUEST_VERIFY)
60
+ response = httpx.get(f"{self.url}/iframe/{self.media_id}", headers=self.headers, params=params, timeout=MAX_TIMEOUT, verify=REQUEST_VERIFY)
62
61
  response.raise_for_status()
63
62
 
64
63
  # Parse response with BeautifulSoup to get iframe source
@@ -123,12 +122,12 @@ class VideoSource:
123
122
  logging.error(f"Error getting content: {e}")
124
123
  raise
125
124
 
126
- def get_playlist(self) -> str | None:
125
+ def get_playlist(self) -> str:
127
126
  """
128
127
  Generate authenticated playlist URL.
129
128
 
130
129
  Returns:
131
- str | None: Fully constructed playlist URL with authentication parameters, or None if content unavailable
130
+ str: Fully constructed playlist URL with authentication parameters, or None if content unavailable
132
131
  """
133
132
  if not self.window_parameter:
134
133
  return None
@@ -66,7 +66,7 @@ def process_search_result(select_title, selections=None):
66
66
  selections (dict, optional): Dictionary containing selection inputs that bypass manual input
67
67
  {'season': season_selection, 'episode': episode_selection}
68
68
  """
69
- if select_title.type == 'Movie' or select_title.type == 'OVA':
69
+ if select_title.type == 'Movie':
70
70
  download_film(select_title)
71
71
 
72
72
  else:
@@ -120,4 +120,4 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
120
120
 
121
121
  # If no results are found, ask again
122
122
  string_to_search = get_user_input()
123
- search(string_to_search, get_onlyDatabase, None, selections)
123
+ search(string_to_search, get_onlyDatabase, None, selections)
@@ -0,0 +1,103 @@
1
+ # 16.03.25
2
+
3
+ import sys
4
+ import subprocess
5
+ from urllib.parse import quote_plus
6
+
7
+
8
+ # External library
9
+ from rich.console import Console
10
+ from rich.prompt import Prompt
11
+
12
+
13
+ # Internal utilities
14
+ from StreamingCommunity.Api.Template import get_select_title
15
+ from StreamingCommunity.Api.Template.config_loader import site_constant
16
+ from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
17
+
18
+
19
+ # Logic class
20
+ from .site import title_search, table_show_manager, media_search_manager
21
+ from .film import download_film
22
+ from .series import download_series
23
+
24
+ # Variable
25
+ indice = 8
26
+ _useFor = "Anime"
27
+ _priority = 0
28
+ _engineDownload = "hls"
29
+ _deprecate = False
30
+
31
+ msg = Prompt()
32
+ console = Console()
33
+
34
+
35
+ def get_user_input(string_to_search: str = None):
36
+ """
37
+ Asks the user to input a search term.
38
+ Handles both Telegram bot input and direct input.
39
+ """
40
+ if string_to_search is None:
41
+ string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
42
+
43
+ return string_to_search
44
+
45
+ def process_search_result(select_title, selections=None):
46
+ """
47
+ Handles the search result and initiates the download for either a film or series.
48
+
49
+ Parameters:
50
+ select_title (MediaItem): The selected media item
51
+ selections (dict, optional): Dictionary containing selection inputs that bypass manual input
52
+ {'season': season_selection, 'episode': episode_selection}
53
+ """
54
+ if select_title.type == 'tv':
55
+ season_selection = None
56
+ episode_selection = None
57
+
58
+ if selections:
59
+ season_selection = selections.get('season')
60
+ episode_selection = selections.get('episode')
61
+
62
+ download_series(select_title, season_selection, episode_selection)
63
+
64
+ else:
65
+ download_film(select_title)
66
+
67
+ # search("Game of Thrones", selections={"season": "1", "episode": "1-3"})
68
+ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
69
+ """
70
+ Main function of the application for search.
71
+
72
+ Parameters:
73
+ string_to_search (str, optional): String to search for
74
+ get_onylDatabase (bool, optional): If True, return only the database object
75
+ direct_item (dict, optional): Direct item to process (bypass search)
76
+ selections (dict, optional): Dictionary containing selection inputs that bypass manual input
77
+ {'season': season_selection, 'episode': episode_selection}
78
+ """
79
+ if direct_item:
80
+ select_title = MediaItem(**direct_item)
81
+ process_search_result(select_title, selections)
82
+ 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))
89
+
90
+ # If only the database is needed, return the manager
91
+ if get_onlyDatabase:
92
+ return media_search_manager
93
+
94
+ if len_database > 0:
95
+ select_title = get_select_title(table_show_manager, media_search_manager, len_database)
96
+ process_search_result(select_title, selections)
97
+
98
+ else:
99
+ console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
100
+
101
+ # If no results are found, ask again
102
+ string_to_search = get_user_input()
103
+ search(string_to_search, get_onlyDatabase, None, selections)
@@ -0,0 +1,83 @@
1
+ # 16.03.25
2
+
3
+ import os
4
+ from urllib.parse import urlparse, parse_qs
5
+
6
+
7
+ # External library
8
+ from rich.console import Console
9
+
10
+
11
+ # Internal utilities
12
+ from StreamingCommunity.Util.os import os_manager
13
+ from StreamingCommunity.Util.message import start_message
14
+ from StreamingCommunity.Util.config_json import config_manager
15
+ from StreamingCommunity.Util.os import get_wvd_path
16
+ from StreamingCommunity.Lib.Downloader.DASH.downloader import DASH_Download
17
+
18
+
19
+ # Logic class
20
+ from StreamingCommunity.Api.Template.config_loader import site_constant
21
+ from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
22
+
23
+
24
+ # Player
25
+ from .util.get_license import get_playback_session, get_auth_token, generate_device_id
26
+
27
+
28
+ # Variable
29
+ console = Console()
30
+ max_timeout = config_manager.get_int("REQUESTS", "timeout")
31
+
32
+
33
+ def download_film(select_title: MediaItem) -> str:
34
+ """
35
+ Downloads a film using the provided film ID, title name, and domain.
36
+
37
+ Parameters:
38
+ - select_title (MediaItem): The selected media item.
39
+
40
+ Return:
41
+ - str: output path if successful, otherwise None
42
+ """
43
+ start_message()
44
+ console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{select_title.name}[/cyan] \n")
45
+
46
+ # Define filename and path for the downloaded video
47
+ mp4_name = os_manager.get_sanitize_file(select_title.name) + ".mp4"
48
+ mp4_path = os.path.join(site_constant.MOVIE_FOLDER, mp4_name.replace(".mp4", ""))
49
+
50
+ # Generate mpd and license URLs
51
+ url_id = select_title.get('url').split('/')[-1]
52
+ device_id = generate_device_id()
53
+ mpd_url, mpd_headers = get_playback_session(get_auth_token(device_id), device_id, url_id)
54
+ parsed_url = urlparse(mpd_url)
55
+ query_params = parse_qs(parsed_url.query)
56
+
57
+ # Download the episode
58
+ r_proc = DASH_Download(
59
+ cdm_device=get_wvd_path(),
60
+ license_url='https://www.crunchyroll.com/license/v1/license/widevine',
61
+ mpd_url=mpd_url,
62
+ output_path=os.path.join(mp4_path, mp4_name),
63
+ )
64
+ r_proc.parse_manifest(custom_headers=mpd_headers)
65
+
66
+ # Create headers for license request
67
+ license_headers = mpd_headers.copy()
68
+ license_headers.update({
69
+ "x-cr-content-id": url_id,
70
+ "x-cr-video-token": query_params['playbackGuid'][0],
71
+ })
72
+
73
+ if r_proc.download_and_decrypt(custom_headers=license_headers):
74
+ r_proc.finalize_output()
75
+
76
+ # Get final output path and status
77
+ status = r_proc.get_status()
78
+
79
+ if status['error'] is not None and status['path']:
80
+ try: os.remove(status['path'])
81
+ except Exception: pass
82
+
83
+ return status['path'], status['stopped']
@@ -0,0 +1,182 @@
1
+ # 16.03.25
2
+
3
+ import os
4
+ from typing import Tuple
5
+ from urllib.parse import urlparse, parse_qs
6
+
7
+
8
+ # External library
9
+ from rich.console import Console
10
+ from rich.prompt import Prompt
11
+
12
+
13
+ # Internal utilities
14
+ from StreamingCommunity.Util.message import start_message
15
+ from StreamingCommunity.Util.os import os_manager
16
+ from StreamingCommunity.Util.os import get_wvd_path
17
+ from StreamingCommunity.Lib.Downloader.DASH.downloader import DASH_Download
18
+
19
+
20
+ # Logic class
21
+ from .util.ScrapeSerie import GetSerieInfo
22
+ from StreamingCommunity.Api.Template.Util import (
23
+ manage_selection,
24
+ map_episode_title,
25
+ validate_selection,
26
+ validate_episode_selection,
27
+ display_episodes_list
28
+ )
29
+ from StreamingCommunity.Api.Template.config_loader import site_constant
30
+ from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
31
+
32
+
33
+ # Player
34
+ from .util.get_license import get_playback_session, get_auth_token, generate_device_id
35
+
36
+
37
+ # Variable
38
+ msg = Prompt()
39
+ console = Console()
40
+
41
+
42
+ def download_video(index_season_selected: int, index_episode_selected: int, scrape_serie: GetSerieInfo) -> Tuple[str,bool]:
43
+ """
44
+ Downloads a specific episode from a specified season.
45
+
46
+ Parameters:
47
+ - index_season_selected (int): Season number
48
+ - index_episode_selected (int): Episode index
49
+ - scrape_serie (GetSerieInfo): Scraper object with series information
50
+
51
+ Returns:
52
+ - str: Path to downloaded file
53
+ - bool: Whether download was stopped
54
+ """
55
+ start_message()
56
+
57
+ # Get episode information
58
+ obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
59
+ console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [bold magenta]{obj_episode.get('name')}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
60
+
61
+ # Define filename and path for the downloaded video
62
+ mp4_name = f"{map_episode_title(scrape_serie.series_name, index_season_selected, index_episode_selected, obj_episode.get('name'))}.mp4"
63
+ mp4_path = os_manager.get_sanitize_path(os.path.join(site_constant.SERIES_FOLDER, scrape_serie.series_name, f"S{index_season_selected}"))
64
+
65
+ # Generate mpd and license URLs
66
+ url_id = obj_episode.get('url').split('/')[-1]
67
+ device_id = generate_device_id()
68
+ mpd_url, mpd_headers = get_playback_session(get_auth_token(device_id), device_id, url_id)
69
+ parsed_url = urlparse(mpd_url)
70
+ query_params = parse_qs(parsed_url.query)
71
+
72
+ # Download the episode
73
+ r_proc = DASH_Download(
74
+ cdm_device=get_wvd_path(),
75
+ license_url='https://www.crunchyroll.com/license/v1/license/widevine',
76
+ mpd_url=mpd_url,
77
+ output_path=os.path.join(mp4_path, mp4_name),
78
+ )
79
+ r_proc.parse_manifest(custom_headers=mpd_headers)
80
+
81
+ # Create headers for license request
82
+ license_headers = mpd_headers.copy()
83
+ license_headers.update({
84
+ "x-cr-content-id": url_id,
85
+ "x-cr-video-token": query_params['playbackGuid'][0],
86
+ })
87
+
88
+ if r_proc.download_and_decrypt(custom_headers=license_headers):
89
+ r_proc.finalize_output()
90
+
91
+ # Get final output path and status
92
+ status = r_proc.get_status()
93
+
94
+ if status['error'] is not None and status['path']:
95
+ try: os.remove(status['path'])
96
+ except Exception: pass
97
+
98
+ return status['path'], status['stopped']
99
+
100
+ def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, download_all: bool = False, episode_selection: str = None) -> None:
101
+ """
102
+ Handle downloading episodes for a specific season.
103
+
104
+ Parameters:
105
+ - index_season_selected (int): Season number
106
+ - scrape_serie (GetSerieInfo): Scraper object with series information
107
+ - download_all (bool): Whether to download all episodes
108
+ - episode_selection (str, optional): Pre-defined episode selection that bypasses manual input
109
+ """
110
+ # Get episodes for the selected season
111
+ episodes = scrape_serie.getEpisodeSeasons(index_season_selected)
112
+ episodes_count = len(episodes)
113
+
114
+ if download_all:
115
+ for i_episode in range(1, episodes_count + 1):
116
+ path, stopped = download_video(index_season_selected, i_episode, scrape_serie)
117
+
118
+ if stopped:
119
+ break
120
+
121
+ console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.")
122
+
123
+ else:
124
+ if episode_selection is not None:
125
+ last_command = episode_selection
126
+ console.print(f"\n[cyan]Using provided episode selection: [yellow]{episode_selection}")
127
+
128
+ else:
129
+ last_command = display_episodes_list(episodes)
130
+
131
+ # Prompt user for episode selection
132
+ list_episode_select = manage_selection(last_command, episodes_count)
133
+ list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
134
+
135
+ # Download selected episodes if not stopped
136
+ for i_episode in list_episode_select:
137
+ path, stopped = download_video(index_season_selected, i_episode, scrape_serie)
138
+
139
+ if stopped:
140
+ break
141
+
142
+ def download_series(select_season: MediaItem, season_selection: str = None, episode_selection: str = None) -> None:
143
+ """
144
+ Handle downloading a complete series.
145
+
146
+ Parameters:
147
+ - select_season (MediaItem): Series metadata from search
148
+ - season_selection (str, optional): Pre-defined season selection that bypasses manual input
149
+ - episode_selection (str, optional): Pre-defined episode selection that bypasses manual input
150
+ """
151
+ scrape_serie = GetSerieInfo(select_season.url.split("/")[-1])
152
+
153
+ # Get total number of seasons
154
+ seasons_count = scrape_serie.getNumberSeason()
155
+
156
+ # Prompt user for season selection and download episodes
157
+ console.print(f"\n[green]Seasons found: [red]{seasons_count}")
158
+
159
+ # If season_selection is provided, use it instead of asking for input
160
+ if season_selection is None:
161
+ index_season_selected = msg.ask(
162
+ "\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
163
+ "[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"
164
+ )
165
+
166
+ else:
167
+ index_season_selected = season_selection
168
+ console.print(f"\n[cyan]Using provided season selection: [yellow]{season_selection}")
169
+
170
+ # Validate the selection
171
+ list_season_select = manage_selection(index_season_selected, seasons_count)
172
+ list_season_select = validate_selection(list_season_select, seasons_count)
173
+
174
+ # Loop through the selected seasons and download episodes
175
+ for i_season in list_season_select:
176
+ if len(list_season_select) > 1 or index_season_selected == "*":
177
+ # Download all episodes if multiple seasons are selected or if '*' is used
178
+ download_episode(i_season, scrape_serie, download_all=True)
179
+
180
+ else:
181
+ # Otherwise, let the user select specific episodes for the single season
182
+ download_episode(i_season, scrape_serie, download_all=False, episode_selection=episode_selection)
@@ -0,0 +1,113 @@
1
+ # 16.03.25
2
+
3
+
4
+ # External libraries
5
+ from curl_cffi import requests
6
+ from rich.console import Console
7
+
8
+
9
+ # Internal utilities
10
+ from StreamingCommunity.Util.config_json import config_manager
11
+ from StreamingCommunity.Util.headers import get_headers
12
+ from StreamingCommunity.Util.table import TVShowManager
13
+
14
+
15
+ # Logic class
16
+ from StreamingCommunity.Api.Template.config_loader import site_constant
17
+ from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
18
+ from .util.get_license import get_auth_token, generate_device_id
19
+
20
+
21
+ # Variable
22
+ console = Console()
23
+ media_search_manager = MediaManager()
24
+ table_show_manager = TVShowManager()
25
+ max_timeout = config_manager.get_int("REQUESTS", "timeout")
26
+
27
+
28
+ def title_search(query: str) -> int:
29
+ """
30
+ Search for titles based on a search query.
31
+
32
+ Parameters:
33
+ - query (str): The query to search for.
34
+
35
+ Returns:
36
+ int: The number of titles found.
37
+ """
38
+ media_search_manager.clear()
39
+ table_show_manager.clear()
40
+
41
+ # Build new Crunchyroll API search URL
42
+ api_url = f"https://www.crunchyroll.com/content/v2/discover/search"
43
+
44
+ params = {
45
+ "q": query,
46
+ "n": 20,
47
+ "type": "series,movie_listing",
48
+ "ratings": "true",
49
+ "preferred_audio_language": "it-IT",
50
+ "locale": "it-IT"
51
+ }
52
+
53
+ headers = get_headers()
54
+ headers['authorization'] = f"Bearer {get_auth_token(generate_device_id()).access_token}"
55
+
56
+ console.print(f"[cyan]Search url: [yellow]{api_url}")
57
+
58
+ try:
59
+ response = requests.get(
60
+ api_url,
61
+ params=params,
62
+ headers=headers,
63
+ timeout=max_timeout,
64
+ impersonate="chrome110"
65
+ )
66
+ response.raise_for_status()
67
+
68
+ except Exception as e:
69
+ console.print(f"[red]Site: {site_constant.SITE_NAME}, request search error: {e}")
70
+ return 0
71
+
72
+ data = response.json()
73
+ found = 0
74
+
75
+ # Parse results
76
+ for block in data.get("data", []):
77
+ if block.get("type") not in ("series", "movie_listing", "top_results"):
78
+ continue
79
+
80
+ for item in block.get("items", []):
81
+ tipo = None
82
+
83
+ if item.get("type") == "movie_listing":
84
+ tipo = "film"
85
+ elif item.get("type") == "series":
86
+ meta = item.get("series_metadata", {})
87
+
88
+ if meta.get("episode_count") == 1 and meta.get("season_count", 1) == 1 and meta.get("series_launch_year"):
89
+ tipo = "film" if "film" in item.get("description", "").lower() or "movie" in item.get("description", "").lower() else "tv"
90
+ else:
91
+ tipo = "tv"
92
+
93
+ else:
94
+ continue
95
+
96
+ url = ""
97
+ if tipo == "tv":
98
+ url = f"https://www.crunchyroll.com/series/{item.get('id')}"
99
+ elif tipo == "film":
100
+ url = f"https://www.crunchyroll.com/series/{item.get('id')}"
101
+ else:
102
+ continue
103
+
104
+ title = item.get("title", "")
105
+
106
+ media_search_manager.add_media({
107
+ 'url': url,
108
+ 'name': title,
109
+ 'type': tipo
110
+ })
111
+ found += 1
112
+
113
+ return media_search_manager.get_length()