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,554 @@
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 AirsDays:
8
+ """
9
+ Represents the days on which a TV show airs.
10
+
11
+ Attributes:
12
+ friday (Optional[bool]): Indicates if the show airs on Fridays.
13
+ monday (Optional[bool]): Indicates if the show airs on Mondays.
14
+ saturday (Optional[bool]): Indicates if the show airs on Saturdays.
15
+ sunday (Optional[bool]): Indicates if the show airs on Sundays.
16
+ thursday (Optional[bool]): Indicates if the show airs on Thursdays.
17
+ tuesday (Optional[bool]): Indicates if the show airs on Tuesdays.
18
+ wednesday (Optional[bool]): Indicates if the show airs on Wednesdays.
19
+ _catchall (dict): Catch-all attribute for any additional data not defined in the class.
20
+ """
21
+ friday: Optional[bool] = None
22
+ monday: Optional[bool] = None
23
+ saturday: Optional[bool] = None
24
+ sunday: Optional[bool] = None
25
+ thursday: Optional[bool] = None
26
+ tuesday: Optional[bool] = None
27
+ wednesday: Optional[bool] = None
28
+ _catchall: dict = field(default_factory=dict, repr=False)
29
+
30
+ @dataclass_json(undefined=Undefined.EXCLUDE)
31
+ @dataclass
32
+ class Alias:
33
+ """
34
+ Represents an alias for a TV show or character.
35
+
36
+ Attributes:
37
+ language (Optional[str]): The language of the alias.
38
+ name (Optional[str]): The name of the alias.
39
+ _catchall (dict): A catch-all field for any additional attributes.
40
+ """
41
+ language: Optional[str] = None
42
+ name: Optional[str] = None
43
+ _catchall: dict = field(default_factory=dict, repr=False)
44
+
45
+ @dataclass_json(undefined=Undefined.EXCLUDE)
46
+ @dataclass
47
+ class TagOption:
48
+ """
49
+ Represents a tag option for a TV show.
50
+
51
+ Attributes:
52
+ helpText (Optional[str]): The help text for the tag option.
53
+ id (Optional[int]): The ID of the tag option.
54
+ name (Optional[str]): The name of the tag option.
55
+ tag (Optional[int]): The tag of the tag option.
56
+ tagName (Optional[str]): The tag name of the tag option.
57
+ _catchall (dict): A catch-all field for any additional attributes not defined in the class.
58
+ """
59
+ helpText: Optional[str] = None
60
+ id: Optional[int] = None
61
+ name: Optional[str] = None
62
+ tag: Optional[int] = None
63
+ tagName: Optional[str] = None
64
+ _catchall: dict = field(default_factory=dict, repr=False)
65
+
66
+ @dataclass_json(undefined=Undefined.EXCLUDE)
67
+ @dataclass
68
+ class Status:
69
+ """
70
+ Represents the status of a TV show.
71
+
72
+ Attributes:
73
+ id (Optional[int]): The ID of the status.
74
+ name (Optional[str]): The name of the status.
75
+ _catchall (dict): A catch-all dictionary for any additional attributes.
76
+ """
77
+ id: Optional[int] = None
78
+ name: Optional[str] = None
79
+ _catchall: dict = field(default_factory=dict, repr=False)
80
+
81
+ @dataclass_json(undefined=Undefined.EXCLUDE)
82
+ @dataclass
83
+ class Artwork:
84
+ """
85
+ Represents artwork associated with a TV show.
86
+
87
+ Attributes:
88
+ episodeId (Optional[int]): The ID of the episode associated with the artwork.
89
+ height (Optional[int]): The height of the artwork image.
90
+ id (Optional[int]): The ID of the artwork.
91
+ image (Optional[str]): The URL or path to the artwork image.
92
+ includesText (Optional[bool]): Indicates whether the artwork includes text.
93
+ language (Optional[str]): The language of the artwork.
94
+ movieId (Optional[int]): The ID of the movie associated with the artwork.
95
+ networkId (Optional[int]): The ID of the network associated with the artwork.
96
+ peopleId (Optional[int]): The ID of the people associated with the artwork.
97
+ score (Optional[int]): The score of the artwork.
98
+ seasonId (Optional[int]): The ID of the season associated with the artwork.
99
+ seriesId (Optional[int]): The ID of the series associated with the artwork.
100
+ seriesPeopleId (Optional[int]): The ID of the series people associated with the artwork.
101
+ status (Optional[Status]): The status of the artwork.
102
+ tagOptions (Optional[List[TagOption]]): The tag options associated with the artwork.
103
+ thumbnail (Optional[str]): The URL or path to the thumbnail image of the artwork.
104
+ thumbnailHeight (Optional[int]): The height of the thumbnail image.
105
+ thumbnailWidth (Optional[int]): The width of the thumbnail image.
106
+ type (Optional[int]): The type of the artwork.
107
+ updatedAt (Optional[int]): The timestamp of when the artwork was last updated.
108
+ width (Optional[int]): The width of the artwork image.
109
+ _catchall (dict): A catch-all field for any additional attributes not defined in the class.
110
+ """
111
+
112
+ episodeId: Optional[int] = None
113
+ height: Optional[int] = None
114
+ id: Optional[int] = None
115
+ image: Optional[str] = None
116
+ includesText: Optional[bool] = None
117
+ language: Optional[str] = None
118
+ movieId: Optional[int] = None
119
+ networkId: Optional[int] = None
120
+ peopleId: Optional[int] = None
121
+ score: Optional[int] = None
122
+ seasonId: Optional[int] = None
123
+ seriesId: Optional[int] = None
124
+ seriesPeopleId: Optional[int] = None
125
+ status: Optional[Status] = None
126
+ tagOptions: Optional[List[TagOption]] = None
127
+ thumbnail: Optional[str] = None
128
+ thumbnailHeight: Optional[int] = None
129
+ thumbnailWidth: Optional[int] = None
130
+ type: Optional[int] = None
131
+ updatedAt: Optional[int] = None
132
+ width: Optional[int] = None
133
+ _catchall: dict = field(default_factory=dict, repr=False)
134
+
135
+ def __post_init__(self):
136
+ self.status = Status(**self.status) if isinstance(self.status, dict) else self.status
137
+ self.tagOptions = [TagOption(**tagOption) if isinstance(tagOption, dict) else tagOption for tagOption in self.tagOptions] if self.tagOptions else []
138
+
139
+ @dataclass_json(undefined=Undefined.EXCLUDE)
140
+ @dataclass
141
+ class EpisodeInfo:
142
+ """
143
+ Represents information about an episode of a TV show.
144
+
145
+ Attributes:
146
+ image (Optional[str]): The image associated with the episode.
147
+ name (Optional[str]): The name of the episode.
148
+ year (Optional[str]): The year the episode was released.
149
+ _catchall (dict): A catch-all dictionary for any additional attributes.
150
+ """
151
+ image: Optional[str] = None
152
+ name: Optional[str] = None
153
+ year: Optional[str] = None
154
+ _catchall: dict = field(default_factory=dict, repr=False)
155
+
156
+ @dataclass_json(undefined=Undefined.EXCLUDE)
157
+ @dataclass
158
+ class Character:
159
+ """
160
+ Represents a character in a TV show.
161
+
162
+ Attributes:
163
+ aliases (Optional[List[Alias]]): List of aliases for the character.
164
+ episode (Optional[EpisodeInfo]): Information about the episode the character appears in.
165
+ episodeId (Optional[int]): ID of the episode the character appears in.
166
+ id (Optional[int]): ID of the character.
167
+ image (Optional[str]): URL of the character's image.
168
+ isFeatured (Optional[bool]): Indicates if the character is featured.
169
+ movieId (Optional[int]): ID of the movie the character appears in.
170
+ movie (Optional[EpisodeInfo]): Information about the movie the character appears in.
171
+ name (Optional[str]): Name of the character.
172
+ nameTranslations (Optional[List[str]]): List of translated names for the character.
173
+ overviewTranslations (Optional[List[str]]): List of translated overviews for the character.
174
+ peopleId (Optional[int]): ID of the people associated with the character.
175
+ personImgURL (Optional[str]): URL of the person's image associated with the character.
176
+ peopleType (Optional[str]): Type of people associated with the character.
177
+ seriesId (Optional[int]): ID of the TV series the character appears in.
178
+ series (Optional[EpisodeInfo]): Information about the TV series the character appears in.
179
+ sort (Optional[int]): Sort order of the character.
180
+ tagOptions (Optional[List[TagOption]]): List of tag options for the character.
181
+ type (Optional[int]): Type of the character.
182
+ url (Optional[str]): URL of the character.
183
+ personName (Optional[str]): Name of the person associated with the character.
184
+ _catchall (dict): Catch-all field for any additional attributes not defined in the class.
185
+
186
+ Methods:
187
+ __post_init__(): Initializes the class instance and converts nested dictionaries to objects.
188
+ """
189
+ aliases: Optional[List[Alias]] = None
190
+ episode: Optional[EpisodeInfo] = None
191
+ episodeId: Optional[int] = None
192
+ id: Optional[int] = None
193
+ image: Optional[str] = None
194
+ isFeatured: Optional[bool] = None
195
+ movieId: Optional[int] = None
196
+ movie: Optional[EpisodeInfo] = None
197
+ name: Optional[str] = None
198
+ nameTranslations: Optional[List[str]] = None
199
+ overviewTranslations: Optional[List[str]] = None
200
+ peopleId: Optional[int] = None
201
+ personImgURL: Optional[str] = None
202
+ peopleType: Optional[str] = None
203
+ seriesId: Optional[int] = None
204
+ series: Optional[EpisodeInfo] = None
205
+ sort: Optional[int] = None
206
+ tagOptions: Optional[List[TagOption]] = None
207
+ type: Optional[int] = None
208
+ url: Optional[str] = None
209
+ personName: Optional[str] = None
210
+ _catchall: dict = field(default_factory=dict, repr=False)
211
+
212
+ def __post_init__(self):
213
+ self.aliases = [Alias(**alias) if isinstance(alias, dict) else alias for alias in self.aliases] if self.aliases else []
214
+ self.episode = EpisodeInfo(**self.episode) if isinstance(self.episode, dict) else self.episode
215
+ self.movie = EpisodeInfo(**self.movie) if isinstance(self.movie, dict) else self.movie
216
+ self.series = EpisodeInfo(**self.series) if isinstance(self.series, dict) else self.series
217
+ self.tagOptions = [TagOption(**tagOption) if isinstance(tagOption, dict) else tagOption for tagOption in self.tagOptions] if self.tagOptions else []
218
+
219
+ @dataclass_json(undefined=Undefined.EXCLUDE)
220
+ @dataclass
221
+ class ContentRating:
222
+ """
223
+ Represents the content rating of a TV show.
224
+
225
+ Attributes:
226
+ id (Optional[int]): The ID of the content rating.
227
+ name (Optional[str]): The name of the content rating.
228
+ description (Optional[str]): The description of the content rating.
229
+ country (Optional[str]): The country of the content rating.
230
+ contentType (Optional[str]): The type of content the rating applies to.
231
+ order (Optional[int]): The order of the content rating.
232
+ fullName (Optional[str]): The full name of the content rating.
233
+ _catchall (dict): A catch-all field for any additional attributes not defined in the class.
234
+ """
235
+ id: Optional[int] = None
236
+ name: Optional[str] = None
237
+ description: Optional[str] = None
238
+ country: Optional[str] = None
239
+ contentType: Optional[str] = None
240
+ order: Optional[int] = None
241
+ fullName: Optional[str] = None
242
+ _catchall: dict = field(default_factory=dict, repr=False)
243
+
244
+ @dataclass_json(undefined=Undefined.EXCLUDE)
245
+ @dataclass
246
+ class Company:
247
+ """
248
+ Represents a company associated with a TV show.
249
+ """
250
+ activeDate: Optional[str] = None
251
+ aliases: Optional[List[Alias]] = None
252
+ country: Optional[str] = None
253
+ id: Optional[int] = None
254
+ inactiveDate: Optional[str] = None
255
+ name: Optional[str] = None
256
+ nameTranslations: Optional[List[str]] = None
257
+ overviewTranslations: Optional[List[str]] = None
258
+ primaryCompanyType: Optional[int] = None
259
+ slug: Optional[str] = None
260
+ parentCompany: Optional[Status] = None
261
+ tagOptions: Optional[List[TagOption]] = None
262
+ _catchall: dict = field(default_factory=dict, repr=False)
263
+
264
+ def __post_init__(self):
265
+ self.aliases = [Alias(**alias) if isinstance(alias, dict) else alias for alias in self.aliases]
266
+ self.parentCompany = Status(**self.parentCompany) if isinstance(self.parentCompany, dict) else self.parentCompany
267
+ self.tagOptions = [TagOption(**tagOption) if isinstance(tagOption, dict) else tagOption for tagOption in self.tagOptions] if self.tagOptions else []
268
+
269
+ @dataclass_json(undefined=Undefined.EXCLUDE)
270
+ @dataclass
271
+ class Companies:
272
+ """
273
+ Represents the companies associated with a TV show.
274
+ """
275
+ studio: Optional[List[Company]] = None
276
+ network: Optional[List[Company]] = None
277
+ production: Optional[List[Company]] = None
278
+ distributor: Optional[List[Company]] = None
279
+ special_effects: Optional[List[Company]] = None
280
+ _catchall: dict = field(default_factory=dict, repr=False)
281
+
282
+ def __post_init__(self):
283
+ self.studio = [Company(**company) if isinstance(company, dict) else company for company in self.studio] if self.studio else []
284
+ self.network = [Company(**company) if isinstance(company, dict) else company for company in self.network] if self.network else []
285
+ self.production = [Company(**company) if isinstance(company, dict) else company for company in self.production] if self.production else []
286
+ self.distributor = [Company(**company) if isinstance(company, dict) else company for company in self.distributor] if self.distributor else []
287
+ self.special_effects = [Company(**company) if isinstance(company, dict) else company for company in self.special_effects] if self.special_effects else []
288
+
289
+ @dataclass_json(undefined=Undefined.EXCLUDE)
290
+ @dataclass
291
+ class Type:
292
+ """
293
+ Represents the type of a TV show.
294
+ """
295
+ alternateName: Optional[str] = None
296
+ id: Optional[int] = None
297
+ name: Optional[str] = None
298
+ type: Optional[str] = None
299
+ _catchall: dict = field(default_factory=dict, repr=False)
300
+
301
+ @dataclass_json(undefined=Undefined.EXCLUDE)
302
+ @dataclass
303
+ class Season:
304
+ """
305
+ Represents a season of a TV show.
306
+ """
307
+ id: Optional[int] = None
308
+ image: Optional[str] = None
309
+ imageType: Optional[int] = None
310
+ lastUpdated: Optional[str] = None
311
+ name: Optional[str] = None
312
+ nameTranslations: Optional[List[str]] = None
313
+ number: Optional[int] = None
314
+ overviewTranslations: Optional[List[str]] = None
315
+ companies: Optional[Companies] = None
316
+ seriesId: Optional[int] = None
317
+ type: Optional[Type] = None
318
+ year: Optional[str] = None
319
+ _catchall: dict = field(default_factory=dict, repr=False)
320
+
321
+ def __post_init__(self):
322
+ self.companies = Companies(**self.companies) if isinstance(self.companies, dict) else self.companies
323
+ self.type = Type(**self.type) if isinstance(self.type, dict) else self.type
324
+
325
+ @dataclass_json(undefined=Undefined.EXCLUDE)
326
+ @dataclass
327
+ class Episode:
328
+ """
329
+ Represents an episode of a TV show.
330
+ """
331
+ aired: Optional[str] = None
332
+ airsAfterSeason: Optional[int] = None
333
+ airsBeforeEpisode: Optional[int] = None
334
+ airsBeforeSeason: Optional[int] = None
335
+ finaleType: Optional[str] = None
336
+ id: Optional[int] = None
337
+ image: Optional[str] = None
338
+ imageType: Optional[int] = None
339
+ isMovie: Optional[int] = None
340
+ lastUpdated: Optional[str] = None
341
+ linkedMovie: Optional[int] = None
342
+ name: Optional[str] = None
343
+ nameTranslations: Optional[List[str]] = None
344
+ number: Optional[int] = None
345
+ overview: Optional[str] = None
346
+ overviewTranslations: Optional[List[str]] = None
347
+ runtime: Optional[int] = None
348
+ seasonNumber: Optional[int] = None
349
+ seasons: Optional[List[Season]] = None
350
+ seriesId: Optional[int] = None
351
+ seasonName: Optional[str] = None
352
+ year: Optional[str] = None
353
+ _catchall: dict = field(default_factory=dict, repr=False)
354
+
355
+ def __post_init__(self):
356
+ self.seasons = [Season(**season) if isinstance(season, dict) else season for season in self.seasons] if self.seasons else []
357
+
358
+ @dataclass_json(undefined=Undefined.EXCLUDE)
359
+ @dataclass
360
+ class ListAlias:
361
+ """
362
+ Represents an alias for a TV show list.
363
+ """
364
+ language: Optional[str] = None
365
+ name: Optional[str] = None
366
+ _catchall: dict = field(default_factory=dict, repr=False)
367
+
368
+ @dataclass_json(undefined=Undefined.EXCLUDE)
369
+ @dataclass
370
+ class RemoteId:
371
+ """
372
+ Represents a remote ID for a TV show.
373
+ """
374
+ id: Optional[str] = None
375
+ type: Optional[int] = None
376
+ sourceName: Optional[str] = None
377
+ _catchall: dict = field(default_factory=dict, repr=False)
378
+
379
+ @dataclass_json(undefined=Undefined.EXCLUDE)
380
+ @dataclass
381
+ class TvdbList:
382
+ """
383
+ Represents a TV show list.
384
+ """
385
+ aliases: Optional[List[ListAlias]] = None
386
+ id: Optional[int] = None
387
+ image: Optional[str] = None
388
+ imageIsFallback: Optional[bool] = None
389
+ isOfficial: Optional[bool] = None
390
+ name: Optional[str] = None
391
+ nameTranslations: Optional[List[str]] = None
392
+ overview: Optional[str] = None
393
+ overviewTranslations: Optional[List[str]] = None
394
+ remoteIds: Optional[List[RemoteId]] = None
395
+ tags: Optional[List[TagOption]] = None
396
+ score: Optional[int] = None
397
+ url: Optional[str] = None
398
+ _catchall: dict = field(default_factory=dict, repr=False)
399
+
400
+ def __post_init__(self):
401
+ self.aliases = [ListAlias(**alias) if isinstance(alias, dict) else alias for alias in self.aliases]
402
+ self.remoteIds = [RemoteId(**remoteId) if isinstance(remoteId, dict) else remoteId for remoteId in self.remoteIds] if self.remoteIds else []
403
+ self.tags = [TagOption(**tag) if isinstance(tag, dict) else tag for tag in self.tags] if self.tags else []
404
+
405
+ @dataclass_json(undefined=Undefined.EXCLUDE)
406
+ @dataclass
407
+ class Genre:
408
+ id: Optional[int] = None
409
+ name: Optional[str] = None
410
+ slug: Optional[str] = None
411
+ _catchall: dict = field(default_factory=dict, repr=False)
412
+
413
+ @dataclass_json(undefined=Undefined.EXCLUDE)
414
+ @dataclass
415
+ class Translation:
416
+ aliases: Optional[List[str]] = None
417
+ isAlias: Optional[bool] = None
418
+ isPrimary: Optional[bool] = None
419
+ language: Optional[str] = None
420
+ name: Optional[str] = None
421
+ overview: Optional[str] = None
422
+ tagline: Optional[str] = None
423
+ _catchall: dict = field(default_factory=dict, repr=False)
424
+
425
+ @dataclass_json(undefined=Undefined.EXCLUDE)
426
+ @dataclass
427
+ class Translations:
428
+ nameTranslations: Optional[List[Translation]] = None
429
+ overviewTranslations: Optional[List[Translation]] = None
430
+ alias: Optional[List[str]] = None
431
+ _catchall: dict = field(default_factory=dict, repr=False)
432
+
433
+ def __post_init__(self):
434
+ self.nameTranslations = [Translation(**translation) if isinstance(translation, dict) else translation for translation in self.nameTranslations]
435
+ self.overviewTranslations = [Translation(**translation) if isinstance(translation, dict) else translation for translation in self.overviewTranslations]
436
+
437
+ @dataclass_json(undefined=Undefined.EXCLUDE)
438
+ @dataclass
439
+ class Trailer:
440
+ id: Optional[int] = None
441
+ language: Optional[str] = None
442
+ name: Optional[str] = None
443
+ url: Optional[str] = None
444
+ runtime: Optional[int] = None
445
+ _catchall: dict = field(default_factory=dict, repr=False)
446
+
447
+ @dataclass_json(undefined=Undefined.EXCLUDE)
448
+ @dataclass
449
+ class TvdbShow:
450
+ """
451
+ Represents a TV show retrieved from the TVDB API.
452
+
453
+ Attributes:
454
+ abbreviation (Optional[str]): The abbreviation of the show.
455
+ airsDays (Optional[AirsDays]): The days on which the show airs.
456
+ airsTime (Optional[str]): The time at which the show airs.
457
+ aliases (Optional[List[Alias]]): The aliases of the show.
458
+ artworks (Optional[List[Artwork]]): The artworks associated with the show.
459
+ averageRuntime (Optional[int]): The average runtime of the show in minutes.
460
+ characters (Optional[List[Character]]): The characters in the show.
461
+ contentRatings (Optional[List[ContentRating]]): The content ratings of the show.
462
+ country (Optional[str]): The country in which the show is produced.
463
+ defaultSeasonType (Optional[int]): The default season type of the show.
464
+ episodes (Optional[List[Episode]]): The episodes of the show.
465
+ firstAired (Optional[str]): The date on which the show first aired.
466
+ lists (Optional[List[TvdbList]]): The lists associated with the show.
467
+ genres (Optional[List[Genre]]): The genres of the show.
468
+ id (Optional[int]): The ID of the show.
469
+ image (Optional[str]): The image associated with the show.
470
+ isOrderRandomized (Optional[bool]): Indicates if the episode order is randomized.
471
+ lastAired (Optional[str]): The date on which the show last aired.
472
+ lastUpdated (Optional[str]): The date on which the show was last updated.
473
+ name (Optional[str]): The name of the show.
474
+ nameTranslations (Optional[List[str]]): The translations of the show's name.
475
+ companies (Optional[List[Company]]): The companies associated with the show.
476
+ nextAired (Optional[str]): The date on which the next episode airs.
477
+ originalCountry (Optional[str]): The original country of the show.
478
+ originalLanguage (Optional[str]): The original language of the show.
479
+ originalNetwork (Optional[Company]): The original network of the show.
480
+ overview (Optional[str]): The overview of the show.
481
+ latestNetwork (Optional[Company]): The latest network of the show.
482
+ overviewTranslations (Optional[List[str]]): The translations of the show's overview.
483
+ remoteIds (Optional[List[RemoteId]]): The remote IDs associated with the show.
484
+ score (Optional[int]): The score of the show.
485
+ seasons (Optional[List[Season]]): The seasons of the show.
486
+ seasonTypes (Optional[List[Type]]): The types of seasons in the show.
487
+ slug (Optional[str]): The slug of the show.
488
+ status (Optional[Status]): The status of the show.
489
+ tags (Optional[List[TagOption]]): The tags associated with the show.
490
+ trailers (Optional[List[Trailer]]): The trailers of the show.
491
+ translations (Optional[Translations]): The translations of the show.
492
+ year (Optional[str]): The year in which the show was released.
493
+ _catchall (dict): A catch-all field for any additional attributes.
494
+ """
495
+ abbreviation: Optional[str] = None
496
+ airsDays: Optional[AirsDays] = None
497
+ airsTime: Optional[str] = None
498
+ aliases: Optional[List[Alias]] = None
499
+ artworks: Optional[List[Artwork]] = None
500
+ averageRuntime: Optional[int] = None
501
+ characters: Optional[List[Character]] = None
502
+ contentRatings: Optional[List[ContentRating]] = None
503
+ country: Optional[str] = None
504
+ defaultSeasonType: Optional[int] = None
505
+ episodes: Optional[List[Episode]] = None
506
+ firstAired: Optional[str] = None
507
+ lists: Optional[List[TvdbList]] = None
508
+ genres: Optional[List[Genre]] = None
509
+ id: Optional[int] = None
510
+ image: Optional[str] = None
511
+ isOrderRandomized: Optional[bool] = None
512
+ lastAired: Optional[str] = None
513
+ lastUpdated: Optional[str] = None
514
+ name: Optional[str] = None
515
+ nameTranslations: Optional[List[str]] = None
516
+ companies: Optional[List[Company]] = None
517
+ nextAired: Optional[str] = None
518
+ originalCountry: Optional[str] = None
519
+ originalLanguage: Optional[str] = None
520
+ originalNetwork: Optional[Company] = None
521
+ overview: Optional[str] = None
522
+ latestNetwork: Optional[Company] = None
523
+ overviewTranslations: Optional[List[str]] = None
524
+ remoteIds: Optional[List[RemoteId]] = None
525
+ score: Optional[int] = None
526
+ seasons: Optional[List[Season]] = None
527
+ seasonTypes: Optional[List[Type]] = None
528
+ slug: Optional[str] = None
529
+ status: Optional[Status] = None
530
+ tags: Optional[List[TagOption]] = None
531
+ trailers: Optional[List[Trailer]] = None
532
+ translations: Optional[Translations] = None
533
+ year: Optional[str] = None
534
+ _catchall: dict = field(default_factory=dict, repr=False)
535
+
536
+ def __post_init__(self):
537
+ self.airsDays = AirsDays(**self.airsDays) if isinstance(self.airsDays, dict) else self.airsDays
538
+ self.aliases = [Alias(**alias) if isinstance(alias, dict) else alias for alias in self.aliases]
539
+ self.artworks = [Artwork(**artwork) if isinstance(artwork, dict) else artwork for artwork in self.artworks] if self.artworks else []
540
+ self.characters = [Character(**character) if isinstance(character, dict) else character for character in self.characters] if self.characters else []
541
+ self.contentRatings = [ContentRating(**contentRating) if isinstance(contentRating, dict) else contentRating for contentRating in self.contentRatings]
542
+ self.episodes = [Episode(**episode) if isinstance(episode, dict) else episode for episode in self.episodes] if self.episodes else []
543
+ self.lists = [TvdbList(**list) if isinstance(list, dict) else list for list in self.lists]
544
+ self.genres = [Genre(**genre) if isinstance(genre, dict) else genre for genre in self.genres]
545
+ self.companies = [Company(**company) if isinstance(company, dict) else company for company in self.companies]
546
+ self.originalNetwork = Company(**self.originalNetwork) if isinstance(self.originalNetwork, dict) else self.originalNetwork
547
+ self.latestNetwork = Company(**self.latestNetwork) if isinstance(self.latestNetwork, dict) else self.latestNetwork
548
+ self.remoteIds = [RemoteId(**remoteId) if isinstance(remoteId, dict) else remoteId for remoteId in self.remoteIds]
549
+ self.seasons = [Season(**season) if isinstance(season, dict) else season for season in self.seasons]
550
+ self.seasonTypes = [Type(**type) if isinstance(type, dict) else type for type in self.seasonTypes]
551
+ self.status = Status(**self.status) if isinstance(self.status, dict) else self.status
552
+ self.tags = [TagOption(**tag) if isinstance(tag, dict) else tag for tag in self.tags]
553
+ self.trailers = [Trailer(**trailer) if isinstance(trailer, dict) else trailer for trailer in self.trailers]
554
+ self.translations = Translations(**self.translations) if isinstance(self.translations, dict) else self.translations
@@ -0,0 +1,65 @@
1
+ import os
2
+ from tvdb_v4_official import TVDB
3
+ from typing import Union
4
+ from plexflow.core.metadata.providers.tvdb.datatypes import TvdbMovie, movie_from_json, show_from_json
5
+ from plexflow.core.metadata.providers.tvdb.tv_datatypes import TvdbShow
6
+ import json
7
+
8
+ def search_movie_by_imdb(imdb_id: str) -> Union[TvdbMovie, None]:
9
+ """
10
+ Search for a movie in TVDB by IMDB ID.
11
+
12
+ Args:
13
+ imdb_id (str): The IMDB ID of the movie.
14
+
15
+ Returns:
16
+ Union[TvdbMovie, None]: The TvdbMovie object representing the movie if found, None otherwise.
17
+ """
18
+ tvdb = TVDB(os.getenv("TVDB_API_KEY"))
19
+ try:
20
+ response = tvdb.search_by_remote_id(imdb_id)
21
+ if response:
22
+ movie_id = response[0]["movie"]['id']
23
+ movie = tvdb.get_movie_extended(movie_id)
24
+ return movie_from_json(json.dumps(movie))
25
+ else:
26
+ raise ValueError(f"No movie found with IMDB ID {imdb_id}")
27
+ except Exception as e:
28
+ raise e
29
+
30
+ def search_show_by_imdb(imdb_id: str) -> Union[TvdbShow, None]:
31
+ """
32
+ Searches for a TV show in the TVDB database using the IMDB ID.
33
+
34
+ Args:
35
+ imdb_id (str): The IMDB ID of the TV show.
36
+
37
+ Returns:
38
+ Union[TvdbShow, None]: An instance of the TvdbShow class representing the TV show if found,
39
+ otherwise None.
40
+
41
+ Raises:
42
+ ValueError: If no TV show is found with the given IMDB ID.
43
+ Exception: If an error occurs during the search.
44
+
45
+ """
46
+ tvdb = TVDB(os.getenv("TVDB_API_KEY"))
47
+ try:
48
+ response = tvdb.search_by_remote_id(imdb_id)
49
+ if response:
50
+ show_id = response[0]["series"]['id']
51
+ show = tvdb.get_series_extended(show_id, meta="episodes", short=True)
52
+ return TvdbShow.from_dict(show)
53
+ else:
54
+ raise ValueError(f"No TV show found with IMDB ID {imdb_id}")
55
+ except Exception as e:
56
+ raise e
57
+
58
+
59
+ def get_season(season_id: int) -> dict:
60
+ tvdb = TVDB(os.getenv("TVDB_API_KEY"))
61
+ try:
62
+ response = tvdb.get_season_extended(season_id)
63
+ return response
64
+ except Exception as e:
65
+ raise e
File without changes