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,70 @@
1
+ from datetime import datetime
2
+ from plexflow.core.torrents.results.torrent import Torrent
3
+ from plexflow.utils.imdb.imdb_codes import IMDbCode
4
+ from plexflow.utils.torrent.hash import extract_torrent_hash
5
+
6
+ class TorrentQuestSearchResult(Torrent):
7
+ def __init__(self, **kwargs):
8
+ super().__init__()
9
+ self._magnet = kwargs.get('magnet_link')
10
+ self._name = kwargs.get('name')
11
+ self._date = kwargs.get('date')
12
+ self._type = kwargs.get('type')
13
+ self._size = kwargs.get('size_bytes')
14
+ self._seeds = kwargs.get('seeds')
15
+ self._peers = kwargs.get('peers')
16
+ self._files = kwargs.get('files')
17
+ self._link = kwargs.get('link')
18
+ self.src = 'torrentquest'
19
+
20
+ @property
21
+ def source(self) -> str:
22
+ return self.src
23
+
24
+ @property
25
+ def magnet(self) -> str:
26
+ return self._magnet
27
+
28
+ @property
29
+ def date(self) -> datetime:
30
+ return self._date
31
+
32
+ @property
33
+ def seeds(self) -> int:
34
+ return self._seeds
35
+
36
+ @property
37
+ def peers(self) -> int:
38
+ return self._peers
39
+
40
+ @property
41
+ def n_files(self) -> int:
42
+ return self._files
43
+
44
+ @property
45
+ def size_bytes(self) -> int:
46
+ return self._size
47
+
48
+ @property
49
+ def imdb_code(self) -> IMDbCode:
50
+ return None
51
+
52
+ @property
53
+ def release_name(self) -> str:
54
+ return self._name
55
+
56
+ @property
57
+ def hash(self) -> str:
58
+ return extract_torrent_hash(self._magnet)
59
+
60
+ @property
61
+ def uploader(self) -> str:
62
+ return None
63
+
64
+ @property
65
+ def url(self) -> str:
66
+ return self._link
67
+
68
+ @property
69
+ def category(self) -> str:
70
+ return self._type
File without changes
@@ -0,0 +1,17 @@
1
+ from plexflow.utils.api.rest.restful import Restful
2
+ from plexflow.core.torrents.providers.tpb.utils import TPBSearchResult
3
+
4
+ class TPB(Restful):
5
+ def __init__(self, base_url: str = 'https://apibay.org'):
6
+ super().__init__(base_url=base_url)
7
+
8
+ def search(self, query: str):
9
+ response = self.get('/q.php', query_params={
10
+ 'q': query,
11
+ })
12
+
13
+ response.raise_for_status()
14
+
15
+ data = response.json()
16
+
17
+ return list(map(lambda x: TPBSearchResult(**x), data))
@@ -0,0 +1,139 @@
1
+ from datetime import datetime
2
+ from plexflow.core.torrents.results.torrent import Torrent
3
+ from plexflow.utils.imdb.imdb_codes import IMDbCode
4
+
5
+ class TPBSearchResult(Torrent):
6
+ added: str
7
+ category: str
8
+ id: str
9
+ imdb: str
10
+ info_hash: str
11
+ leechers: str
12
+ name: str
13
+ num_files: str
14
+ seeders: str
15
+ size: str
16
+ status: str
17
+ username: str
18
+
19
+ def __init__(self, **kwargs):
20
+ super().__init__()
21
+ self.added = kwargs.get("added")
22
+ self.category = kwargs.get("category")
23
+ self.id = kwargs.get("id")
24
+ self.imdb = kwargs.get("imdb")
25
+ self.info_hash = kwargs.get("info_hash")
26
+ self.leechers = kwargs.get("leechers")
27
+ self.name = kwargs.get("name")
28
+ self.num_files = kwargs.get("num_files")
29
+ self.seeders = kwargs.get("seeders")
30
+ self.size = kwargs.get("size")
31
+ self.status = kwargs.get("status")
32
+ self.username = kwargs.get("username")
33
+ self.src = "tpb"
34
+
35
+ @property
36
+ def magnet(self) -> str:
37
+ return f'magnet:?xt=urn:btih:{self.info_hash}'
38
+
39
+ @property
40
+ def date(self) -> datetime:
41
+ return datetime.fromtimestamp(int(self.added))
42
+
43
+ @property
44
+ def seeds(self) -> int:
45
+ return int(self.seeders)
46
+
47
+ @property
48
+ def peers(self) -> int:
49
+ return int(self.leechers)
50
+
51
+ @property
52
+ def n_files(self) -> int:
53
+ return int(self.num_files)
54
+
55
+ @property
56
+ def size_bytes(self) -> int:
57
+ return int(self.size)
58
+
59
+ @property
60
+ def imdb_code(self) -> IMDbCode:
61
+ return IMDbCode(self.imdb)
62
+
63
+ @property
64
+ def release_name(self) -> str:
65
+ return self.name
66
+
67
+ @property
68
+ def hash(self) -> str:
69
+ return self.info_hash
70
+
71
+ @property
72
+ def uploader(self) -> str:
73
+ return self.username
74
+
75
+ @property
76
+ def torrent_id(self) -> str:
77
+ return self.id
78
+
79
+ @property
80
+ def url(self) -> str:
81
+ return f'https://apibay.org/t.php?id={self.id}'
82
+
83
+ @property
84
+ def category_name(self) -> str:
85
+ categories = {
86
+ '100': 'Audio',
87
+ '101': 'Music',
88
+ '102': 'Audio books',
89
+ '103': 'Sound clips',
90
+ '104': 'FLAC',
91
+ '199': 'Other',
92
+ '200': 'Video',
93
+ '201': 'Movies',
94
+ '202': 'Movies DVDR',
95
+ '203': 'Music videos',
96
+ '204': 'Movie clips',
97
+ '205': 'TV shows',
98
+ '206': 'Handheld',
99
+ '207': 'HD - Movies',
100
+ '208': 'HD - TV shows',
101
+ '209': '3D',
102
+ '299': 'Other',
103
+ '300': 'Applications',
104
+ '301': 'Windows',
105
+ '302': 'Mac',
106
+ '303': 'UNIX',
107
+ '304': 'Handheld',
108
+ '305': 'IOS (iPad/iPhone)',
109
+ '306': 'Android',
110
+ '399': 'Other',
111
+ '400': 'Games',
112
+ '401': 'PC',
113
+ '402': 'Mac',
114
+ '403': 'PSx',
115
+ '404': 'XBOX360',
116
+ '405': 'Wii',
117
+ '406': 'Handheld',
118
+ '407': 'IOS (iPad/iPhone)',
119
+ '408': 'Android',
120
+ '499': 'Other',
121
+ '500': 'Porn',
122
+ '501': 'Movies',
123
+ '502': 'Movies DVDR',
124
+ '503': 'Pictures',
125
+ '504': 'Games',
126
+ '505': 'HD - Movies',
127
+ '506': 'Movie clips',
128
+ '599': 'Other',
129
+ '600': 'Other',
130
+ '601': 'E-books',
131
+ '602': 'Comics',
132
+ '603': 'Pictures',
133
+ '604': 'Covers',
134
+ '605': 'Physibles',
135
+ '699': 'Other'
136
+ }
137
+ return categories.get(self.category, 'Unknown')
138
+
139
+
File without changes
@@ -0,0 +1,57 @@
1
+ from datetime import datetime
2
+ from plexflow.core.torrents.results.torrent import Torrent
3
+ from plexflow.utils.imdb.imdb_codes import IMDbCode
4
+
5
+ class YTSSearchResult(Torrent):
6
+ def __init__(self, **kwargs):
7
+ super().__init__()
8
+ self._url = kwargs.get("url")
9
+ self._hash = kwargs.get("hash")
10
+ self._quality = kwargs.get("quality")
11
+ self._type = kwargs.get("type")
12
+ self._is_repack = kwargs.get("is_repack")
13
+ self._video_codec = kwargs.get("video_codec")
14
+ self._bit_depth = kwargs.get("bit_depth")
15
+ self._audio_channels = kwargs.get("audio_channels")
16
+ self._seeds = kwargs.get("seeds")
17
+ self._peers = kwargs.get("peers")
18
+ self._size = kwargs.get("size")
19
+ self._size_bytes = kwargs.get("size_bytes")
20
+ self._date_uploaded = kwargs.get("date_uploaded")
21
+ self._name = kwargs.get("name")
22
+ self._imdb_code = kwargs.get("imdb_code")
23
+ self._date_uploaded_unix = kwargs.get("date_uploaded_unix")
24
+ self.src = "yts"
25
+
26
+ @property
27
+ def magnet(self) -> str:
28
+ return f'magnet:?xt=urn:btih:{self._hash}'
29
+
30
+ @property
31
+ def date(self) -> datetime:
32
+ return datetime.fromtimestamp(int(self._date_uploaded_unix))
33
+
34
+ @property
35
+ def seeds(self) -> int:
36
+ return self._seeds
37
+
38
+ @property
39
+ def peers(self) -> int:
40
+ return self._peers
41
+
42
+ @property
43
+ def size_bytes(self) -> int:
44
+ return self._size_bytes
45
+
46
+ @property
47
+ def imdb_code(self) -> IMDbCode:
48
+ return IMDbCode(self._imdb_code)
49
+
50
+ @property
51
+ def release_name(self) -> str:
52
+ return self._name
53
+
54
+ @property
55
+ def hash(self) -> str:
56
+ return self._hash
57
+
@@ -0,0 +1,31 @@
1
+ from plexflow.utils.api.rest.restful import Restful
2
+ from plexflow.core.torrents.providers.yts.utils import YTSSearchResult
3
+
4
+ class YTS(Restful):
5
+ def __init__(self, base_url: str = 'https://yts.mx'):
6
+ super().__init__(base_url=base_url)
7
+
8
+ def search(self, query: str):
9
+ response = self.get(url='/api/v2/list_movies.json', query_params={
10
+ 'query_term': query,
11
+ })
12
+
13
+ response.raise_for_status()
14
+
15
+ data = response.json()
16
+
17
+ data = data.get("data", {})
18
+ movies = data.get("movies", [])
19
+
20
+ results = []
21
+
22
+ for m in movies:
23
+ torrents = m.get("torrents", [])
24
+ torrents = map(lambda t: YTSSearchResult(**{
25
+ **t,
26
+ "name": "_".join(filter(lambda p: p, [m.get("slug"), t.get("type"), t.get("quality"), t.get("video_codec")])) + "-YTS",
27
+ "imdb_code": m.get("imdb_code")
28
+ }), torrents)
29
+ results.extend(torrents)
30
+
31
+ return results
File without changes
@@ -0,0 +1,165 @@
1
+ from abc import ABC, abstractmethod
2
+ from datetime import datetime
3
+ import PTN
4
+ from typing import List, Union
5
+ from plexflow.utils.imdb.imdb_codes import IMDbCode
6
+
7
+ class Torrent(ABC):
8
+ """
9
+ This is an abstract base class that represents a Torrent.
10
+ Any class that inherits from this must implement all the properties.
11
+ """
12
+
13
+ @property
14
+ @abstractmethod
15
+ def seeds(self) -> int:
16
+ """
17
+ This property represents the number of seeds for the torrent.
18
+ It must be implemented by any class that inherits from this class.
19
+ """
20
+ pass
21
+
22
+ @property
23
+ @abstractmethod
24
+ def peers(self) -> int:
25
+ """
26
+ This property represents the number of peers for the torrent.
27
+ It must be implemented by any class that inherits from this class.
28
+ """
29
+ pass
30
+
31
+ @property
32
+ @abstractmethod
33
+ def release_name(self) -> str:
34
+ """
35
+ This property represents the release name of the torrent.
36
+ It must be implemented by any class that inherits from this class.
37
+ """
38
+ pass
39
+
40
+ @property
41
+ @abstractmethod
42
+ def size_bytes(self) -> int:
43
+ """
44
+ This property represents the size of the torrent in bytes.
45
+ It must be implemented by any class that inherits from this class.
46
+ """
47
+ pass
48
+
49
+ @property
50
+ @abstractmethod
51
+ def magnet(self) -> str:
52
+ """
53
+ This property represents the magnet link of the torrent.
54
+ It must be implemented by any class that inherits from this class.
55
+ """
56
+ pass
57
+
58
+ @property
59
+ @abstractmethod
60
+ def hash(self) -> str:
61
+ """
62
+ This property represents the hash of the torrent.
63
+ It must be implemented by any class that inherits from this class.
64
+ """
65
+ pass
66
+
67
+ @property
68
+ def uploader(self) -> str:
69
+ """
70
+ This property represents the uploader of the torrent.
71
+ It must be implemented by any class that inherits from this class.
72
+ """
73
+ return self.source
74
+
75
+ @property
76
+ @abstractmethod
77
+ def date(self) -> datetime:
78
+ """
79
+ This property represents the date of the torrent upload.
80
+ It must be implemented by any class that inherits from this class.
81
+ """
82
+ pass
83
+
84
+ @property
85
+ @abstractmethod
86
+ def imdb_code(self) -> IMDbCode:
87
+ """
88
+ This property represents the IMDB code of the torrent.
89
+ It must be implemented by any class that inherits from this class.
90
+ """
91
+ pass
92
+
93
+ @property
94
+ def source(self) -> str:
95
+ """
96
+ This property represents the source of the torrent.
97
+ It must be implemented by any class that inherits from this class.
98
+ """
99
+ return self.src
100
+
101
+ @property
102
+ def torrent_id(self) -> str:
103
+ """
104
+ This property represents the id of the torrent.
105
+ It must be implemented by any class that inherits from this class.
106
+ """
107
+ return self.source + "_" + self.hash
108
+
109
+ @property
110
+ def parsed_release_name(self) -> dict:
111
+ return PTN.parse(self.release_name)
112
+
113
+ @property
114
+ def encoder_name(self) -> str:
115
+ parts = self.parsed_release_name
116
+ return parts.get("encoder")
117
+
118
+ @property
119
+ def season(self) -> Union[int, List[int]]:
120
+ parts = self.parsed_release_name
121
+ return parts.get("season")
122
+
123
+ @property
124
+ def episode(self) -> Union[int, List[int]]:
125
+ parts = self.parsed_release_name
126
+ return parts.get("episode")
127
+
128
+ @property
129
+ def has_multiple_episodes(self):
130
+ tmp = self.episode
131
+ return isinstance(tmp, list) and len(tmp) > 1
132
+
133
+ @property
134
+ def has_multiple_seasons(self):
135
+ tmp = self.season
136
+ return isinstance(tmp, list) and len(tmp) > 1
137
+
138
+ @property
139
+ def title(self) -> str:
140
+ parts = self.parsed_release_name
141
+ return parts.get("title")
142
+
143
+ @property
144
+ def year(self) -> int:
145
+ parts = self.parsed_release_name
146
+ return parts.get("year")
147
+
148
+ @property
149
+ def quality(self) -> str:
150
+ parts = self.parsed_release_name
151
+ return parts.get("quality")
152
+
153
+ @property
154
+ def is_bad_quality(self):
155
+ parsed_title = self.parsed_release_name
156
+ bad_qualities = ['CAM', 'HDCAM', 'TS', 'HDTS', 'TELESYNC',
157
+ 'PDVD', 'PREDVDRIP', 'WP', 'WORKPRINT', 'TC',
158
+ 'HDTC', 'TELECINE', 'PPV', 'PPVRIP', 'SCR', 'SCREENER',
159
+ 'DVDSCR', 'DVDSCREENER', 'BDSCR', 'WEBSCREENER', 'DDC',
160
+ 'R5', 'R5.LINE', 'R5.AC3.5.1.HQ']
161
+ return parsed_title.get('quality', '').upper() in bad_qualities
162
+
163
+ @property
164
+ def url(self) -> str:
165
+ return None