plexflow 0.0.64__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.
Files changed (256) hide show
  1. plexflow/__init__.py +0 -0
  2. plexflow/__main__.py +15 -0
  3. plexflow/core/.DS_Store +0 -0
  4. plexflow/core/__init__.py +0 -0
  5. plexflow/core/context/__init__.py +0 -0
  6. plexflow/core/context/metadata/__init__.py +0 -0
  7. plexflow/core/context/metadata/context.py +32 -0
  8. plexflow/core/context/metadata/tmdb/__init__.py +0 -0
  9. plexflow/core/context/metadata/tmdb/context.py +45 -0
  10. plexflow/core/context/partial_context.py +46 -0
  11. plexflow/core/context/partials/__init__.py +8 -0
  12. plexflow/core/context/partials/cache.py +16 -0
  13. plexflow/core/context/partials/context.py +12 -0
  14. plexflow/core/context/partials/ids.py +37 -0
  15. plexflow/core/context/partials/movie.py +115 -0
  16. plexflow/core/context/partials/tgx_batch.py +33 -0
  17. plexflow/core/context/partials/tgx_context.py +34 -0
  18. plexflow/core/context/partials/torrents.py +23 -0
  19. plexflow/core/context/partials/watchlist.py +35 -0
  20. plexflow/core/context/plexflow_context.py +29 -0
  21. plexflow/core/context/plexflow_property.py +36 -0
  22. plexflow/core/context/root/__init__.py +0 -0
  23. plexflow/core/context/root/context.py +25 -0
  24. plexflow/core/context/select/__init__.py +0 -0
  25. plexflow/core/context/select/context.py +45 -0
  26. plexflow/core/context/torrent/__init__.py +0 -0
  27. plexflow/core/context/torrent/context.py +43 -0
  28. plexflow/core/context/torrent/tpb/__init__.py +0 -0
  29. plexflow/core/context/torrent/tpb/context.py +45 -0
  30. plexflow/core/context/torrent/yts/__init__.py +0 -0
  31. plexflow/core/context/torrent/yts/context.py +45 -0
  32. plexflow/core/context/watchlist/__init__.py +0 -0
  33. plexflow/core/context/watchlist/context.py +46 -0
  34. plexflow/core/downloads/__init__.py +0 -0
  35. plexflow/core/downloads/candidates/__init__.py +0 -0
  36. plexflow/core/downloads/candidates/download_candidate.py +210 -0
  37. plexflow/core/downloads/candidates/filtered.py +51 -0
  38. plexflow/core/downloads/candidates/utils.py +39 -0
  39. plexflow/core/env/__init__.py +0 -0
  40. plexflow/core/env/env.py +31 -0
  41. plexflow/core/genai/__init__.py +0 -0
  42. plexflow/core/genai/bot.py +9 -0
  43. plexflow/core/genai/plexa.py +54 -0
  44. plexflow/core/genai/torrent/imdb_verify.py +65 -0
  45. plexflow/core/genai/torrent/movie.py +25 -0
  46. plexflow/core/genai/utils/__init__.py +0 -0
  47. plexflow/core/genai/utils/loader.py +5 -0
  48. plexflow/core/metadata/__init__.py +0 -0
  49. plexflow/core/metadata/auto/__init__.py +0 -0
  50. plexflow/core/metadata/auto/auto_meta.py +40 -0
  51. plexflow/core/metadata/auto/auto_providers/__init__.py +0 -0
  52. plexflow/core/metadata/auto/auto_providers/auto/__init__.py +0 -0
  53. plexflow/core/metadata/auto/auto_providers/auto/episode.py +49 -0
  54. plexflow/core/metadata/auto/auto_providers/auto/item.py +55 -0
  55. plexflow/core/metadata/auto/auto_providers/auto/movie.py +13 -0
  56. plexflow/core/metadata/auto/auto_providers/auto/season.py +43 -0
  57. plexflow/core/metadata/auto/auto_providers/auto/show.py +26 -0
  58. plexflow/core/metadata/auto/auto_providers/imdb/__init__.py +0 -0
  59. plexflow/core/metadata/auto/auto_providers/imdb/movie.py +36 -0
  60. plexflow/core/metadata/auto/auto_providers/imdb/show.py +45 -0
  61. plexflow/core/metadata/auto/auto_providers/moviemeter/__init__.py +0 -0
  62. plexflow/core/metadata/auto/auto_providers/moviemeter/movie.py +40 -0
  63. plexflow/core/metadata/auto/auto_providers/plex/__init__.py +0 -0
  64. plexflow/core/metadata/auto/auto_providers/plex/movie.py +39 -0
  65. plexflow/core/metadata/auto/auto_providers/tmdb/__init__.py +0 -0
  66. plexflow/core/metadata/auto/auto_providers/tmdb/episode.py +30 -0
  67. plexflow/core/metadata/auto/auto_providers/tmdb/movie.py +36 -0
  68. plexflow/core/metadata/auto/auto_providers/tmdb/season.py +23 -0
  69. plexflow/core/metadata/auto/auto_providers/tmdb/show.py +41 -0
  70. plexflow/core/metadata/auto/auto_providers/tmdb.py +92 -0
  71. plexflow/core/metadata/auto/auto_providers/tvdb/__init__.py +0 -0
  72. plexflow/core/metadata/auto/auto_providers/tvdb/episode.py +28 -0
  73. plexflow/core/metadata/auto/auto_providers/tvdb/movie.py +36 -0
  74. plexflow/core/metadata/auto/auto_providers/tvdb/season.py +25 -0
  75. plexflow/core/metadata/auto/auto_providers/tvdb/show.py +41 -0
  76. plexflow/core/metadata/providers/__init__.py +0 -0
  77. plexflow/core/metadata/providers/imdb/__init__.py +0 -0
  78. plexflow/core/metadata/providers/imdb/datatypes.py +53 -0
  79. plexflow/core/metadata/providers/imdb/imdb.py +112 -0
  80. plexflow/core/metadata/providers/moviemeter/__init__.py +0 -0
  81. plexflow/core/metadata/providers/moviemeter/datatypes.py +111 -0
  82. plexflow/core/metadata/providers/moviemeter/moviemeter.py +42 -0
  83. plexflow/core/metadata/providers/plex/__init__.py +0 -0
  84. plexflow/core/metadata/providers/plex/datatypes.py +693 -0
  85. plexflow/core/metadata/providers/plex/plex.py +167 -0
  86. plexflow/core/metadata/providers/tmdb/__init__.py +0 -0
  87. plexflow/core/metadata/providers/tmdb/datatypes.py +460 -0
  88. plexflow/core/metadata/providers/tmdb/tmdb.py +85 -0
  89. plexflow/core/metadata/providers/tvdb/__init__.py +0 -0
  90. plexflow/core/metadata/providers/tvdb/datatypes.py +257 -0
  91. plexflow/core/metadata/providers/tvdb/tv_datatypes.py +554 -0
  92. plexflow/core/metadata/providers/tvdb/tvdb.py +65 -0
  93. plexflow/core/metadata/providers/universal/__init__.py +0 -0
  94. plexflow/core/metadata/providers/universal/movie.py +130 -0
  95. plexflow/core/metadata/providers/universal/old.py +192 -0
  96. plexflow/core/metadata/providers/universal/show.py +107 -0
  97. plexflow/core/plex/__init__.py +0 -0
  98. plexflow/core/plex/api/context/authorized.py +15 -0
  99. plexflow/core/plex/api/context/discover.py +14 -0
  100. plexflow/core/plex/api/context/library.py +14 -0
  101. plexflow/core/plex/discover/__init__.py +0 -0
  102. plexflow/core/plex/discover/activity.py +448 -0
  103. plexflow/core/plex/discover/comment.py +89 -0
  104. plexflow/core/plex/discover/feed.py +11 -0
  105. plexflow/core/plex/hooks/__init__.py +0 -0
  106. plexflow/core/plex/hooks/plex_authorized.py +60 -0
  107. plexflow/core/plex/hooks/plexflow_database.py +6 -0
  108. plexflow/core/plex/library/__init__.py +0 -0
  109. plexflow/core/plex/library/library.py +103 -0
  110. plexflow/core/plex/token/__init__.py +0 -0
  111. plexflow/core/plex/token/auto_token.py +91 -0
  112. plexflow/core/plex/utils/__init__.py +0 -0
  113. plexflow/core/plex/utils/paginated.py +39 -0
  114. plexflow/core/plex/watchlist/__init__.py +0 -0
  115. plexflow/core/plex/watchlist/datatypes.py +124 -0
  116. plexflow/core/plex/watchlist/watchlist.py +23 -0
  117. plexflow/core/storage/__init__.py +0 -0
  118. plexflow/core/storage/object/__init__.py +0 -0
  119. plexflow/core/storage/object/plexflow_storage.py +143 -0
  120. plexflow/core/storage/object/redis_storage.py +169 -0
  121. plexflow/core/subtitles/__init__.py +0 -0
  122. plexflow/core/subtitles/providers/__init__.py +0 -0
  123. plexflow/core/subtitles/providers/auto_subtitles.py +48 -0
  124. plexflow/core/subtitles/providers/oss/__init__.py +0 -0
  125. plexflow/core/subtitles/providers/oss/datatypes.py +104 -0
  126. plexflow/core/subtitles/providers/oss/download.py +48 -0
  127. plexflow/core/subtitles/providers/oss/old.py +144 -0
  128. plexflow/core/subtitles/providers/oss/oss.py +400 -0
  129. plexflow/core/subtitles/providers/oss/oss_subtitle.py +32 -0
  130. plexflow/core/subtitles/providers/oss/search.py +52 -0
  131. plexflow/core/subtitles/providers/oss/unlimited_oss.py +231 -0
  132. plexflow/core/subtitles/providers/oss/utils/__init__.py +0 -0
  133. plexflow/core/subtitles/providers/oss/utils/config.py +63 -0
  134. plexflow/core/subtitles/providers/oss/utils/download_client.py +22 -0
  135. plexflow/core/subtitles/providers/oss/utils/exceptions.py +35 -0
  136. plexflow/core/subtitles/providers/oss/utils/file_utils.py +83 -0
  137. plexflow/core/subtitles/providers/oss/utils/languages.py +78 -0
  138. plexflow/core/subtitles/providers/oss/utils/response_base.py +221 -0
  139. plexflow/core/subtitles/providers/oss/utils/responses.py +176 -0
  140. plexflow/core/subtitles/providers/oss/utils/srt.py +561 -0
  141. plexflow/core/subtitles/results/__init__.py +0 -0
  142. plexflow/core/subtitles/results/subtitle.py +170 -0
  143. plexflow/core/torrents/__init__.py +0 -0
  144. plexflow/core/torrents/analyzers/analyzed_torrent.py +143 -0
  145. plexflow/core/torrents/analyzers/analyzer.py +45 -0
  146. plexflow/core/torrents/analyzers/torrentquest/analyzer.py +47 -0
  147. plexflow/core/torrents/auto/auto_providers/auto/__init__.py +0 -0
  148. plexflow/core/torrents/auto/auto_providers/auto/torrent.py +64 -0
  149. plexflow/core/torrents/auto/auto_providers/tpb/torrent.py +62 -0
  150. plexflow/core/torrents/auto/auto_torrents.py +29 -0
  151. plexflow/core/torrents/providers/__init__.py +0 -0
  152. plexflow/core/torrents/providers/ext/__init__.py +0 -0
  153. plexflow/core/torrents/providers/ext/ext.py +18 -0
  154. plexflow/core/torrents/providers/ext/utils.py +64 -0
  155. plexflow/core/torrents/providers/extratorrent/__init__.py +0 -0
  156. plexflow/core/torrents/providers/extratorrent/extratorrent.py +21 -0
  157. plexflow/core/torrents/providers/extratorrent/utils.py +66 -0
  158. plexflow/core/torrents/providers/eztv/__init__.py +0 -0
  159. plexflow/core/torrents/providers/eztv/eztv.py +47 -0
  160. plexflow/core/torrents/providers/eztv/utils.py +83 -0
  161. plexflow/core/torrents/providers/rarbg2/__init__.py +0 -0
  162. plexflow/core/torrents/providers/rarbg2/rarbg2.py +19 -0
  163. plexflow/core/torrents/providers/rarbg2/utils.py +76 -0
  164. plexflow/core/torrents/providers/snowfl/__init__.py +0 -0
  165. plexflow/core/torrents/providers/snowfl/snowfl.py +36 -0
  166. plexflow/core/torrents/providers/snowfl/utils.py +59 -0
  167. plexflow/core/torrents/providers/tgx/__init__.py +0 -0
  168. plexflow/core/torrents/providers/tgx/context.py +50 -0
  169. plexflow/core/torrents/providers/tgx/dump.py +40 -0
  170. plexflow/core/torrents/providers/tgx/tgx.py +22 -0
  171. plexflow/core/torrents/providers/tgx/utils.py +61 -0
  172. plexflow/core/torrents/providers/therarbg/__init__.py +0 -0
  173. plexflow/core/torrents/providers/therarbg/therarbg.py +17 -0
  174. plexflow/core/torrents/providers/therarbg/utils.py +61 -0
  175. plexflow/core/torrents/providers/torrentquest/__init__.py +0 -0
  176. plexflow/core/torrents/providers/torrentquest/torrentquest.py +20 -0
  177. plexflow/core/torrents/providers/torrentquest/utils.py +70 -0
  178. plexflow/core/torrents/providers/tpb/__init__.py +0 -0
  179. plexflow/core/torrents/providers/tpb/tpb.py +17 -0
  180. plexflow/core/torrents/providers/tpb/utils.py +139 -0
  181. plexflow/core/torrents/providers/yts/__init__.py +0 -0
  182. plexflow/core/torrents/providers/yts/utils.py +57 -0
  183. plexflow/core/torrents/providers/yts/yts.py +31 -0
  184. plexflow/core/torrents/results/__init__.py +0 -0
  185. plexflow/core/torrents/results/torrent.py +165 -0
  186. plexflow/core/torrents/results/universal.py +220 -0
  187. plexflow/core/torrents/results/utils.py +15 -0
  188. plexflow/events/__init__.py +0 -0
  189. plexflow/events/download/__init__.py +0 -0
  190. plexflow/events/download/torrent_events.py +96 -0
  191. plexflow/events/publish/__init__.py +0 -0
  192. plexflow/events/publish/publish.py +34 -0
  193. plexflow/logging/__init__.py +0 -0
  194. plexflow/logging/log_setup.py +8 -0
  195. plexflow/spiders/quiet_logger.py +9 -0
  196. plexflow/spiders/tgx/pipelines/dump_json_pipeline.py +30 -0
  197. plexflow/spiders/tgx/pipelines/meta_pipeline.py +13 -0
  198. plexflow/spiders/tgx/pipelines/publish_pipeline.py +14 -0
  199. plexflow/spiders/tgx/pipelines/torrent_info_pipeline.py +12 -0
  200. plexflow/spiders/tgx/pipelines/validation_pipeline.py +17 -0
  201. plexflow/spiders/tgx/settings.py +36 -0
  202. plexflow/spiders/tgx/spider.py +72 -0
  203. plexflow/utils/__init__.py +0 -0
  204. plexflow/utils/antibot/human_like_requests.py +122 -0
  205. plexflow/utils/api/__init__.py +0 -0
  206. plexflow/utils/api/context/http.py +62 -0
  207. plexflow/utils/api/rest/__init__.py +0 -0
  208. plexflow/utils/api/rest/antibot_restful.py +68 -0
  209. plexflow/utils/api/rest/restful.py +49 -0
  210. plexflow/utils/captcha/__init__.py +0 -0
  211. plexflow/utils/captcha/bypass/__init__.py +0 -0
  212. plexflow/utils/captcha/bypass/decode_audio.py +34 -0
  213. plexflow/utils/download/__init__.py +0 -0
  214. plexflow/utils/download/gz.py +26 -0
  215. plexflow/utils/filesystem/__init__.py +0 -0
  216. plexflow/utils/filesystem/search.py +129 -0
  217. plexflow/utils/gmail/__init__.py +0 -0
  218. plexflow/utils/gmail/mails.py +116 -0
  219. plexflow/utils/hooks/__init__.py +0 -0
  220. plexflow/utils/hooks/http.py +84 -0
  221. plexflow/utils/hooks/postgresql.py +93 -0
  222. plexflow/utils/hooks/redis.py +112 -0
  223. plexflow/utils/image/storage.py +36 -0
  224. plexflow/utils/imdb/__init__.py +0 -0
  225. plexflow/utils/imdb/imdb_codes.py +107 -0
  226. plexflow/utils/pubsub/consume.py +82 -0
  227. plexflow/utils/pubsub/produce.py +25 -0
  228. plexflow/utils/retry/__init__.py +0 -0
  229. plexflow/utils/retry/utils.py +38 -0
  230. plexflow/utils/strings/__init__.py +0 -0
  231. plexflow/utils/strings/filesize.py +55 -0
  232. plexflow/utils/strings/language.py +14 -0
  233. plexflow/utils/subtitle/search.py +76 -0
  234. plexflow/utils/tasks/decorators.py +78 -0
  235. plexflow/utils/tasks/k8s/task.py +70 -0
  236. plexflow/utils/thread_safe/safe_list.py +54 -0
  237. plexflow/utils/thread_safe/safe_set.py +69 -0
  238. plexflow/utils/torrent/__init__.py +0 -0
  239. plexflow/utils/torrent/analyze.py +118 -0
  240. plexflow/utils/torrent/extract/common.py +37 -0
  241. plexflow/utils/torrent/extract/ext.py +2391 -0
  242. plexflow/utils/torrent/extract/extratorrent.py +56 -0
  243. plexflow/utils/torrent/extract/kat.py +1581 -0
  244. plexflow/utils/torrent/extract/tgx.py +96 -0
  245. plexflow/utils/torrent/extract/therarbg.py +170 -0
  246. plexflow/utils/torrent/extract/torrentquest.py +171 -0
  247. plexflow/utils/torrent/files.py +36 -0
  248. plexflow/utils/torrent/hash.py +90 -0
  249. plexflow/utils/transcribe/__init__.py +0 -0
  250. plexflow/utils/transcribe/speech2text.py +40 -0
  251. plexflow/utils/video/__init__.py +0 -0
  252. plexflow/utils/video/subtitle.py +73 -0
  253. plexflow-0.0.64.dist-info/METADATA +71 -0
  254. plexflow-0.0.64.dist-info/RECORD +256 -0
  255. plexflow-0.0.64.dist-info/WHEEL +4 -0
  256. plexflow-0.0.64.dist-info/entry_points.txt +24 -0
