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,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
|