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.
- plexflow/__init__.py +0 -0
- plexflow/__main__.py +15 -0
- plexflow/core/.DS_Store +0 -0
- plexflow/core/__init__.py +0 -0
- plexflow/core/context/__init__.py +0 -0
- plexflow/core/context/metadata/__init__.py +0 -0
- plexflow/core/context/metadata/context.py +32 -0
- plexflow/core/context/metadata/tmdb/__init__.py +0 -0
- plexflow/core/context/metadata/tmdb/context.py +45 -0
- plexflow/core/context/partial_context.py +46 -0
- plexflow/core/context/partials/__init__.py +8 -0
- plexflow/core/context/partials/cache.py +16 -0
- plexflow/core/context/partials/context.py +12 -0
- plexflow/core/context/partials/ids.py +37 -0
- plexflow/core/context/partials/movie.py +115 -0
- plexflow/core/context/partials/tgx_batch.py +33 -0
- plexflow/core/context/partials/tgx_context.py +34 -0
- plexflow/core/context/partials/torrents.py +23 -0
- plexflow/core/context/partials/watchlist.py +35 -0
- plexflow/core/context/plexflow_context.py +29 -0
- plexflow/core/context/plexflow_property.py +36 -0
- plexflow/core/context/root/__init__.py +0 -0
- plexflow/core/context/root/context.py +25 -0
- plexflow/core/context/select/__init__.py +0 -0
- plexflow/core/context/select/context.py +45 -0
- plexflow/core/context/torrent/__init__.py +0 -0
- plexflow/core/context/torrent/context.py +43 -0
- plexflow/core/context/torrent/tpb/__init__.py +0 -0
- plexflow/core/context/torrent/tpb/context.py +45 -0
- plexflow/core/context/torrent/yts/__init__.py +0 -0
- plexflow/core/context/torrent/yts/context.py +45 -0
- plexflow/core/context/watchlist/__init__.py +0 -0
- plexflow/core/context/watchlist/context.py +46 -0
- plexflow/core/downloads/__init__.py +0 -0
- plexflow/core/downloads/candidates/__init__.py +0 -0
- plexflow/core/downloads/candidates/download_candidate.py +210 -0
- plexflow/core/downloads/candidates/filtered.py +51 -0
- plexflow/core/downloads/candidates/utils.py +39 -0
- plexflow/core/env/__init__.py +0 -0
- plexflow/core/env/env.py +31 -0
- plexflow/core/genai/__init__.py +0 -0
- plexflow/core/genai/bot.py +9 -0
- plexflow/core/genai/plexa.py +54 -0
- plexflow/core/genai/torrent/imdb_verify.py +65 -0
- plexflow/core/genai/torrent/movie.py +25 -0
- plexflow/core/genai/utils/__init__.py +0 -0
- plexflow/core/genai/utils/loader.py +5 -0
- plexflow/core/metadata/__init__.py +0 -0
- plexflow/core/metadata/auto/__init__.py +0 -0
- plexflow/core/metadata/auto/auto_meta.py +40 -0
- plexflow/core/metadata/auto/auto_providers/__init__.py +0 -0
- plexflow/core/metadata/auto/auto_providers/auto/__init__.py +0 -0
- plexflow/core/metadata/auto/auto_providers/auto/episode.py +49 -0
- plexflow/core/metadata/auto/auto_providers/auto/item.py +55 -0
- plexflow/core/metadata/auto/auto_providers/auto/movie.py +13 -0
- plexflow/core/metadata/auto/auto_providers/auto/season.py +43 -0
- plexflow/core/metadata/auto/auto_providers/auto/show.py +26 -0
- plexflow/core/metadata/auto/auto_providers/imdb/__init__.py +0 -0
- plexflow/core/metadata/auto/auto_providers/imdb/movie.py +36 -0
- plexflow/core/metadata/auto/auto_providers/imdb/show.py +45 -0
- plexflow/core/metadata/auto/auto_providers/moviemeter/__init__.py +0 -0
- plexflow/core/metadata/auto/auto_providers/moviemeter/movie.py +40 -0
- plexflow/core/metadata/auto/auto_providers/plex/__init__.py +0 -0
- plexflow/core/metadata/auto/auto_providers/plex/movie.py +39 -0
- plexflow/core/metadata/auto/auto_providers/tmdb/__init__.py +0 -0
- plexflow/core/metadata/auto/auto_providers/tmdb/episode.py +30 -0
- plexflow/core/metadata/auto/auto_providers/tmdb/movie.py +36 -0
- plexflow/core/metadata/auto/auto_providers/tmdb/season.py +23 -0
- plexflow/core/metadata/auto/auto_providers/tmdb/show.py +41 -0
- plexflow/core/metadata/auto/auto_providers/tmdb.py +92 -0
- plexflow/core/metadata/auto/auto_providers/tvdb/__init__.py +0 -0
- plexflow/core/metadata/auto/auto_providers/tvdb/episode.py +28 -0
- plexflow/core/metadata/auto/auto_providers/tvdb/movie.py +36 -0
- plexflow/core/metadata/auto/auto_providers/tvdb/season.py +25 -0
- plexflow/core/metadata/auto/auto_providers/tvdb/show.py +41 -0
- plexflow/core/metadata/providers/__init__.py +0 -0
- plexflow/core/metadata/providers/imdb/__init__.py +0 -0
- plexflow/core/metadata/providers/imdb/datatypes.py +53 -0
- plexflow/core/metadata/providers/imdb/imdb.py +112 -0
- plexflow/core/metadata/providers/moviemeter/__init__.py +0 -0
- plexflow/core/metadata/providers/moviemeter/datatypes.py +111 -0
- plexflow/core/metadata/providers/moviemeter/moviemeter.py +42 -0
- plexflow/core/metadata/providers/plex/__init__.py +0 -0
- plexflow/core/metadata/providers/plex/datatypes.py +693 -0
- plexflow/core/metadata/providers/plex/plex.py +167 -0
- plexflow/core/metadata/providers/tmdb/__init__.py +0 -0
- plexflow/core/metadata/providers/tmdb/datatypes.py +460 -0
- plexflow/core/metadata/providers/tmdb/tmdb.py +85 -0
- plexflow/core/metadata/providers/tvdb/__init__.py +0 -0
- plexflow/core/metadata/providers/tvdb/datatypes.py +257 -0
- plexflow/core/metadata/providers/tvdb/tv_datatypes.py +554 -0
- plexflow/core/metadata/providers/tvdb/tvdb.py +65 -0
- plexflow/core/metadata/providers/universal/__init__.py +0 -0
- plexflow/core/metadata/providers/universal/movie.py +130 -0
- plexflow/core/metadata/providers/universal/old.py +192 -0
- plexflow/core/metadata/providers/universal/show.py +107 -0
- plexflow/core/plex/__init__.py +0 -0
- plexflow/core/plex/api/context/authorized.py +15 -0
- plexflow/core/plex/api/context/discover.py +14 -0
- plexflow/core/plex/api/context/library.py +14 -0
- plexflow/core/plex/discover/__init__.py +0 -0
- plexflow/core/plex/discover/activity.py +448 -0
- plexflow/core/plex/discover/comment.py +89 -0
- plexflow/core/plex/discover/feed.py +11 -0
- plexflow/core/plex/hooks/__init__.py +0 -0
- plexflow/core/plex/hooks/plex_authorized.py +60 -0
- plexflow/core/plex/hooks/plexflow_database.py +6 -0
- plexflow/core/plex/library/__init__.py +0 -0
- plexflow/core/plex/library/library.py +103 -0
- plexflow/core/plex/token/__init__.py +0 -0
- plexflow/core/plex/token/auto_token.py +91 -0
- plexflow/core/plex/utils/__init__.py +0 -0
- plexflow/core/plex/utils/paginated.py +39 -0
- plexflow/core/plex/watchlist/__init__.py +0 -0
- plexflow/core/plex/watchlist/datatypes.py +124 -0
- plexflow/core/plex/watchlist/watchlist.py +23 -0
- plexflow/core/storage/__init__.py +0 -0
- plexflow/core/storage/object/__init__.py +0 -0
- plexflow/core/storage/object/plexflow_storage.py +143 -0
- plexflow/core/storage/object/redis_storage.py +169 -0
- plexflow/core/subtitles/__init__.py +0 -0
- plexflow/core/subtitles/providers/__init__.py +0 -0
- plexflow/core/subtitles/providers/auto_subtitles.py +48 -0
- plexflow/core/subtitles/providers/oss/__init__.py +0 -0
- plexflow/core/subtitles/providers/oss/datatypes.py +104 -0
- plexflow/core/subtitles/providers/oss/download.py +48 -0
- plexflow/core/subtitles/providers/oss/old.py +144 -0
- plexflow/core/subtitles/providers/oss/oss.py +400 -0
- plexflow/core/subtitles/providers/oss/oss_subtitle.py +32 -0
- plexflow/core/subtitles/providers/oss/search.py +52 -0
- plexflow/core/subtitles/providers/oss/unlimited_oss.py +231 -0
- plexflow/core/subtitles/providers/oss/utils/__init__.py +0 -0
- plexflow/core/subtitles/providers/oss/utils/config.py +63 -0
- plexflow/core/subtitles/providers/oss/utils/download_client.py +22 -0
- plexflow/core/subtitles/providers/oss/utils/exceptions.py +35 -0
- plexflow/core/subtitles/providers/oss/utils/file_utils.py +83 -0
- plexflow/core/subtitles/providers/oss/utils/languages.py +78 -0
- plexflow/core/subtitles/providers/oss/utils/response_base.py +221 -0
- plexflow/core/subtitles/providers/oss/utils/responses.py +176 -0
- plexflow/core/subtitles/providers/oss/utils/srt.py +561 -0
- plexflow/core/subtitles/results/__init__.py +0 -0
- plexflow/core/subtitles/results/subtitle.py +170 -0
- plexflow/core/torrents/__init__.py +0 -0
- plexflow/core/torrents/analyzers/analyzed_torrent.py +143 -0
- plexflow/core/torrents/analyzers/analyzer.py +45 -0
- plexflow/core/torrents/analyzers/torrentquest/analyzer.py +47 -0
- plexflow/core/torrents/auto/auto_providers/auto/__init__.py +0 -0
- plexflow/core/torrents/auto/auto_providers/auto/torrent.py +64 -0
- plexflow/core/torrents/auto/auto_providers/tpb/torrent.py +62 -0
- plexflow/core/torrents/auto/auto_torrents.py +29 -0
- plexflow/core/torrents/providers/__init__.py +0 -0
- plexflow/core/torrents/providers/ext/__init__.py +0 -0
- plexflow/core/torrents/providers/ext/ext.py +18 -0
- plexflow/core/torrents/providers/ext/utils.py +64 -0
- plexflow/core/torrents/providers/extratorrent/__init__.py +0 -0
- plexflow/core/torrents/providers/extratorrent/extratorrent.py +21 -0
- plexflow/core/torrents/providers/extratorrent/utils.py +66 -0
- plexflow/core/torrents/providers/eztv/__init__.py +0 -0
- plexflow/core/torrents/providers/eztv/eztv.py +47 -0
- plexflow/core/torrents/providers/eztv/utils.py +83 -0
- plexflow/core/torrents/providers/rarbg2/__init__.py +0 -0
- plexflow/core/torrents/providers/rarbg2/rarbg2.py +19 -0
- plexflow/core/torrents/providers/rarbg2/utils.py +76 -0
- plexflow/core/torrents/providers/snowfl/__init__.py +0 -0
- plexflow/core/torrents/providers/snowfl/snowfl.py +36 -0
- plexflow/core/torrents/providers/snowfl/utils.py +59 -0
- plexflow/core/torrents/providers/tgx/__init__.py +0 -0
- plexflow/core/torrents/providers/tgx/context.py +50 -0
- plexflow/core/torrents/providers/tgx/dump.py +40 -0
- plexflow/core/torrents/providers/tgx/tgx.py +22 -0
- plexflow/core/torrents/providers/tgx/utils.py +61 -0
- plexflow/core/torrents/providers/therarbg/__init__.py +0 -0
- plexflow/core/torrents/providers/therarbg/therarbg.py +17 -0
- plexflow/core/torrents/providers/therarbg/utils.py +61 -0
- plexflow/core/torrents/providers/torrentquest/__init__.py +0 -0
- plexflow/core/torrents/providers/torrentquest/torrentquest.py +20 -0
- plexflow/core/torrents/providers/torrentquest/utils.py +70 -0
- plexflow/core/torrents/providers/tpb/__init__.py +0 -0
- plexflow/core/torrents/providers/tpb/tpb.py +17 -0
- plexflow/core/torrents/providers/tpb/utils.py +139 -0
- plexflow/core/torrents/providers/yts/__init__.py +0 -0
- plexflow/core/torrents/providers/yts/utils.py +57 -0
- plexflow/core/torrents/providers/yts/yts.py +31 -0
- plexflow/core/torrents/results/__init__.py +0 -0
- plexflow/core/torrents/results/torrent.py +165 -0
- plexflow/core/torrents/results/universal.py +220 -0
- plexflow/core/torrents/results/utils.py +15 -0
- plexflow/events/__init__.py +0 -0
- plexflow/events/download/__init__.py +0 -0
- plexflow/events/download/torrent_events.py +96 -0
- plexflow/events/publish/__init__.py +0 -0
- plexflow/events/publish/publish.py +34 -0
- plexflow/logging/__init__.py +0 -0
- plexflow/logging/log_setup.py +8 -0
- plexflow/spiders/quiet_logger.py +9 -0
- plexflow/spiders/tgx/pipelines/dump_json_pipeline.py +30 -0
- plexflow/spiders/tgx/pipelines/meta_pipeline.py +13 -0
- plexflow/spiders/tgx/pipelines/publish_pipeline.py +14 -0
- plexflow/spiders/tgx/pipelines/torrent_info_pipeline.py +12 -0
- plexflow/spiders/tgx/pipelines/validation_pipeline.py +17 -0
- plexflow/spiders/tgx/settings.py +36 -0
- plexflow/spiders/tgx/spider.py +72 -0
- plexflow/utils/__init__.py +0 -0
- plexflow/utils/antibot/human_like_requests.py +122 -0
- plexflow/utils/api/__init__.py +0 -0
- plexflow/utils/api/context/http.py +62 -0
- plexflow/utils/api/rest/__init__.py +0 -0
- plexflow/utils/api/rest/antibot_restful.py +68 -0
- plexflow/utils/api/rest/restful.py +49 -0
- plexflow/utils/captcha/__init__.py +0 -0
- plexflow/utils/captcha/bypass/__init__.py +0 -0
- plexflow/utils/captcha/bypass/decode_audio.py +34 -0
- plexflow/utils/download/__init__.py +0 -0
- plexflow/utils/download/gz.py +26 -0
- plexflow/utils/filesystem/__init__.py +0 -0
- plexflow/utils/filesystem/search.py +129 -0
- plexflow/utils/gmail/__init__.py +0 -0
- plexflow/utils/gmail/mails.py +116 -0
- plexflow/utils/hooks/__init__.py +0 -0
- plexflow/utils/hooks/http.py +84 -0
- plexflow/utils/hooks/postgresql.py +93 -0
- plexflow/utils/hooks/redis.py +112 -0
- plexflow/utils/image/storage.py +36 -0
- plexflow/utils/imdb/__init__.py +0 -0
- plexflow/utils/imdb/imdb_codes.py +107 -0
- plexflow/utils/pubsub/consume.py +82 -0
- plexflow/utils/pubsub/produce.py +25 -0
- plexflow/utils/retry/__init__.py +0 -0
- plexflow/utils/retry/utils.py +38 -0
- plexflow/utils/strings/__init__.py +0 -0
- plexflow/utils/strings/filesize.py +55 -0
- plexflow/utils/strings/language.py +14 -0
- plexflow/utils/subtitle/search.py +76 -0
- plexflow/utils/tasks/decorators.py +78 -0
- plexflow/utils/tasks/k8s/task.py +70 -0
- plexflow/utils/thread_safe/safe_list.py +54 -0
- plexflow/utils/thread_safe/safe_set.py +69 -0
- plexflow/utils/torrent/__init__.py +0 -0
- plexflow/utils/torrent/analyze.py +118 -0
- plexflow/utils/torrent/extract/common.py +37 -0
- plexflow/utils/torrent/extract/ext.py +2391 -0
- plexflow/utils/torrent/extract/extratorrent.py +56 -0
- plexflow/utils/torrent/extract/kat.py +1581 -0
- plexflow/utils/torrent/extract/tgx.py +96 -0
- plexflow/utils/torrent/extract/therarbg.py +170 -0
- plexflow/utils/torrent/extract/torrentquest.py +171 -0
- plexflow/utils/torrent/files.py +36 -0
- plexflow/utils/torrent/hash.py +90 -0
- plexflow/utils/transcribe/__init__.py +0 -0
- plexflow/utils/transcribe/speech2text.py +40 -0
- plexflow/utils/video/__init__.py +0 -0
- plexflow/utils/video/subtitle.py +73 -0
- plexflow-0.0.64.dist-info/METADATA +71 -0
- plexflow-0.0.64.dist-info/RECORD +256 -0
- plexflow-0.0.64.dist-info/WHEEL +4 -0
- 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
|
File without changes
|
@@ -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))
|