@@ -0,0 +1,36 @@
1
+ from plexflow.core.metadata.auto.auto_providers.auto.movie import AutoMovie
2
+ from plexflow.core.metadata.providers.tvdb.tvdb import search_movie_by_imdb
3
+ from datetime import datetime
4
+
5
+ class AutoTvdbMovie(AutoMovie):
6
+ def __init__(self, imdb_id: str) -> None:
7
+ super().__init__(imdb_id, 'tvdb')
8
+ self._movie = search_movie_by_imdb(self.imdb_id)
9
+
10
+ @property
11
+ def id(self) -> int:
12
+ return self._movie.id
13
+
14
+ @property
15
+ def title(self) -> str:
16
+ return self._movie.name
17
+
18
+ @property
19
+ def release_date(self) -> datetime:
20
+ return datetime.strptime(self._movie.first_release.date, '%Y-%m-%d')
21
+
22
+ @property
23
+ def runtime(self) -> int:
24
+ return self._movie.runtime
25
+
26
+ @property
27
+ def titles(self) -> list:
28
+ return [x.name for x in self._movie.aliases]
29
+
30
+ @property
31
+ def summary(self) -> str:
32
+ return ""
33
+
34
+ @property
35
+ def language(self) -> str:
36
+ return self._movie.originalLanguage
@@ -0,0 +1,25 @@
1
+ from datetime import datetime
2
+ from plexflow.core.metadata.auto.auto_providers.auto.season import AutoSeason
3
+ from plexflow.core.metadata.auto.auto_providers.auto.show import AutoShow
4
+ from plexflow.core.metadata.auto.auto_providers.auto.episode import AutoEpisode
5
+ from plexflow.core.metadata.auto.auto_providers.tvdb.episode import AutoTvdbEpisode
6
+ from plexflow.core.metadata.providers.tvdb.tvdb import get_season
7
+
8
+ class AutoTvdbSeason(AutoSeason):
9
+ def __init__(self, parent: AutoShow, season_id: int) -> None:
10
+ super().__init__(parent)
11
+ self._season = get_season(season_id)
12
+
13
+ @property
14
+ def episodes(self) -> list[AutoEpisode]:
15
+ return [AutoTvdbEpisode(self, x) for x in self._season.get('episodes')]
16
+
17
+ @property
18
+ def release_date(self) -> datetime:
19
+ episodes = self._season.get('episodes')
20
+ first_aired = next(filter(lambda x: x, map(lambda x: x.get('aired'), sorted(episodes, key=lambda x: x.get('number')))), None)
21
+ return datetime.strptime(first_aired, '%Y-%m-%d') if first_aired else None
22
+
23
+ @property
24
+ def season_number(self) -> int:
25
+ return self._season.get('number')
@@ -0,0 +1,41 @@
1
+ from plexflow.core.metadata.auto.auto_providers.auto.show import AutoShow
2
+ from plexflow.core.metadata.providers.tvdb.tvdb import search_show_by_imdb
3
+ from datetime import datetime
4
+ from plexflow.core.metadata.auto.auto_providers.tvdb.season import AutoTvdbSeason, AutoSeason
5
+
6
+ class AutoTvdbShow(AutoShow):
7
+ def __init__(self, imdb_id: str) -> None:
8
+ super().__init__(imdb_id, 'tvdb')
9
+ self._show = search_show_by_imdb(self.imdb_id)
10
+
11
+ @property
12
+ def id(self) -> int:
13
+ return self._show.id
14
+
15
+ @property
16
+ def title(self) -> str:
17
+ return self._show.name
18
+
19
+ @property
20
+ def release_date(self) -> datetime:
21
+ return datetime.strptime(self._show.firstAired, '%Y-%m-%d')
22
+
23
+ @property
24
+ def runtime(self) -> int:
25
+ return self._show.averageRuntime
26
+
27
+ @property
28
+ def titles(self) -> list:
29
+ return [x.name for x in self._show.aliases]
30
+
31
+ @property
32
+ def summary(self) -> str:
33
+ return self._show.overview
34
+
35
+ @property
36
+ def language(self) -> str:
37
+ return self._show.originalLanguage
38
+
39
+ @property
40
+ def seasons(self) -> list[AutoSeason]:
41
+ return [AutoTvdbSeason(self, x.id) for x in self._show.seasons]
File without changes
File without changes
@@ -0,0 +1,53 @@
1
+ from datetime import datetime as dt
2
+ from dataclasses import dataclass, field
3
+ from dataclasses_json import dataclass_json, Undefined
4
+
5
+ @dataclass_json(undefined=Undefined.EXCLUDE)
6
+ @dataclass
7
+ class ImdbMovie:
8
+ """
9
+ Represents a movie from IMDb.
10
+
11
+ Attributes:
12
+ imdb_id (str): The IMDb ID of the movie.
13
+ title (str): The title of the movie.
14
+ release_date (dt): The release date of the movie.
15
+ runtime (int): The runtime of the movie in minutes.
16
+ rating (float): The rating of the movie.
17
+ votes (int): The number of votes for the movie.
18
+ rank (int): The rank of the movie.
19
+ """
20
+ imdb_id: str
21
+ title: str
22
+ release_date: dt
23
+ runtime: int
24
+ rating: float
25
+ votes: int
26
+ rank: int
27
+
28
+ @dataclass_json(undefined=Undefined.EXCLUDE)
29
+ @dataclass
30
+ class ImdbShow:
31
+ """
32
+ Represents a TV show from IMDb.
33
+
34
+ Attributes:
35
+ imdb_id (str): The IMDb ID of the show.
36
+ title (str): The title of the show.
37
+ release_date (dt): The release date of the show.
38
+ runtime (int): The runtime of each episode in minutes.
39
+ rating (float): The average rating of the show.
40
+ votes (int): The number of votes the show has received.
41
+ rank (int): The IMDb rank of the show.
42
+ episodes (int): The total number of episodes in the show.
43
+ seasons (int): The total number of seasons in the show.
44
+ """
45
+ imdb_id: str
46
+ title: str
47
+ release_date: dt
48
+ runtime: int
49
+ rating: float
50
+ votes: int
51
+ rank: int
52
+ episodes: int
53
+ seasons: int
@@ -0,0 +1,112 @@
1
+ import requests
2
+ from bs4 import BeautifulSoup
3
+ from datetime import datetime as dt
4
+ from plexflow.core.metadata.providers.imdb.datatypes import ImdbMovie, ImdbShow
5
+ from typing import Union
6
+ import json
7
+
8
+ def search_movie_by_imdb(imdb_id: str) -> Union[ImdbMovie, None]:
9
+ """
10
+ Search for a movie using its IMDB ID.
11
+
12
+ Args:
13
+ imdb_id (str): The IMDB ID of the movie.
14
+
15
+ Returns:
16
+ Union[ImdbMovie, None]: An instance of the ImdbMovie class representing the movie if found,
17
+ or None if the movie is not found.
18
+
19
+ Raises:
20
+ RuntimeError: If an HTTP error, connection error, timeout error, or request exception occurs.
21
+ """
22
+ url = f"https://www.imdb.com/title/{imdb_id}"
23
+ headers = {
24
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
25
+ }
26
+
27
+ try:
28
+ r = requests.get(url=url, headers=headers)
29
+ r.raise_for_status()
30
+ except requests.exceptions.RequestException as err:
31
+ raise RuntimeError("An error occurred during the request.") from err
32
+
33
+ soup = BeautifulSoup(r.text, "html.parser")
34
+ scripts = soup.select("script[type='application/json']")
35
+
36
+ for script in scripts:
37
+ content = script.text
38
+ data = json.loads(content)
39
+ imdb_id = data.get("props", {}).get("pageProps", {}).get("tconst")
40
+ data = data.get("props", {}).get("pageProps", {}).get("aboveTheFoldData")
41
+
42
+ if data:
43
+ title = data.get("originalTitleText", {}).get("text")
44
+ release_day = data.get("releaseDate", {}).get("day")
45
+ release_month = data.get("releaseDate", {}).get("month")
46
+ release_year = data.get("releaseDate", {}).get("year")
47
+ date_str = f"{release_year:04d}-{release_month:02d}-{release_day:02d}"
48
+ release_date = dt.strptime(date_str, "%Y-%m-%d")
49
+
50
+ runtime = data.get("runtime", {}).get("seconds")
51
+ rating = data.get("ratingsSummary", {}).get("aggregateRating")
52
+ votes = data.get("ratingsSummary", {}).get("voteCount")
53
+ rank = data.get("meterRanking", {}).get("currentRank")
54
+
55
+ return ImdbMovie(imdb_id, title, release_date, runtime, rating, votes, rank)
56
+
57
+ return None
58
+
59
+
60
+ def search_show_by_imdb(imdb_id: str) -> Union[ImdbShow, None]:
61
+ """
62
+ Search for a TV show on IMDb using the IMDb ID.
63
+
64
+ Args:
65
+ imdb_id (str): The IMDb ID of the TV show.
66
+
67
+ Returns:
68
+ Union[ImdbShow, None]: An instance of the ImdbShow class representing the TV show if found,
69
+ or None if the TV show is not found.
70
+
71
+ Raises:
72
+ RuntimeError: If an HTTP error, connection error, timeout error, or request exception occurs.
73
+ """
74
+ url = f"https://www.imdb.com/title/{imdb_id}"
75
+ headers = {
76
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
77
+ }
78
+
79
+ try:
80
+ r = requests.get(url=url, headers=headers)
81
+ r.raise_for_status()
82
+ except requests.exceptions.RequestException as err:
83
+ raise RuntimeError("An error occurred during the request.") from err
84
+
85
+ soup = BeautifulSoup(r.text, "html.parser")
86
+ scripts = soup.select("script[type='application/json']")
87
+
88
+ for script in scripts:
89
+ content = script.text
90
+ data = json.loads(content)
91
+ imdb_id = data.get("props", {}).get("pageProps", {}).get("tconst")
92
+ main_data = data.get("props", {}).get("pageProps", {}).get("mainColumnData")
93
+ data = data.get("props", {}).get("pageProps", {}).get("aboveTheFoldData")
94
+
95
+ if data:
96
+ title = data.get("originalTitleText", {}).get("text")
97
+ release_day = data.get("releaseDate", {}).get("day")
98
+ release_month = data.get("releaseDate", {}).get("month")
99
+ release_year = data.get("releaseDate", {}).get("year")
100
+ date_str = f"{release_year:04d}-{release_month:02d}-{release_day:02d}"
101
+ release_date = dt.strptime(date_str, "%Y-%m-%d")
102
+ episodes = main_data.get("episodes", {}).get("episodes", {}).get("total")
103
+ seasons = len(set(map(lambda s: s.get("number"), main_data.get("episodes", {}).get("seasons", []))))
104
+
105
+ runtime = data.get("runtime", {}).get("seconds")
106
+ rating = data.get("ratingsSummary", {}).get("aggregateRating")
107
+ votes = data.get("ratingsSummary", {}).get("voteCount")
108
+ rank = data.get("meterRanking", {}).get("currentRank")
109
+
110
+ return ImdbShow(imdb_id, title, release_date, runtime, rating, votes, rank, episodes, seasons)
111
+
112
+ return None
@@ -0,0 +1,111 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import List, Optional, Dict
3
+ from dataclasses_json import dataclass_json, Undefined
4
+
5
+ @dataclass_json(undefined=Undefined.EXCLUDE)
6
+ @dataclass
7
+ class Actor:
8
+ """
9
+ Represents an actor in a movie.
10
+
11
+ Attributes:
12
+ name (Optional[str]): The name of the actor.
13
+ voice (Optional[bool]): Indicates if the actor provided a voice-over for the movie.
14
+ _catchall (Dict[str, str]): A catch-all dictionary for any additional attributes.
15
+ """
16
+ name: Optional[str] = None
17
+ voice: Optional[bool] = None
18
+ _catchall: Dict[str, str] = field(default_factory=dict)
19
+
20
+ @dataclass_json(undefined=Undefined.EXCLUDE)
21
+ @dataclass
22
+ class Director:
23
+ """
24
+ Represents a movie director.
25
+
26
+ Attributes:
27
+ name (Optional[str]): The name of the director.
28
+ id (Optional[str]): The ID of the director.
29
+ _catchall (Dict[str, str]): A catch-all dictionary for any additional attributes.
30
+ """
31
+ name: Optional[str] = None
32
+ id: Optional[str] = None
33
+ _catchall: Dict[str, str] = field(default_factory=dict)
34
+
35
+ @dataclass_json(undefined=Undefined.EXCLUDE)
36
+ @dataclass
37
+ class Posters:
38
+ """
39
+ Represents the posters of a movie.
40
+
41
+ Attributes:
42
+ thumb (Optional[str]): The URL of the thumbnail-sized poster.
43
+ small (Optional[str]): The URL of the small-sized poster.
44
+ regular (Optional[str]): The URL of the regular-sized poster.
45
+ large (Optional[str]): The URL of the large-sized poster.
46
+ _catchall (Dict[str, str]): A catch-all dictionary for any additional poster URLs.
47
+ """
48
+ thumb: Optional[str] = None
49
+ small: Optional[str] = None
50
+ regular: Optional[str] = None
51
+ large: Optional[str] = None
52
+ _catchall: Dict[str, str] = field(default_factory=dict)
53
+
54
+ @dataclass_json(undefined=Undefined.EXCLUDE)
55
+ @dataclass
56
+ class MovieMeterMovie:
57
+ """
58
+ Represents a movie from MovieMeter.
59
+
60
+ Attributes:
61
+ id (Optional[int]): The ID of the movie.
62
+ objectID (Optional[int]): The object ID of the movie.
63
+ url (Optional[str]): The URL of the movie.
64
+ year (Optional[int]): The year of the movie.
65
+ imdb (Optional[str]): The IMDb ID of the movie.
66
+ title (Optional[str]): The title of the movie.
67
+ display_title (Optional[str]): The display title of the movie.
68
+ alternative_title (Optional[str]): The alternative title of the movie.
69
+ plot (Optional[str]): The plot of the movie.
70
+ duration (Optional[int]): The duration of the movie.
71
+ votes_count (Optional[int]): The count of votes for the movie.
72
+ average (Optional[float]): The average rating of the movie.
73
+ average_x2 (Optional[float]): The average rating multiplied by 2.
74
+ average_x10 (Optional[float]): The average rating multiplied by 10.
75
+ posters (Optional[Posters]): The posters of the movie.
76
+ countries (Optional[List[str]]): The countries of the movie.
77
+ genres (Optional[List[str]]): The genres of the movie.
78
+ type (Optional[List[str]]): The type of the movie.
79
+ actors (Optional[List[Actor]]): The actors in the movie.
80
+ directors (Optional[List[Director]]): The directors of the movie.
81
+ user_vote (Optional[str]): The user's vote for the movie.
82
+ _catchall (Dict[str, str]): A catch-all dictionary for additional attributes.
83
+ """
84
+
85
+ id: Optional[int] = None
86
+ objectID: Optional[int] = None
87
+ url: Optional[str] = None
88
+ year: Optional[int] = None
89
+ imdb: Optional[str] = None
90
+ title: Optional[str] = None
91
+ display_title: Optional[str] = None
92
+ alternative_title: Optional[str] = None
93
+ plot: Optional[str] = None
94
+ duration: Optional[int] = None
95
+ votes_count: Optional[int] = None
96
+ average: Optional[float] = None
97
+ average_x2: Optional[float] = None
98
+ average_x10: Optional[float] = None
99
+ posters: Optional[Posters] = None
100
+ countries: Optional[List[str]] = None
101
+ genres: Optional[List[str]] = None
102
+ type: Optional[List[str]] = None
103
+ actors: Optional[List[Actor]] = None
104
+ directors: Optional[List[Director]] = None
105
+ user_vote: Optional[str] = None
106
+ _catchall: Dict[str, str] = field(default_factory=dict)
107
+
108
+ def __post_init__(self):
109
+ self.actors = [Actor(**actor) if isinstance(actor, dict) else actor for actor in self.actors]
110
+ self.directors = [Director(**director) if isinstance(director, dict) else director for director in self.directors]
111
+ self.posters = Posters(**self.posters) if isinstance(self.posters, dict) else self.posters
@@ -0,0 +1,42 @@
1
+ import requests
2
+ import os
3
+ from typing import Union
4
+ from plexflow.core.metadata.providers.moviemeter.datatypes import MovieMeterMovie
5
+
6
+ def search_movie_by_imdb(imdb_id: str) -> Union[MovieMeterMovie, None]:
7
+ """
8
+ Search for a movie using its IMDB ID.
9
+
10
+ Parameters:
11
+ - imdb_id (str): The IMDB ID of the movie.
12
+
13
+ Returns:
14
+ - MovieMeterMovie: A MovieMeterMovie object containing all the details of the movie.
15
+ - None: If the movie is not found or an error occurs.
16
+
17
+ Example:
18
+ >>> search_movie_by_imdb('tt0111161')
19
+ MovieMeterMovie(title='The Shawshank Redemption', release_date=datetime.datetime(1994, 9, 23, 0, 0), runtime=8520, rating=9.3, votes=2400000, rank=1)
20
+ """
21
+ url = f"https://www.moviemeter.nl/api/film/{imdb_id}"
22
+ headers = {
23
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
24
+ }
25
+ params = {
26
+ "api_key": os.getenv("MOVIEMETER_API_KEY")
27
+ }
28
+
29
+ try:
30
+ r = requests.get(url=url, headers=headers, params=params)
31
+ r.raise_for_status()
32
+ except requests.exceptions.HTTPError as errh:
33
+ raise RuntimeError("HTTP Error occurred") from errh
34
+ except requests.exceptions.ConnectionError as errc:
35
+ raise RuntimeError("Connection Error occurred") from errc
36
+ except requests.exceptions.Timeout as errt:
37
+ raise RuntimeError("Timeout Error occurred") from errt
38
+ except requests.exceptions.RequestException as err:
39
+ raise RuntimeError("Request Exception occurred") from err
40
+
41
+ data = r.json()
42
+ return MovieMeterMovie.from_dict(data)
File without changes