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,130 @@
1
+ from datetime import datetime
2
+ from plexflow.core.metadata.auto.auto_providers.auto.movie import AutoMovie
3
+ from typing import List, Any
4
+ from langcodes import standardize_tag
5
+
6
+ class UniversalMovie:
7
+ def __init__(self, providers: List[AutoMovie]):
8
+ self.providers = providers
9
+
10
+ @property
11
+ def plex_rating_key(self) -> str:
12
+ for p in self.providers:
13
+ if p.source == "plex":
14
+ return p.id
15
+
16
+ @property
17
+ def title(self) -> str:
18
+ for p in self.providers:
19
+ if p.title:
20
+ return p.title
21
+
22
+ @property
23
+ def year(self) -> int:
24
+ for p in self.providers:
25
+ if p.year:
26
+ return p.year
27
+
28
+ @property
29
+ def rank(self) -> int:
30
+ for p in self.providers:
31
+ if p.rank:
32
+ return p.rank
33
+
34
+ @property
35
+ def release_date(self) -> datetime:
36
+ for p in self.providers:
37
+ if p.release_date:
38
+ return p.release_date
39
+
40
+ @property
41
+ def imdb_id(self) -> str:
42
+ for p in self.providers:
43
+ if p.imdb_id:
44
+ return p.imdb_id
45
+
46
+ @property
47
+ def titles(self) -> List[str]:
48
+ results = set()
49
+
50
+ for p in self.providers:
51
+ results.update(p.titles)
52
+ results.add(p.title)
53
+
54
+ return list(results)
55
+
56
+ @property
57
+ def runtime(self) -> int:
58
+ for p in self.providers:
59
+ if p.runtime:
60
+ return p.runtime
61
+
62
+ @property
63
+ def summary(self) -> str:
64
+ for p in self.providers:
65
+ if p.summary:
66
+ return p.summary
67
+
68
+ @property
69
+ def sources(self) -> List[str]:
70
+ return list({p.source for p in self.providers})
71
+
72
+ @property
73
+ def language(self) -> str:
74
+ for p in self.providers:
75
+ if p.language:
76
+ return standardize_tag(p.language)
77
+
78
+ @property
79
+ def release_dates(self) -> List[datetime]:
80
+ return list({p.release_date for p in self.providers if isinstance(p.release_date, datetime)})
81
+
82
+ @property
83
+ def years(self) -> List[int]:
84
+ return list({p.year for p in self.providers if isinstance(p.year, int)})
85
+
86
+ @property
87
+ def runtimes(self) -> List[int]:
88
+ return list({p.runtime for p in self.providers if isinstance(p.runtime, int)})
89
+
90
+ @property
91
+ def languages(self) -> List[str]:
92
+ return list({standardize_tag(p.language) for p in self.providers if isinstance(p.language, str)})
93
+
94
+ @property
95
+ def imdb_ids(self) -> List[str]:
96
+ return list({p.imdb_id for p in self.providers if isinstance(p.imdb_id, str)})
97
+
98
+ def _is_field_consistent(self, field: str) -> bool:
99
+ return len(getattr(self, field)) == 1
100
+
101
+ @property
102
+ def is_imdb_id_consistent(self) -> bool:
103
+ return self._is_field_consistent("imdb_ids")
104
+
105
+ @property
106
+ def is_year_consistent(self) -> bool:
107
+ return self._is_field_consistent("years")
108
+
109
+ @property
110
+ def is_release_date_consistent(self) -> bool:
111
+ return self._is_field_consistent("release_dates")
112
+
113
+ @property
114
+ def is_runtime_consistent(self) -> bool:
115
+ return self._is_field_consistent("runtimes")
116
+
117
+ @property
118
+ def is_language_consistent(self) -> bool:
119
+ return self._is_field_consistent("languages")
120
+
121
+ @property
122
+ def is_released(self) -> bool:
123
+ now = datetime.now()
124
+ return all(d <= now for d in self.release_dates)
125
+
126
+ @property
127
+ def days_until_release(self) -> int:
128
+ now = datetime.now()
129
+
130
+ return min((d - now).days for d in self.release_dates)
@@ -0,0 +1,192 @@
1
+ from datetime import datetime
2
+ from dataclasses import dataclass
3
+ from dataclasses_json import dataclass_json, Undefined
4
+ from plexflow.core.metadata.providers.tmdb.datatypes import TmdbMovie
5
+ from plexflow.core.metadata.providers.tvdb.datatypes import TvdbMovie
6
+ from plexflow.core.metadata.providers.imdb.datatypes import ImdbMovie
7
+ from plexflow.core.metadata.providers.moviemeter.datatypes import MovieMeterMovie
8
+ from plexflow.core.metadata.providers.plex.datatypes import PlexMovieMetadata
9
+
10
+ @dataclass_json(undefined=Undefined.EXCLUDE)
11
+ @dataclass
12
+ class UniversalMovie:
13
+ """
14
+ Represents a movie with data from TmdbMovie, TvdbMovie, ImdbMovie, and MovieMeterMovie.
15
+
16
+ Attributes:
17
+ tmdb_movie (TmdbMovie): The TmdbMovie object.
18
+ tvdb_movie (TvdbMovie): The TvdbMovie object.
19
+ imdb_movie (ImdbMovie): The ImdbMovie object.
20
+ moviemeter_movie (MovieMeterMovie): The MovieMeterMovie object.
21
+ """
22
+ tmdb_movie: TmdbMovie
23
+ tvdb_movie: TvdbMovie
24
+ imdb_movie: ImdbMovie
25
+ moviemeter_movie: MovieMeterMovie
26
+ plex_movie: PlexMovieMetadata
27
+
28
+ @property
29
+ def is_release_date_consistent(self) -> bool:
30
+ """
31
+ Checks whether tmdb, tvdb, and imdb have the same release date.
32
+
33
+ Returns:
34
+ bool: True if the release dates are the same, False otherwise.
35
+ """
36
+ if self.tmdb_movie.release_date and self.tvdb_movie.first_release.date and self.imdb_movie.release_date:
37
+ return self.tmdb_movie.release_date == self.tvdb_movie.first_release.date == self.imdb_movie.release_date.strftime("%Y-%m-%d")
38
+ return False
39
+
40
+ @property
41
+ def is_year_consistent(self) -> bool:
42
+ """
43
+ Checks whether tmdb, tvdb, imdb, and moviemeter have the same release year.
44
+
45
+ Returns:
46
+ bool: True if the release years are the same, False otherwise.
47
+ """
48
+ if self.tmdb_movie.release_date and self.tvdb_movie.first_release.date and self.imdb_movie.release_date and self.moviemeter_movie.year:
49
+ tmdb_year = datetime.strptime(self.tmdb_movie.release_date, "%Y-%m-%d").year
50
+ tvdb_year = datetime.strptime(self.tvdb_movie.first_release.date, "%Y-%m-%d").year
51
+ imdb_year = self.imdb_movie.release_date.year
52
+ moviemeter_year = self.moviemeter_movie.year
53
+ return tmdb_year == tvdb_year == imdb_year == moviemeter_year
54
+ return False
55
+
56
+ @property
57
+ def titles(self) -> set:
58
+ """
59
+ Gets a set of all possible titles for the movie.
60
+
61
+ Returns:
62
+ set: A set of all possible titles for the movie in lowercase.
63
+ """
64
+ titles = set()
65
+ if self.tmdb_movie.title:
66
+ titles.add(self.tmdb_movie.title.lower())
67
+ if self.tvdb_movie.name:
68
+ titles.add(self.tvdb_movie.name.lower())
69
+ if self.imdb_movie.title:
70
+ titles.add(self.imdb_movie.title.lower())
71
+ if self.moviemeter_movie.title:
72
+ titles.add(self.moviemeter_movie.title.lower())
73
+ if self.tmdb_movie.alternative_titles:
74
+ for alt_title in self.tmdb_movie.alternative_titles.titles:
75
+ titles.add(alt_title.title.lower())
76
+ return titles
77
+
78
+ @property
79
+ def imdb(self) -> str:
80
+ """
81
+ Gets the IMDb ID of the movie.
82
+
83
+ Returns:
84
+ str: The IMDb ID of the movie.
85
+ """
86
+ return self.tmdb_movie.imdb_id
87
+
88
+ @property
89
+ def title(self) -> str:
90
+ """
91
+ Gets the title of the movie.
92
+
93
+ Returns:
94
+ str: The title of the movie.
95
+ """
96
+ return self.tmdb_movie.title
97
+
98
+ @property
99
+ def year(self) -> int:
100
+ """
101
+ Gets the year of the movie.
102
+
103
+ Returns:
104
+ int: The year of the movie. If the year is not available in tmdb, it uses tvdb, then imdb, then moviemeter.
105
+ """
106
+ if self.tmdb_movie.release_date:
107
+ return datetime.strptime(self.tmdb_movie.release_date, "%Y-%m-%d").year
108
+ elif self.tvdb_movie.first_release.date:
109
+ return datetime.strptime(self.tvdb_movie.first_release.date, "%Y-%m-%d").year
110
+ elif self.imdb_movie.release_date:
111
+ return self.imdb_movie.release_date.year
112
+ elif self.moviemeter_movie.year:
113
+ return self.moviemeter_movie.year
114
+ else:
115
+ raise ValueError("Year is not available in tmdb, tvdb, imdb, and moviemeter.")
116
+
117
+ @property
118
+ def is_released(self) -> bool:
119
+ """
120
+ Checks whether the movie is already released.
121
+
122
+ Returns:
123
+ bool: True if the movie is already released, False otherwise. If the status is not available in tmdb, it uses tvdb, then imdb.
124
+ """
125
+ if not self.tmdb_movie.release_date and not self.tvdb_movie.first_release.date and not self.imdb_movie.release_date:
126
+ raise ValueError("Status is not available in tmdb, tvdb, and imdb.")
127
+
128
+ now = datetime.now()
129
+ if self.tmdb_movie.release_date:
130
+ tmdb_date = datetime.strptime(self.tmdb_movie.release_date, "%Y-%m-%d")
131
+ if tmdb_date <= now:
132
+ return True
133
+ if self.tvdb_movie.first_release.date:
134
+ tvdb_date = datetime.strptime(self.tvdb_movie.first_release.date, "%Y-%m-%d")
135
+ if tvdb_date <= now:
136
+ return True
137
+ if self.imdb_movie.release_date:
138
+ if self.imdb_movie.release_date <= now:
139
+ return True
140
+ return False
141
+
142
+ @property
143
+ def days_until_release(self) -> int:
144
+ """
145
+ Gets the number of days until release.
146
+
147
+ Returns:
148
+ int: The number of days until release. If different release dates, it uses the closest.
149
+ """
150
+ if self.tmdb_movie.release_date and self.tvdb_movie.first_release.date and self.imdb_movie.release_date:
151
+ tmdb_release_date = datetime.strptime(self.tmdb_movie.release_date, "%Y-%m-%d")
152
+ tvdb_release_date = datetime.strptime(self.tvdb_movie.first_release.date, "%Y-%m-%d")
153
+ imdb_release_date = self.imdb_movie.release_date
154
+ return min((tmdb_release_date - datetime.now()).days, (tvdb_release_date - datetime.now()).days, (imdb_release_date - datetime.now()).days)
155
+ elif self.tmdb_movie.release_date:
156
+ return (datetime.strptime(self.tmdb_movie.release_date, "%Y-%m-%d") - datetime.now()).days
157
+ elif self.tvdb_movie.first_release.date:
158
+ return (datetime.strptime(self.tvdb_movie.first_release.date, "%Y-%m-%d") - datetime.now()).days
159
+ elif self.imdb_movie.release_date:
160
+ return (self.imdb_movie.release_date - datetime.now()).days
161
+ else:
162
+ raise ValueError("Release date is not available in tmdb, tvdb, and imdb.")
163
+
164
+ @property
165
+ def rank(self) -> int:
166
+ """
167
+ Gets the IMDb rank of the movie.
168
+
169
+ Returns:
170
+ int: The IMDb rank of the movie.
171
+ """
172
+ return self.imdb_movie.rank
173
+
174
+ @property
175
+ def rating(self) -> float:
176
+ """
177
+ Gets the IMDb rating of the movie.
178
+
179
+ Returns:
180
+ float: The IMDb rating of the movie.
181
+ """
182
+ return self.imdb_movie.rating
183
+
184
+ @property
185
+ def votes(self) -> int:
186
+ """
187
+ Gets the IMDb votes of the movie.
188
+
189
+ Returns:
190
+ int: The IMDb votes of the movie.
191
+ """
192
+ return self.imdb_movie.votes
@@ -0,0 +1,107 @@
1
+ from datetime import datetime
2
+ from dataclasses import dataclass
3
+ from dataclasses_json import dataclass_json, Undefined
4
+ from plexflow.core.metadata.providers.tmdb.datatypes import TmdbShow
5
+ from plexflow.core.metadata.providers.tvdb.tv_datatypes import TvdbShow
6
+ from plexflow.core.metadata.providers.imdb.datatypes import ImdbShow
7
+ from plexflow.core.metadata.providers.plex.datatypes import PlexShowMetadata
8
+
9
+ @dataclass_json(undefined=Undefined.EXCLUDE)
10
+ @dataclass
11
+ class UniversalShow:
12
+ tmdb_show: TmdbShow
13
+ tvdb_show: TvdbShow
14
+ plex_show: PlexShowMetadata
15
+ imdb_show: ImdbShow
16
+
17
+ @property
18
+ def is_release_date_consistent(self) -> bool:
19
+ """
20
+ Check if the release dates from different providers (TMDB, TVDB, and IMDb) are consistent.
21
+
22
+ Returns:
23
+ bool: True if the release dates are consistent, False otherwise.
24
+ """
25
+ tmdb_date = datetime.strptime(self.tmdb_show.first_air_date, "%Y-%m-%d")
26
+ tvdb_date = datetime.strptime(self.tvdb_show.firstAired, "%Y-%m-%d")
27
+ imdb_date = datetime.strptime(self.imdb_show.release_date, "%Y-%m-%d")
28
+
29
+ return tmdb_date == tvdb_date == imdb_date
30
+
31
+ @property
32
+ def is_year_consistent(self) -> bool:
33
+ tmdb_date = datetime.strptime(self.tmdb_show.first_air_date, "%Y-%m-%d")
34
+ tvdb_year = self.tvdb_show.year
35
+ plex_year = self.plex_show.year
36
+ return tmdb_date.year == tvdb_year == plex_year
37
+
38
+ @property
39
+ def titles(self) -> set:
40
+ title_set = set()
41
+
42
+ title_set.add(self.tmdb_show.original_name)
43
+ title_set.add(self.tvdb_show.name)
44
+ title_set.update(alias.name for alias in self.tvdb_show.aliases)
45
+ title_set.update(title.title for title in self.tmdb_show.alternative_titles.results)
46
+ title_set.add(self.plex_show.title)
47
+ title_set.add(self.imdb_show.title)
48
+
49
+ return set(map(lambda s: s.lower().strip(), title_set))
50
+
51
+ @property
52
+ def imdb(self) -> str:
53
+ """
54
+ Gets the IMDb ID of the movie.
55
+
56
+ Returns:
57
+ str: The IMDb ID of the movie.
58
+ """
59
+ return self.tmdb_show.imdb_id
60
+
61
+ @property
62
+ def title(self) -> str:
63
+ """
64
+ Gets the title of the movie.
65
+
66
+ Returns:
67
+ str: The title of the movie.
68
+ """
69
+ return self.tvdb_show.name
70
+
71
+ @property
72
+ def year(self) -> int:
73
+ return self.tvdb_show.year
74
+
75
+ @property
76
+ def is_released(self) -> bool:
77
+ tmdb_date = datetime.strptime(self.tmdb_show.first_air_date, "%Y-%m-%d")
78
+ tvdb_date = datetime.strptime(self.tvdb_show.firstAired, "%Y-%m-%d")
79
+ imdb_date = datetime.strptime(self.imdb_show.release_date, "%Y-%m-%d")
80
+
81
+ now = datetime.now()
82
+ return tmdb_date < now or tvdb_date < now or imdb_date < now
83
+
84
+ @property
85
+ def days_until_release(self) -> int:
86
+ tmdb_date = datetime.strptime(self.tmdb_show.first_air_date, "%Y-%m-%d")
87
+ tvdb_date = datetime.strptime(self.tvdb_show.firstAired, "%Y-%m-%d")
88
+ imdb_date = datetime.strptime(self.imdb_show.release_date, "%Y-%m-%d")
89
+
90
+ now = datetime.now()
91
+ tmdb_days = (tmdb_date - now).days
92
+ tvdb_days = (tvdb_date - now).days
93
+ imdb_days = (imdb_date - now).days
94
+
95
+ return min(tmdb_days, tvdb_days, imdb_days)
96
+
97
+ @property
98
+ def rank(self) -> int:
99
+ return self.imdb_show.rank
100
+
101
+ @property
102
+ def rating(self) -> float:
103
+ return self.imdb_show.rating
104
+
105
+ @property
106
+ def votes(self) -> int:
107
+ return self.imdb_show.votes
File without changes
@@ -0,0 +1,15 @@
1
+ from plexflow.utils.api.context.http import HttpRequestContext
2
+ from plexflow.core.plex.token.auto_token import PlexAutoToken
3
+
4
+ class PlexAuthorizedRequestContext(HttpRequestContext):
5
+ """
6
+ A class for setting up a default request context for Plex API with X-Plex-Token header.
7
+
8
+ Args:
9
+ base_url (str): The base URL for the Plex API.
10
+ x_plex_token (str): The X-Plex-Token for the Plex API.
11
+ """
12
+
13
+ def __init__(self, base_url: str, x_plex_token: str = None):
14
+ # Initialize the parent class with the base_url and default_headers
15
+ super().__init__(base_url, {'X-Plex-Token': PlexAutoToken(plex_token=x_plex_token).get_token(), 'Accept': 'application/json'})
@@ -0,0 +1,14 @@
1
+ from plexflow.core.plex.api.context.authorized import PlexAuthorizedRequestContext
2
+ from plexflow.core.plex.token.auto_token import PlexAutoToken
3
+ import os
4
+
5
+ class PlexDiscoverRequestContext(PlexAuthorizedRequestContext):
6
+ """
7
+ A class for setting up a default request context for Plex Discover API with X-Plex-Token header.
8
+ """
9
+
10
+ def __init__(self, base_url: str = None, x_plex_token: str = None):
11
+ # Initialize the parent class with the base_url and default_headers
12
+ super().__init__(base_url or f'https://discover.provider.plex.tv',
13
+ {'X-Plex-Token': PlexAutoToken(plex_token=x_plex_token).get_token(),
14
+ 'Accept': 'application/json'})
@@ -0,0 +1,14 @@
1
+ from plexflow.core.plex.api.context.authorized import PlexAuthorizedRequestContext
2
+ from plexflow.core.plex.token.auto_token import PlexAutoToken
3
+ import os
4
+
5
+ class PlexLibraryRequestContext(PlexAuthorizedRequestContext):
6
+ """
7
+ A class for setting up a default request context for Plex Library API with X-Plex-Token header.
8
+ """
9
+
10
+ def __init__(self, base_url: str = None, x_plex_token: str = None):
11
+ # Initialize the parent class with the base_url and default_headers
12
+ super().__init__(base_url or f'http://{os.getenv("PLEX_HOST")}:{os.getenv("PLEX_PORT", 32400)}',
13
+ {'X-Plex-Token': PlexAutoToken(plex_token=x_plex_token).get_token(),
14
+ 'Accept': 'application/json'})
File without changes