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,130 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from plexflow.core.metadata.auto.auto_providers.auto.movie import AutoMovie
|
3
|
+
from typing import List, Any
|
4
|
+
from langcodes import standardize_tag
|
5
|
+
|
6
|
+
class UniversalMovie:
|
7
|
+
def __init__(self, providers: List[AutoMovie]):
|
8
|
+
self.providers = providers
|
9
|
+
|
10
|
+
@property
|
11
|
+
def plex_rating_key(self) -> str:
|
12
|
+
for p in self.providers:
|
13
|
+
if p.source == "plex":
|
14
|
+
return p.id
|
15
|
+
|
16
|
+
@property
|
17
|
+
def title(self) -> str:
|
18
|
+
for p in self.providers:
|
19
|
+
if p.title:
|
20
|
+
return p.title
|
21
|
+
|
22
|
+
@property
|
23
|
+
def year(self) -> int:
|
24
|
+
for p in self.providers:
|
25
|
+
if p.year:
|
26
|
+
return p.year
|
27
|
+
|
28
|
+
@property
|
29
|
+
def rank(self) -> int:
|
30
|
+
for p in self.providers:
|
31
|
+
if p.rank:
|
32
|
+
return p.rank
|
33
|
+
|
34
|
+
@property
|
35
|
+
def release_date(self) -> datetime:
|
36
|
+
for p in self.providers:
|
37
|
+
if p.release_date:
|
38
|
+
return p.release_date
|
39
|
+
|
40
|
+
@property
|
41
|
+
def imdb_id(self) -> str:
|
42
|
+
for p in self.providers:
|
43
|
+
if p.imdb_id:
|
44
|
+
return p.imdb_id
|
45
|
+
|
46
|
+
@property
|
47
|
+
def titles(self) -> List[str]:
|
48
|
+
results = set()
|
49
|
+
|
50
|
+
for p in self.providers:
|
51
|
+
results.update(p.titles)
|
52
|
+
results.add(p.title)
|
53
|
+
|
54
|
+
return list(results)
|
55
|
+
|
56
|
+
@property
|
57
|
+
def runtime(self) -> int:
|
58
|
+
for p in self.providers:
|
59
|
+
if p.runtime:
|
60
|
+
return p.runtime
|
61
|
+
|
62
|
+
@property
|
63
|
+
def summary(self) -> str:
|
64
|
+
for p in self.providers:
|
65
|
+
if p.summary:
|
66
|
+
return p.summary
|
67
|
+
|
68
|
+
@property
|
69
|
+
def sources(self) -> List[str]:
|
70
|
+
return list({p.source for p in self.providers})
|
71
|
+
|
72
|
+
@property
|
73
|
+
def language(self) -> str:
|
74
|
+
for p in self.providers:
|
75
|
+
if p.language:
|
76
|
+
return standardize_tag(p.language)
|
77
|
+
|
78
|
+
@property
|
79
|
+
def release_dates(self) -> List[datetime]:
|
80
|
+
return list({p.release_date for p in self.providers if isinstance(p.release_date, datetime)})
|
81
|
+
|
82
|
+
@property
|
83
|
+
def years(self) -> List[int]:
|
84
|
+
return list({p.year for p in self.providers if isinstance(p.year, int)})
|
85
|
+
|
86
|
+
@property
|
87
|
+
def runtimes(self) -> List[int]:
|
88
|
+
return list({p.runtime for p in self.providers if isinstance(p.runtime, int)})
|
89
|
+
|
90
|
+
@property
|
91
|
+
def languages(self) -> List[str]:
|
92
|
+
return list({standardize_tag(p.language) for p in self.providers if isinstance(p.language, str)})
|
93
|
+
|
94
|
+
@property
|
95
|
+
def imdb_ids(self) -> List[str]:
|
96
|
+
return list({p.imdb_id for p in self.providers if isinstance(p.imdb_id, str)})
|
97
|
+
|
98
|
+
def _is_field_consistent(self, field: str) -> bool:
|
99
|
+
return len(getattr(self, field)) == 1
|
100
|
+
|
101
|
+
@property
|
102
|
+
def is_imdb_id_consistent(self) -> bool:
|
103
|
+
return self._is_field_consistent("imdb_ids")
|
104
|
+
|
105
|
+
@property
|
106
|
+
def is_year_consistent(self) -> bool:
|
107
|
+
return self._is_field_consistent("years")
|
108
|
+
|
109
|
+
@property
|
110
|
+
def is_release_date_consistent(self) -> bool:
|
111
|
+
return self._is_field_consistent("release_dates")
|
112
|
+
|
113
|
+
@property
|
114
|
+
def is_runtime_consistent(self) -> bool:
|
115
|
+
return self._is_field_consistent("runtimes")
|
116
|
+
|
117
|
+
@property
|
118
|
+
def is_language_consistent(self) -> bool:
|
119
|
+
return self._is_field_consistent("languages")
|
120
|
+
|
121
|
+
@property
|
122
|
+
def is_released(self) -> bool:
|
123
|
+
now = datetime.now()
|
124
|
+
return all(d <= now for d in self.release_dates)
|
125
|
+
|
126
|
+
@property
|
127
|
+
def days_until_release(self) -> int:
|
128
|
+
now = datetime.now()
|
129
|
+
|
130
|
+
return min((d - now).days for d in self.release_dates)
|
@@ -0,0 +1,192 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from dataclasses_json import dataclass_json, Undefined
|
4
|
+
from plexflow.core.metadata.providers.tmdb.datatypes import TmdbMovie
|
5
|
+
from plexflow.core.metadata.providers.tvdb.datatypes import TvdbMovie
|
6
|
+
from plexflow.core.metadata.providers.imdb.datatypes import ImdbMovie
|
7
|
+
from plexflow.core.metadata.providers.moviemeter.datatypes import MovieMeterMovie
|
8
|
+
from plexflow.core.metadata.providers.plex.datatypes import PlexMovieMetadata
|
9
|
+
|
10
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
11
|
+
@dataclass
|
12
|
+
class UniversalMovie:
|
13
|
+
"""
|
14
|
+
Represents a movie with data from TmdbMovie, TvdbMovie, ImdbMovie, and MovieMeterMovie.
|
15
|
+
|
16
|
+
Attributes:
|
17
|
+
tmdb_movie (TmdbMovie): The TmdbMovie object.
|
18
|
+
tvdb_movie (TvdbMovie): The TvdbMovie object.
|
19
|
+
imdb_movie (ImdbMovie): The ImdbMovie object.
|
20
|
+
moviemeter_movie (MovieMeterMovie): The MovieMeterMovie object.
|
21
|
+
"""
|
22
|
+
tmdb_movie: TmdbMovie
|
23
|
+
tvdb_movie: TvdbMovie
|
24
|
+
imdb_movie: ImdbMovie
|
25
|
+
moviemeter_movie: MovieMeterMovie
|
26
|
+
plex_movie: PlexMovieMetadata
|
27
|
+
|
28
|
+
@property
|
29
|
+
def is_release_date_consistent(self) -> bool:
|
30
|
+
"""
|
31
|
+
Checks whether tmdb, tvdb, and imdb have the same release date.
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
bool: True if the release dates are the same, False otherwise.
|
35
|
+
"""
|
36
|
+
if self.tmdb_movie.release_date and self.tvdb_movie.first_release.date and self.imdb_movie.release_date:
|
37
|
+
return self.tmdb_movie.release_date == self.tvdb_movie.first_release.date == self.imdb_movie.release_date.strftime("%Y-%m-%d")
|
38
|
+
return False
|
39
|
+
|
40
|
+
@property
|
41
|
+
def is_year_consistent(self) -> bool:
|
42
|
+
"""
|
43
|
+
Checks whether tmdb, tvdb, imdb, and moviemeter have the same release year.
|
44
|
+
|
45
|
+
Returns:
|
46
|
+
bool: True if the release years are the same, False otherwise.
|
47
|
+
"""
|
48
|
+
if self.tmdb_movie.release_date and self.tvdb_movie.first_release.date and self.imdb_movie.release_date and self.moviemeter_movie.year:
|
49
|
+
tmdb_year = datetime.strptime(self.tmdb_movie.release_date, "%Y-%m-%d").year
|
50
|
+
tvdb_year = datetime.strptime(self.tvdb_movie.first_release.date, "%Y-%m-%d").year
|
51
|
+
imdb_year = self.imdb_movie.release_date.year
|
52
|
+
moviemeter_year = self.moviemeter_movie.year
|
53
|
+
return tmdb_year == tvdb_year == imdb_year == moviemeter_year
|
54
|
+
return False
|
55
|
+
|
56
|
+
@property
|
57
|
+
def titles(self) -> set:
|
58
|
+
"""
|
59
|
+
Gets a set of all possible titles for the movie.
|
60
|
+
|
61
|
+
Returns:
|
62
|
+
set: A set of all possible titles for the movie in lowercase.
|
63
|
+
"""
|
64
|
+
titles = set()
|
65
|
+
if self.tmdb_movie.title:
|
66
|
+
titles.add(self.tmdb_movie.title.lower())
|
67
|
+
if self.tvdb_movie.name:
|
68
|
+
titles.add(self.tvdb_movie.name.lower())
|
69
|
+
if self.imdb_movie.title:
|
70
|
+
titles.add(self.imdb_movie.title.lower())
|
71
|
+
if self.moviemeter_movie.title:
|
72
|
+
titles.add(self.moviemeter_movie.title.lower())
|
73
|
+
if self.tmdb_movie.alternative_titles:
|
74
|
+
for alt_title in self.tmdb_movie.alternative_titles.titles:
|
75
|
+
titles.add(alt_title.title.lower())
|
76
|
+
return titles
|
77
|
+
|
78
|
+
@property
|
79
|
+
def imdb(self) -> str:
|
80
|
+
"""
|
81
|
+
Gets the IMDb ID of the movie.
|
82
|
+
|
83
|
+
Returns:
|
84
|
+
str: The IMDb ID of the movie.
|
85
|
+
"""
|
86
|
+
return self.tmdb_movie.imdb_id
|
87
|
+
|
88
|
+
@property
|
89
|
+
def title(self) -> str:
|
90
|
+
"""
|
91
|
+
Gets the title of the movie.
|
92
|
+
|
93
|
+
Returns:
|
94
|
+
str: The title of the movie.
|
95
|
+
"""
|
96
|
+
return self.tmdb_movie.title
|
97
|
+
|
98
|
+
@property
|
99
|
+
def year(self) -> int:
|
100
|
+
"""
|
101
|
+
Gets the year of the movie.
|
102
|
+
|
103
|
+
Returns:
|
104
|
+
int: The year of the movie. If the year is not available in tmdb, it uses tvdb, then imdb, then moviemeter.
|
105
|
+
"""
|
106
|
+
if self.tmdb_movie.release_date:
|
107
|
+
return datetime.strptime(self.tmdb_movie.release_date, "%Y-%m-%d").year
|
108
|
+
elif self.tvdb_movie.first_release.date:
|
109
|
+
return datetime.strptime(self.tvdb_movie.first_release.date, "%Y-%m-%d").year
|
110
|
+
elif self.imdb_movie.release_date:
|
111
|
+
return self.imdb_movie.release_date.year
|
112
|
+
elif self.moviemeter_movie.year:
|
113
|
+
return self.moviemeter_movie.year
|
114
|
+
else:
|
115
|
+
raise ValueError("Year is not available in tmdb, tvdb, imdb, and moviemeter.")
|
116
|
+
|
117
|
+
@property
|
118
|
+
def is_released(self) -> bool:
|
119
|
+
"""
|
120
|
+
Checks whether the movie is already released.
|
121
|
+
|
122
|
+
Returns:
|
123
|
+
bool: True if the movie is already released, False otherwise. If the status is not available in tmdb, it uses tvdb, then imdb.
|
124
|
+
"""
|
125
|
+
if not self.tmdb_movie.release_date and not self.tvdb_movie.first_release.date and not self.imdb_movie.release_date:
|
126
|
+
raise ValueError("Status is not available in tmdb, tvdb, and imdb.")
|
127
|
+
|
128
|
+
now = datetime.now()
|
129
|
+
if self.tmdb_movie.release_date:
|
130
|
+
tmdb_date = datetime.strptime(self.tmdb_movie.release_date, "%Y-%m-%d")
|
131
|
+
if tmdb_date <= now:
|
132
|
+
return True
|
133
|
+
if self.tvdb_movie.first_release.date:
|
134
|
+
tvdb_date = datetime.strptime(self.tvdb_movie.first_release.date, "%Y-%m-%d")
|
135
|
+
if tvdb_date <= now:
|
136
|
+
return True
|
137
|
+
if self.imdb_movie.release_date:
|
138
|
+
if self.imdb_movie.release_date <= now:
|
139
|
+
return True
|
140
|
+
return False
|
141
|
+
|
142
|
+
@property
|
143
|
+
def days_until_release(self) -> int:
|
144
|
+
"""
|
145
|
+
Gets the number of days until release.
|
146
|
+
|
147
|
+
Returns:
|
148
|
+
int: The number of days until release. If different release dates, it uses the closest.
|
149
|
+
"""
|
150
|
+
if self.tmdb_movie.release_date and self.tvdb_movie.first_release.date and self.imdb_movie.release_date:
|
151
|
+
tmdb_release_date = datetime.strptime(self.tmdb_movie.release_date, "%Y-%m-%d")
|
152
|
+
tvdb_release_date = datetime.strptime(self.tvdb_movie.first_release.date, "%Y-%m-%d")
|
153
|
+
imdb_release_date = self.imdb_movie.release_date
|
154
|
+
return min((tmdb_release_date - datetime.now()).days, (tvdb_release_date - datetime.now()).days, (imdb_release_date - datetime.now()).days)
|
155
|
+
elif self.tmdb_movie.release_date:
|
156
|
+
return (datetime.strptime(self.tmdb_movie.release_date, "%Y-%m-%d") - datetime.now()).days
|
157
|
+
elif self.tvdb_movie.first_release.date:
|
158
|
+
return (datetime.strptime(self.tvdb_movie.first_release.date, "%Y-%m-%d") - datetime.now()).days
|
159
|
+
elif self.imdb_movie.release_date:
|
160
|
+
return (self.imdb_movie.release_date - datetime.now()).days
|
161
|
+
else:
|
162
|
+
raise ValueError("Release date is not available in tmdb, tvdb, and imdb.")
|
163
|
+
|
164
|
+
@property
|
165
|
+
def rank(self) -> int:
|
166
|
+
"""
|
167
|
+
Gets the IMDb rank of the movie.
|
168
|
+
|
169
|
+
Returns:
|
170
|
+
int: The IMDb rank of the movie.
|
171
|
+
"""
|
172
|
+
return self.imdb_movie.rank
|
173
|
+
|
174
|
+
@property
|
175
|
+
def rating(self) -> float:
|
176
|
+
"""
|
177
|
+
Gets the IMDb rating of the movie.
|
178
|
+
|
179
|
+
Returns:
|
180
|
+
float: The IMDb rating of the movie.
|
181
|
+
"""
|
182
|
+
return self.imdb_movie.rating
|
183
|
+
|
184
|
+
@property
|
185
|
+
def votes(self) -> int:
|
186
|
+
"""
|
187
|
+
Gets the IMDb votes of the movie.
|
188
|
+
|
189
|
+
Returns:
|
190
|
+
int: The IMDb votes of the movie.
|
191
|
+
"""
|
192
|
+
return self.imdb_movie.votes
|
@@ -0,0 +1,107 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from dataclasses_json import dataclass_json, Undefined
|
4
|
+
from plexflow.core.metadata.providers.tmdb.datatypes import TmdbShow
|
5
|
+
from plexflow.core.metadata.providers.tvdb.tv_datatypes import TvdbShow
|
6
|
+
from plexflow.core.metadata.providers.imdb.datatypes import ImdbShow
|
7
|
+
from plexflow.core.metadata.providers.plex.datatypes import PlexShowMetadata
|
8
|
+
|
9
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
10
|
+
@dataclass
|
11
|
+
class UniversalShow:
|
12
|
+
tmdb_show: TmdbShow
|
13
|
+
tvdb_show: TvdbShow
|
14
|
+
plex_show: PlexShowMetadata
|
15
|
+
imdb_show: ImdbShow
|
16
|
+
|
17
|
+
@property
|
18
|
+
def is_release_date_consistent(self) -> bool:
|
19
|
+
"""
|
20
|
+
Check if the release dates from different providers (TMDB, TVDB, and IMDb) are consistent.
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
bool: True if the release dates are consistent, False otherwise.
|
24
|
+
"""
|
25
|
+
tmdb_date = datetime.strptime(self.tmdb_show.first_air_date, "%Y-%m-%d")
|
26
|
+
tvdb_date = datetime.strptime(self.tvdb_show.firstAired, "%Y-%m-%d")
|
27
|
+
imdb_date = datetime.strptime(self.imdb_show.release_date, "%Y-%m-%d")
|
28
|
+
|
29
|
+
return tmdb_date == tvdb_date == imdb_date
|
30
|
+
|
31
|
+
@property
|
32
|
+
def is_year_consistent(self) -> bool:
|
33
|
+
tmdb_date = datetime.strptime(self.tmdb_show.first_air_date, "%Y-%m-%d")
|
34
|
+
tvdb_year = self.tvdb_show.year
|
35
|
+
plex_year = self.plex_show.year
|
36
|
+
return tmdb_date.year == tvdb_year == plex_year
|
37
|
+
|
38
|
+
@property
|
39
|
+
def titles(self) -> set:
|
40
|
+
title_set = set()
|
41
|
+
|
42
|
+
title_set.add(self.tmdb_show.original_name)
|
43
|
+
title_set.add(self.tvdb_show.name)
|
44
|
+
title_set.update(alias.name for alias in self.tvdb_show.aliases)
|
45
|
+
title_set.update(title.title for title in self.tmdb_show.alternative_titles.results)
|
46
|
+
title_set.add(self.plex_show.title)
|
47
|
+
title_set.add(self.imdb_show.title)
|
48
|
+
|
49
|
+
return set(map(lambda s: s.lower().strip(), title_set))
|
50
|
+
|
51
|
+
@property
|
52
|
+
def imdb(self) -> str:
|
53
|
+
"""
|
54
|
+
Gets the IMDb ID of the movie.
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
str: The IMDb ID of the movie.
|
58
|
+
"""
|
59
|
+
return self.tmdb_show.imdb_id
|
60
|
+
|
61
|
+
@property
|
62
|
+
def title(self) -> str:
|
63
|
+
"""
|
64
|
+
Gets the title of the movie.
|
65
|
+
|
66
|
+
Returns:
|
67
|
+
str: The title of the movie.
|
68
|
+
"""
|
69
|
+
return self.tvdb_show.name
|
70
|
+
|
71
|
+
@property
|
72
|
+
def year(self) -> int:
|
73
|
+
return self.tvdb_show.year
|
74
|
+
|
75
|
+
@property
|
76
|
+
def is_released(self) -> bool:
|
77
|
+
tmdb_date = datetime.strptime(self.tmdb_show.first_air_date, "%Y-%m-%d")
|
78
|
+
tvdb_date = datetime.strptime(self.tvdb_show.firstAired, "%Y-%m-%d")
|
79
|
+
imdb_date = datetime.strptime(self.imdb_show.release_date, "%Y-%m-%d")
|
80
|
+
|
81
|
+
now = datetime.now()
|
82
|
+
return tmdb_date < now or tvdb_date < now or imdb_date < now
|
83
|
+
|
84
|
+
@property
|
85
|
+
def days_until_release(self) -> int:
|
86
|
+
tmdb_date = datetime.strptime(self.tmdb_show.first_air_date, "%Y-%m-%d")
|
87
|
+
tvdb_date = datetime.strptime(self.tvdb_show.firstAired, "%Y-%m-%d")
|
88
|
+
imdb_date = datetime.strptime(self.imdb_show.release_date, "%Y-%m-%d")
|
89
|
+
|
90
|
+
now = datetime.now()
|
91
|
+
tmdb_days = (tmdb_date - now).days
|
92
|
+
tvdb_days = (tvdb_date - now).days
|
93
|
+
imdb_days = (imdb_date - now).days
|
94
|
+
|
95
|
+
return min(tmdb_days, tvdb_days, imdb_days)
|
96
|
+
|
97
|
+
@property
|
98
|
+
def rank(self) -> int:
|
99
|
+
return self.imdb_show.rank
|
100
|
+
|
101
|
+
@property
|
102
|
+
def rating(self) -> float:
|
103
|
+
return self.imdb_show.rating
|
104
|
+
|
105
|
+
@property
|
106
|
+
def votes(self) -> int:
|
107
|
+
return self.imdb_show.votes
|
File without changes
|
@@ -0,0 +1,15 @@
|
|
1
|
+
from plexflow.utils.api.context.http import HttpRequestContext
|
2
|
+
from plexflow.core.plex.token.auto_token import PlexAutoToken
|
3
|
+
|
4
|
+
class PlexAuthorizedRequestContext(HttpRequestContext):
|
5
|
+
"""
|
6
|
+
A class for setting up a default request context for Plex API with X-Plex-Token header.
|
7
|
+
|
8
|
+
Args:
|
9
|
+
base_url (str): The base URL for the Plex API.
|
10
|
+
x_plex_token (str): The X-Plex-Token for the Plex API.
|
11
|
+
"""
|
12
|
+
|
13
|
+
def __init__(self, base_url: str, x_plex_token: str = None):
|
14
|
+
# Initialize the parent class with the base_url and default_headers
|
15
|
+
super().__init__(base_url, {'X-Plex-Token': PlexAutoToken(plex_token=x_plex_token).get_token(), 'Accept': 'application/json'})
|
@@ -0,0 +1,14 @@
|
|
1
|
+
from plexflow.core.plex.api.context.authorized import PlexAuthorizedRequestContext
|
2
|
+
from plexflow.core.plex.token.auto_token import PlexAutoToken
|
3
|
+
import os
|
4
|
+
|
5
|
+
class PlexDiscoverRequestContext(PlexAuthorizedRequestContext):
|
6
|
+
"""
|
7
|
+
A class for setting up a default request context for Plex Discover API with X-Plex-Token header.
|
8
|
+
"""
|
9
|
+
|
10
|
+
def __init__(self, base_url: str = None, x_plex_token: str = None):
|
11
|
+
# Initialize the parent class with the base_url and default_headers
|
12
|
+
super().__init__(base_url or f'https://discover.provider.plex.tv',
|
13
|
+
{'X-Plex-Token': PlexAutoToken(plex_token=x_plex_token).get_token(),
|
14
|
+
'Accept': 'application/json'})
|
@@ -0,0 +1,14 @@
|
|
1
|
+
from plexflow.core.plex.api.context.authorized import PlexAuthorizedRequestContext
|
2
|
+
from plexflow.core.plex.token.auto_token import PlexAutoToken
|
3
|
+
import os
|
4
|
+
|
5
|
+
class PlexLibraryRequestContext(PlexAuthorizedRequestContext):
|
6
|
+
"""
|
7
|
+
A class for setting up a default request context for Plex Library API with X-Plex-Token header.
|
8
|
+
"""
|
9
|
+
|
10
|
+
def __init__(self, base_url: str = None, x_plex_token: str = None):
|
11
|
+
# Initialize the parent class with the base_url and default_headers
|
12
|
+
super().__init__(base_url or f'http://{os.getenv("PLEX_HOST")}:{os.getenv("PLEX_PORT", 32400)}',
|
13
|
+
{'X-Plex-Token': PlexAutoToken(plex_token=x_plex_token).get_token(),
|
14
|
+
'Accept': 'application/json'})
|
File without changes
|