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,66 @@
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 ExtraTorrentSearchResult(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._link = kwargs.get('link')
17
+ self._uploader = kwargs.get('uploader')
18
+ self.src = 'extratorrent'
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 size_bytes(self) -> int:
42
+ return self._size
43
+
44
+ @property
45
+ def imdb_code(self) -> IMDbCode:
46
+ return None
47
+
48
+ @property
49
+ def release_name(self) -> str:
50
+ return self._name
51
+
52
+ @property
53
+ def hash(self) -> str:
54
+ return extract_torrent_hash(self._magnet)
55
+
56
+ @property
57
+ def uploader(self) -> str:
58
+ return self._uploader
59
+
60
+ @property
61
+ def url(self) -> str:
62
+ return self._link
63
+
64
+ @property
65
+ def category(self) -> str:
66
+ return self._type
File without changes
@@ -0,0 +1,47 @@
1
+ from plexflow.utils.api.rest.restful import Restful
2
+ from plexflow.core.torrents.providers.eztv.utils import EZTVSearchResult
3
+
4
+ class EZTV(Restful):
5
+ """EZTV class to interact with the EZTV API.
6
+
7
+ This class inherits from the Restful class and provides an interface to
8
+ interact with the EZTV API. It provides a method to search for torrents
9
+ using an IMDb ID.
10
+
11
+ Attributes:
12
+ http_conn_id (str): The connection ID for the HTTP connection.
13
+ config_folder (str): The folder where the configuration files are stored.
14
+ """
15
+
16
+ def __init__(self, base_url: str = 'https://eztv.re'):
17
+ """Initializes EZTV with the given HTTP connection ID and config folder."""
18
+ super().__init__(base_url=base_url)
19
+
20
+ def search(self, imdb_id: str):
21
+ """Searches for torrents using an IMDb ID.
22
+
23
+ This method takes an IMDb ID as input, normalizes it by removing the
24
+ leading 'tt' and any leading zeros, and then makes a GET request to
25
+ the EZTV API to search for torrents. It raises an exception if the
26
+ response status is not OK, and returns a list of EZTVSearchResult
27
+ objects if the response is OK.
28
+
29
+ Args:
30
+ imdb_id (str): The IMDb ID to search for.
31
+
32
+ Returns:
33
+ list[EZTVSearchResult]: A list of EZTVSearchResult objects.
34
+ """
35
+ # Normalize the imdb_id by removing leading 'tt' and zeros
36
+ imdb_id = imdb_id.lstrip('tt')
37
+
38
+ response = self.get(url='/api/get-torrents', query_params={
39
+ 'imdb_id': imdb_id,
40
+ })
41
+
42
+ response.raise_for_status()
43
+
44
+ # TODO pagination
45
+ data = response.json()
46
+
47
+ return list(map(lambda x: EZTVSearchResult(**x), data.get("torrents", [])))
@@ -0,0 +1,83 @@
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 EZTVSearchResult(Torrent):
6
+ """EZTVSearchResult class to handle the data structure returned by the EZTV API.
7
+
8
+ This class inherits from the Torrent class and provides an interface to
9
+ interact with the EZTV API. It provides a method to search for torrents
10
+ using an IMDb ID.
11
+
12
+ Attributes:
13
+ id (str): The ID of the torrent.
14
+ hash (str): The hash of the torrent.
15
+ filename (str): The filename of the torrent.
16
+ torrent_url (str): The URL of the torrent.
17
+ magnet_url (str): The magnet URL of the torrent.
18
+ title (str): The title of the torrent.
19
+ imdb_id (str): The IMDb ID of the torrent.
20
+ season (str): The season of the torrent.
21
+ episode (str): The episode of the torrent.
22
+ small_screenshot (str): The small screenshot of the torrent.
23
+ large_screenshot (str): The large screenshot of the torrent.
24
+ seeds (int): The number of seeds of the torrent.
25
+ peers (int): The number of peers of the torrent.
26
+ date_released_unix (int): The release date of the torrent in Unix time.
27
+ size_bytes (int): The size of the torrent in bytes.
28
+ """
29
+
30
+ def __init__(self, **kwargs):
31
+ super().__init__()
32
+ self.id = kwargs.get("id")
33
+ self._hash = kwargs.get("hash")
34
+ self.filename = kwargs.get("filename")
35
+ self.torrent_url = kwargs.get("torrent_url")
36
+ self.magnet_url = kwargs.get("magnet_url")
37
+ self._title = kwargs.get("title")
38
+ self.imdb_id = kwargs.get("imdb_id")
39
+ self._season = kwargs.get("season")
40
+ self._episode = kwargs.get("episode")
41
+ self.small_screenshot = kwargs.get("small_screenshot")
42
+ self.large_screenshot = kwargs.get("large_screenshot")
43
+ self._seeds = kwargs.get("seeds")
44
+ self._peers = kwargs.get("peers")
45
+ self.date_released_unix = kwargs.get("date_released_unix")
46
+ self._size_bytes = kwargs.get("size_bytes")
47
+ self.src = "eztv"
48
+
49
+ @property
50
+ def magnet(self) -> str:
51
+ return self.magnet_url
52
+
53
+ @property
54
+ def hash(self) -> str:
55
+ return self._hash
56
+
57
+ @property
58
+ def peers(self) -> str:
59
+ return self._peers
60
+
61
+ @property
62
+ def seeds(self) -> str:
63
+ return self._seeds
64
+
65
+ @property
66
+ def size_bytes(self) -> str:
67
+ return int(self._size_bytes)
68
+
69
+ @property
70
+ def date(self) -> datetime:
71
+ return datetime.fromtimestamp(self.date_released_unix)
72
+
73
+ @property
74
+ def imdb_code(self) -> IMDbCode:
75
+ return IMDbCode(self.imdb_id)
76
+
77
+ @property
78
+ def release_name(self) -> str:
79
+ return self._title
80
+
81
+ @property
82
+ def torrent_id(self) -> str:
83
+ return str(self.id)
File without changes
@@ -0,0 +1,19 @@
1
+ import requests
2
+ from dataclasses import dataclass
3
+ import json
4
+ from plexflow.utils.api.rest.restful import Restful
5
+ from plexflow.core.torrents.providers.rarbg2.utils import RARBG2SearchResult
6
+
7
+ class RARBG2(Restful):
8
+ def __init__(self, http_conn_id: str = 'rarbg2_hook', config_folder: str = 'config'):
9
+ super().__init__(http_conn_id=http_conn_id, config_folder=config_folder)
10
+
11
+ def search(self, query: str):
12
+ # query params does not work due to odd values in q parameter
13
+ response = self.get(f'/api.php?url=/q.php?q={query}')
14
+
15
+ response.raise_for_status()
16
+
17
+ data = response.json()
18
+
19
+ return list(map(lambda x: RARBG2SearchResult(**x), data))
@@ -0,0 +1,76 @@
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 RARBG2SearchResult(Torrent):
6
+ id: str
7
+ name: str
8
+ info_hash: str
9
+ leechers: str
10
+ seeders: str
11
+ num_files: str
12
+ size: str
13
+ username: str
14
+ added: str
15
+ status: str
16
+ category: str
17
+ imdb: str
18
+
19
+ def __init__(self, **kwargs):
20
+ self.id = kwargs.get("id")
21
+ self.name = kwargs.get("name")
22
+ self.info_hash = kwargs.get("info_hash")
23
+ self.leechers = kwargs.get("leechers")
24
+ self.seeders = kwargs.get("seeders")
25
+ self.num_files = kwargs.get("num_files")
26
+ self.size = kwargs.get("size")
27
+ self.username = kwargs.get("username")
28
+ self.added = kwargs.get("added")
29
+ self.status = kwargs.get("status")
30
+ self.category = kwargs.get("category")
31
+ self.imdb = kwargs.get("imdb")
32
+ self.src = "rarbg2"
33
+
34
+ @property
35
+ def magnet(self) -> str:
36
+ return f'magnet:?xt=urn:btih:{self.info_hash}'
37
+
38
+ @property
39
+ def date(self) -> datetime:
40
+ return datetime.fromtimestamp(int(self.added))
41
+
42
+ @property
43
+ def seeds(self) -> int:
44
+ return int(self.seeders)
45
+
46
+ @property
47
+ def peers(self) -> int:
48
+ return int(self.leechers)
49
+
50
+ @property
51
+ def n_files(self) -> int:
52
+ return int(self.num_files)
53
+
54
+ @property
55
+ def size_bytes(self) -> int:
56
+ return int(self.size)
57
+
58
+ @property
59
+ def imdb_code(self) -> IMDbCode:
60
+ return IMDbCode(self.imdb)
61
+
62
+ @property
63
+ def hash(self) -> str:
64
+ return self.info_hash
65
+
66
+ @property
67
+ def release_name(self) -> str:
68
+ return self.name
69
+
70
+ @property
71
+ def uploader(self) -> str:
72
+ return self.username
73
+
74
+ @property
75
+ def torrent_id(self) -> str:
76
+ return self.id
File without changes
@@ -0,0 +1,36 @@
1
+ from plexflow.utils.api.rest.restful import Restful
2
+ from plexflow.core.torrents.providers.snowfl.utils import SnowflSearchResult
3
+ import urllib.parse
4
+
5
+ class Snowfl(Restful):
6
+ def __init__(self, base_url: str = 'https://snowfl.com'):
7
+ super().__init__(base_url=base_url)
8
+
9
+ def search(self, query: str):
10
+ # lets use quote_plus to encode the query
11
+ encoded_query = urllib.parse.quote(query)
12
+ response = self.get(
13
+ url=f'/jZEQIcyiahCKmuKDzRzbwkSbyQHFzQnLTWnocRed/{encoded_query}/40d5GE72/1/SEED/NONE/1',
14
+ headers={
15
+ 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36',
16
+ 'x-requested-with': 'XMLHttpRequest'
17
+ }
18
+ )
19
+
20
+ response.raise_for_status()
21
+
22
+ data = response.json()
23
+
24
+ if isinstance(data, list):
25
+ return list(map(lambda t: SnowflSearchResult(**t), data))
26
+
27
+
28
+ if __name__ == '__main__':
29
+ snowfl = Snowfl()
30
+ torrents = snowfl.search('Twisters 2024')
31
+
32
+ print(len(torrents), "torrents found")
33
+
34
+ for t in torrents:
35
+ print(t.release_name, t.url)
36
+
@@ -0,0 +1,59 @@
1
+ from datetime import datetime
2
+ from plexflow.core.torrents.results.torrent import Torrent
3
+ from plexflow.utils.imdb.imdb_codes import IMDbCode
4
+ import dateparser
5
+ from plexflow.utils.strings.filesize import parse_size
6
+ from plexflow.utils.torrent.hash import extract_torrent_hash
7
+
8
+ class SnowflSearchResult(Torrent):
9
+ def __init__(self, **kwargs):
10
+ super().__init__()
11
+ self._url = kwargs.get("url")
12
+ self._magnet = kwargs.get("magnet")
13
+ self._type = kwargs.get("type")
14
+ self._seeds = kwargs.get("seeder")
15
+ self._peers = kwargs.get("leecher")
16
+ self._size = kwargs.get("size")
17
+ self._size_bytes = next(iter(parse_size(self._size)), None)
18
+ self._age = kwargs.get("age")
19
+ self._date = dateparser.parse(f"{self._age} ago")
20
+ self._name = kwargs.get("name")
21
+ self.src = "snowfl"
22
+
23
+ @property
24
+ def magnet(self) -> str:
25
+ return self._magnet
26
+
27
+ @property
28
+ def date(self) -> datetime:
29
+ return self._date
30
+
31
+ @property
32
+ def seeds(self) -> int:
33
+ return self._seeds
34
+
35
+ @property
36
+ def peers(self) -> int:
37
+ return self._peers
38
+
39
+ @property
40
+ def size_bytes(self) -> int:
41
+ return self._size_bytes
42
+
43
+ @property
44
+ def imdb_code(self) -> IMDbCode:
45
+ return None
46
+
47
+ @property
48
+ def release_name(self) -> str:
49
+ return self._name
50
+
51
+ @property
52
+ def hash(self) -> str:
53
+ return extract_torrent_hash(self._magnet)
54
+
55
+ @property
56
+ def url(self) -> str:
57
+ return self._url
58
+
59
+
File without changes
@@ -0,0 +1,50 @@
1
+ from playwright.sync_api import sync_playwright
2
+ import time
3
+ from typing import List, Optional
4
+ from pydantic import BaseModel
5
+
6
+ class TorrentGalaxyContext(BaseModel):
7
+ cookies: Optional[List[dict]]
8
+ screenshot_bytes: Optional[bytes]
9
+ url: str
10
+ headless: bool = True
11
+ domain: str
12
+ torrent_id: str
13
+ wait_seconds: int
14
+
15
+
16
+ def get_request_context(headless: bool = True,
17
+ domain: str = "torrentgalaxy.to",
18
+ torrent_id: str = "16100045",
19
+ wait_seconds: int = 10) -> TorrentGalaxyContext:
20
+ with sync_playwright() as p:
21
+ browser = p.chromium.launch(headless=headless)
22
+ context = browser.new_context()
23
+ page = context.new_page()
24
+
25
+ # Navigate to the URL
26
+ url = f"https://{domain}/torrent/{torrent_id}"
27
+ page.goto(url)
28
+
29
+ # Wait for the page to load completely
30
+ time.sleep(wait_seconds)
31
+
32
+ # Take a screenshot and get the image bytes
33
+ page.set_viewport_size({"width": 1920, "height": 1080})
34
+ screenshot_bytes = page.screenshot(full_page=True)
35
+
36
+ # Retrieve cookies
37
+ cookies = context.cookies()
38
+
39
+ # Close the browser
40
+ browser.close()
41
+
42
+ return TorrentGalaxyContext(
43
+ cookies=cookies,
44
+ screenshot_bytes=screenshot_bytes,
45
+ url=url,
46
+ headless=headless,
47
+ domain=domain,
48
+ torrent_id=torrent_id,
49
+ wait_seconds=wait_seconds
50
+ )
@@ -0,0 +1,40 @@
1
+ import pandas as pd
2
+ import re
3
+ from typing import Optional
4
+ from plexflow.utils.download.gz import download_and_extract_gz
5
+
6
+ ID_PATTERN = re.compile(r'/torrent/(\d+)/', re.IGNORECASE)
7
+
8
+ def extract_id(url: str) -> Optional[str]:
9
+ """
10
+ Extracts the ID from the given URL using a regular expression.
11
+
12
+ Args:
13
+ url (str): The URL from which to extract the ID.
14
+
15
+ Returns:
16
+ str: The extracted ID if found, otherwise None.
17
+ """
18
+ match = ID_PATTERN.search(url)
19
+ return match.group(1) if match else None
20
+
21
+ def read_and_transform_dump(url: str, output_filename: str) -> pd.DataFrame:
22
+ """
23
+ Downloads, extracts, and transforms a gzipped dump file into a DataFrame.
24
+
25
+ This function downloads a gzipped dump file from the specified URL, extracts it,
26
+ and reads the data into a DataFrame. It then renames the columns and adds a new
27
+ column 'id' by extracting the ID from the 'url' column.
28
+
29
+ Args:
30
+ url (str): The URL of the gzipped dump file to download.
31
+ output_filename (str): The filename for the extracted file.
32
+
33
+ Returns:
34
+ pd.DataFrame: The transformed DataFrame.
35
+ """
36
+ download_and_extract_gz(url, output_filename)
37
+
38
+ df = pd.read_csv(output_filename, sep='|', header=None, names=["hash", "name", "category", "url", "torrent"])
39
+ df["id"] = df.url.apply(extract_id)
40
+ return df
@@ -0,0 +1,22 @@
1
+ from plexflow.utils.api.rest.restful import Restful
2
+ from plexflow.core.torrents.providers.tgx.utils import TGXSearchResult
3
+
4
+ class TGX(Restful):
5
+ def __init__(self, postgres_conn_id: str = 'postgresql_hook', config_folder: str = 'config'):
6
+ super().__init__(postgres_conn_id=postgres_conn_id, config_folder=config_folder)
7
+
8
+ def search(self, imdb: str):
9
+ rows = self.get_all("""
10
+ SELECT
11
+ *
12
+ FROM tgx.results
13
+ WHERE
14
+ imdb = %(imdb)s
15
+ AND
16
+ category IN ('movies', 'tv')
17
+ LIMIT 1000
18
+ """, params={
19
+ "imdb": imdb,
20
+ })
21
+
22
+ return list(map(lambda x: TGXSearchResult(**x), rows))
@@ -0,0 +1,61 @@
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 TGXSearchResult(Torrent):
6
+ def __init__(self, **kwargs):
7
+ super().__init__()
8
+ self._id = kwargs.get("id")
9
+ self._name = kwargs.get("name")
10
+ self._category = kwargs.get("category")
11
+ self._sub_category = kwargs.get("sub_category")
12
+ self._date_added = kwargs.get("date_added")
13
+ self._uploader = kwargs.get("uploader")
14
+ self._peers = kwargs.get("peers")
15
+ self._seeds = kwargs.get("seeds")
16
+ self._imdb = kwargs.get("imdb")
17
+ self._imdb_link = kwargs.get("imdb_link")
18
+ self._magnet = kwargs.get("magnet")
19
+ self._hash = kwargs.get("hash")
20
+ self._language = kwargs.get("language")
21
+ self._subtitles = kwargs.get("subtitles")
22
+ self._deleted = kwargs.get("deleted")
23
+ self._errored = kwargs.get("errored")
24
+ self._date_last_scrape = kwargs.get("date_last_scrape")
25
+ self._size_bytes = kwargs.get("size_bytes")
26
+ self.src = "tgx"
27
+
28
+ @property
29
+ def magnet(self) -> str:
30
+ return self._magnet
31
+
32
+ @property
33
+ def date(self) -> datetime:
34
+ parts = self._date_added.split(" ")
35
+ if len(parts) > 0:
36
+ return datetime.strptime(parts[0], "%d-%m-%Y")
37
+
38
+ @property
39
+ def seeds(self) -> int:
40
+ return self._seeds
41
+
42
+ @property
43
+ def peers(self) -> int:
44
+ return self._peers
45
+
46
+ @property
47
+ def size_bytes(self) -> int:
48
+ return round(self._size_bytes)
49
+
50
+ @property
51
+ def imdb_code(self) -> IMDbCode:
52
+ return IMDbCode(self._imdb)
53
+
54
+ @property
55
+ def release_name(self) -> str:
56
+ return self._name
57
+
58
+ @property
59
+ def hash(self) -> str:
60
+ return self._hash
61
+
File without changes
@@ -0,0 +1,17 @@
1
+ from plexflow.utils.api.rest.antibot_restful import AntibotRestful
2
+ from plexflow.utils.api.rest.restful import Restful
3
+ from plexflow.core.torrents.providers.therarbg.utils import TheRarbgSearchResult
4
+ from plexflow.utils.torrent.extract.therarbg import extract_torrent_results
5
+ from typing import List
6
+
7
+ class TheRarbg(Restful):
8
+ def __init__(self, base_url: str = 'https://therarbg.com'):
9
+ super().__init__(base_url=base_url)
10
+
11
+ def search(self, query: str) -> List[TheRarbgSearchResult]:
12
+ response = self.get(f'/get-posts/keywords:{query}/')
13
+
14
+ response.raise_for_status
15
+
16
+ data = extract_torrent_results(html=response.text)
17
+ return list(map(lambda t: TheRarbgSearchResult(**t), data))
@@ -0,0 +1,61 @@
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 TheRarbgSearchResult(Torrent):
7
+ def __init__(self, **kwargs):
8
+ super().__init__()
9
+ self._name = kwargs.get('name')
10
+ self._date = kwargs.get('date')
11
+ self._type = kwargs.get('type')
12
+ self._size = kwargs.get('size_bytes')
13
+ self._seeds = kwargs.get('seeds')
14
+ self._peers = kwargs.get('peers')
15
+ self._link = kwargs.get('link')
16
+ self._imdb = kwargs.get('imdb')
17
+ self.src = 'therarbg'
18
+
19
+ @property
20
+ def source(self) -> str:
21
+ return self.src
22
+
23
+ @property
24
+ def magnet(self) -> str:
25
+ return None
26
+
27
+ @property
28
+ def date(self) -> datetime:
29
+ return self._date
30
+
31
+ @property
32
+ def seeds(self) -> int:
33
+ return self._seeds
34
+
35
+ @property
36
+ def peers(self) -> int:
37
+ return self._peers
38
+
39
+ @property
40
+ def size_bytes(self) -> int:
41
+ return self._size
42
+
43
+ @property
44
+ def imdb_code(self) -> IMDbCode:
45
+ return self._imdb
46
+
47
+ @property
48
+ def release_name(self) -> str:
49
+ return self._name
50
+
51
+ @property
52
+ def hash(self) -> str:
53
+ return extract_torrent_hash(self._magnet)
54
+
55
+ @property
56
+ def url(self) -> str:
57
+ return self._link
58
+
59
+ @property
60
+ def category(self) -> str:
61
+ return self._type
@@ -0,0 +1,20 @@
1
+ from plexflow.utils.api.rest.antibot_restful import AntibotRestful
2
+ from plexflow.core.torrents.providers.torrentquest.utils import TorrentQuestSearchResult
3
+ from plexflow.utils.torrent.extract.torrentquest import extract_torrent_results
4
+ from typing import List
5
+ import os
6
+
7
+ class TorrentQuest(AntibotRestful):
8
+ def __init__(self, base_url: str = 'https://torrentquest.com', use_xvfb: bool = os.getenv('USE_XVFB', 'false').lower() == 'true'):
9
+ super().__init__(base_url=base_url, use_xvfb=use_xvfb)
10
+
11
+ def search(self, query: str) -> List[TorrentQuestSearchResult]:
12
+ capture = self.get('/search', query_params={
13
+ 'q': query,
14
+ 'm': '1',
15
+ 'x': '14',
16
+ 'y': '17',
17
+ }, wait_condition='regex', wait_value='Download Name')
18
+
19
+ data = extract_torrent_results(html=capture.html)
20
+ return list(map(lambda t: TorrentQuestSearchResult(**t), data))