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,36 @@
|
|
1
|
+
from plexflow.core.metadata.auto.auto_providers.auto.movie import AutoMovie
|
2
|
+
from plexflow.core.metadata.providers.tvdb.tvdb import search_movie_by_imdb
|
3
|
+
from datetime import datetime
|
4
|
+
|
5
|
+
class AutoTvdbMovie(AutoMovie):
|
6
|
+
def __init__(self, imdb_id: str) -> None:
|
7
|
+
super().__init__(imdb_id, 'tvdb')
|
8
|
+
self._movie = search_movie_by_imdb(self.imdb_id)
|
9
|
+
|
10
|
+
@property
|
11
|
+
def id(self) -> int:
|
12
|
+
return self._movie.id
|
13
|
+
|
14
|
+
@property
|
15
|
+
def title(self) -> str:
|
16
|
+
return self._movie.name
|
17
|
+
|
18
|
+
@property
|
19
|
+
def release_date(self) -> datetime:
|
20
|
+
return datetime.strptime(self._movie.first_release.date, '%Y-%m-%d')
|
21
|
+
|
22
|
+
@property
|
23
|
+
def runtime(self) -> int:
|
24
|
+
return self._movie.runtime
|
25
|
+
|
26
|
+
@property
|
27
|
+
def titles(self) -> list:
|
28
|
+
return [x.name for x in self._movie.aliases]
|
29
|
+
|
30
|
+
@property
|
31
|
+
def summary(self) -> str:
|
32
|
+
return ""
|
33
|
+
|
34
|
+
@property
|
35
|
+
def language(self) -> str:
|
36
|
+
return self._movie.originalLanguage
|
@@ -0,0 +1,25 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from plexflow.core.metadata.auto.auto_providers.auto.season import AutoSeason
|
3
|
+
from plexflow.core.metadata.auto.auto_providers.auto.show import AutoShow
|
4
|
+
from plexflow.core.metadata.auto.auto_providers.auto.episode import AutoEpisode
|
5
|
+
from plexflow.core.metadata.auto.auto_providers.tvdb.episode import AutoTvdbEpisode
|
6
|
+
from plexflow.core.metadata.providers.tvdb.tvdb import get_season
|
7
|
+
|
8
|
+
class AutoTvdbSeason(AutoSeason):
|
9
|
+
def __init__(self, parent: AutoShow, season_id: int) -> None:
|
10
|
+
super().__init__(parent)
|
11
|
+
self._season = get_season(season_id)
|
12
|
+
|
13
|
+
@property
|
14
|
+
def episodes(self) -> list[AutoEpisode]:
|
15
|
+
return [AutoTvdbEpisode(self, x) for x in self._season.get('episodes')]
|
16
|
+
|
17
|
+
@property
|
18
|
+
def release_date(self) -> datetime:
|
19
|
+
episodes = self._season.get('episodes')
|
20
|
+
first_aired = next(filter(lambda x: x, map(lambda x: x.get('aired'), sorted(episodes, key=lambda x: x.get('number')))), None)
|
21
|
+
return datetime.strptime(first_aired, '%Y-%m-%d') if first_aired else None
|
22
|
+
|
23
|
+
@property
|
24
|
+
def season_number(self) -> int:
|
25
|
+
return self._season.get('number')
|
@@ -0,0 +1,41 @@
|
|
1
|
+
from plexflow.core.metadata.auto.auto_providers.auto.show import AutoShow
|
2
|
+
from plexflow.core.metadata.providers.tvdb.tvdb import search_show_by_imdb
|
3
|
+
from datetime import datetime
|
4
|
+
from plexflow.core.metadata.auto.auto_providers.tvdb.season import AutoTvdbSeason, AutoSeason
|
5
|
+
|
6
|
+
class AutoTvdbShow(AutoShow):
|
7
|
+
def __init__(self, imdb_id: str) -> None:
|
8
|
+
super().__init__(imdb_id, 'tvdb')
|
9
|
+
self._show = search_show_by_imdb(self.imdb_id)
|
10
|
+
|
11
|
+
@property
|
12
|
+
def id(self) -> int:
|
13
|
+
return self._show.id
|
14
|
+
|
15
|
+
@property
|
16
|
+
def title(self) -> str:
|
17
|
+
return self._show.name
|
18
|
+
|
19
|
+
@property
|
20
|
+
def release_date(self) -> datetime:
|
21
|
+
return datetime.strptime(self._show.firstAired, '%Y-%m-%d')
|
22
|
+
|
23
|
+
@property
|
24
|
+
def runtime(self) -> int:
|
25
|
+
return self._show.averageRuntime
|
26
|
+
|
27
|
+
@property
|
28
|
+
def titles(self) -> list:
|
29
|
+
return [x.name for x in self._show.aliases]
|
30
|
+
|
31
|
+
@property
|
32
|
+
def summary(self) -> str:
|
33
|
+
return self._show.overview
|
34
|
+
|
35
|
+
@property
|
36
|
+
def language(self) -> str:
|
37
|
+
return self._show.originalLanguage
|
38
|
+
|
39
|
+
@property
|
40
|
+
def seasons(self) -> list[AutoSeason]:
|
41
|
+
return [AutoTvdbSeason(self, x.id) for x in self._show.seasons]
|
File without changes
|
File without changes
|
@@ -0,0 +1,53 @@
|
|
1
|
+
from datetime import datetime as dt
|
2
|
+
from dataclasses import dataclass, field
|
3
|
+
from dataclasses_json import dataclass_json, Undefined
|
4
|
+
|
5
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
6
|
+
@dataclass
|
7
|
+
class ImdbMovie:
|
8
|
+
"""
|
9
|
+
Represents a movie from IMDb.
|
10
|
+
|
11
|
+
Attributes:
|
12
|
+
imdb_id (str): The IMDb ID of the movie.
|
13
|
+
title (str): The title of the movie.
|
14
|
+
release_date (dt): The release date of the movie.
|
15
|
+
runtime (int): The runtime of the movie in minutes.
|
16
|
+
rating (float): The rating of the movie.
|
17
|
+
votes (int): The number of votes for the movie.
|
18
|
+
rank (int): The rank of the movie.
|
19
|
+
"""
|
20
|
+
imdb_id: str
|
21
|
+
title: str
|
22
|
+
release_date: dt
|
23
|
+
runtime: int
|
24
|
+
rating: float
|
25
|
+
votes: int
|
26
|
+
rank: int
|
27
|
+
|
28
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
29
|
+
@dataclass
|
30
|
+
class ImdbShow:
|
31
|
+
"""
|
32
|
+
Represents a TV show from IMDb.
|
33
|
+
|
34
|
+
Attributes:
|
35
|
+
imdb_id (str): The IMDb ID of the show.
|
36
|
+
title (str): The title of the show.
|
37
|
+
release_date (dt): The release date of the show.
|
38
|
+
runtime (int): The runtime of each episode in minutes.
|
39
|
+
rating (float): The average rating of the show.
|
40
|
+
votes (int): The number of votes the show has received.
|
41
|
+
rank (int): The IMDb rank of the show.
|
42
|
+
episodes (int): The total number of episodes in the show.
|
43
|
+
seasons (int): The total number of seasons in the show.
|
44
|
+
"""
|
45
|
+
imdb_id: str
|
46
|
+
title: str
|
47
|
+
release_date: dt
|
48
|
+
runtime: int
|
49
|
+
rating: float
|
50
|
+
votes: int
|
51
|
+
rank: int
|
52
|
+
episodes: int
|
53
|
+
seasons: int
|
@@ -0,0 +1,112 @@
|
|
1
|
+
import requests
|
2
|
+
from bs4 import BeautifulSoup
|
3
|
+
from datetime import datetime as dt
|
4
|
+
from plexflow.core.metadata.providers.imdb.datatypes import ImdbMovie, ImdbShow
|
5
|
+
from typing import Union
|
6
|
+
import json
|
7
|
+
|
8
|
+
def search_movie_by_imdb(imdb_id: str) -> Union[ImdbMovie, None]:
|
9
|
+
"""
|
10
|
+
Search for a movie using its IMDB ID.
|
11
|
+
|
12
|
+
Args:
|
13
|
+
imdb_id (str): The IMDB ID of the movie.
|
14
|
+
|
15
|
+
Returns:
|
16
|
+
Union[ImdbMovie, None]: An instance of the ImdbMovie class representing the movie if found,
|
17
|
+
or None if the movie is not found.
|
18
|
+
|
19
|
+
Raises:
|
20
|
+
RuntimeError: If an HTTP error, connection error, timeout error, or request exception occurs.
|
21
|
+
"""
|
22
|
+
url = f"https://www.imdb.com/title/{imdb_id}"
|
23
|
+
headers = {
|
24
|
+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
|
25
|
+
}
|
26
|
+
|
27
|
+
try:
|
28
|
+
r = requests.get(url=url, headers=headers)
|
29
|
+
r.raise_for_status()
|
30
|
+
except requests.exceptions.RequestException as err:
|
31
|
+
raise RuntimeError("An error occurred during the request.") from err
|
32
|
+
|
33
|
+
soup = BeautifulSoup(r.text, "html.parser")
|
34
|
+
scripts = soup.select("script[type='application/json']")
|
35
|
+
|
36
|
+
for script in scripts:
|
37
|
+
content = script.text
|
38
|
+
data = json.loads(content)
|
39
|
+
imdb_id = data.get("props", {}).get("pageProps", {}).get("tconst")
|
40
|
+
data = data.get("props", {}).get("pageProps", {}).get("aboveTheFoldData")
|
41
|
+
|
42
|
+
if data:
|
43
|
+
title = data.get("originalTitleText", {}).get("text")
|
44
|
+
release_day = data.get("releaseDate", {}).get("day")
|
45
|
+
release_month = data.get("releaseDate", {}).get("month")
|
46
|
+
release_year = data.get("releaseDate", {}).get("year")
|
47
|
+
date_str = f"{release_year:04d}-{release_month:02d}-{release_day:02d}"
|
48
|
+
release_date = dt.strptime(date_str, "%Y-%m-%d")
|
49
|
+
|
50
|
+
runtime = data.get("runtime", {}).get("seconds")
|
51
|
+
rating = data.get("ratingsSummary", {}).get("aggregateRating")
|
52
|
+
votes = data.get("ratingsSummary", {}).get("voteCount")
|
53
|
+
rank = data.get("meterRanking", {}).get("currentRank")
|
54
|
+
|
55
|
+
return ImdbMovie(imdb_id, title, release_date, runtime, rating, votes, rank)
|
56
|
+
|
57
|
+
return None
|
58
|
+
|
59
|
+
|
60
|
+
def search_show_by_imdb(imdb_id: str) -> Union[ImdbShow, None]:
|
61
|
+
"""
|
62
|
+
Search for a TV show on IMDb using the IMDb ID.
|
63
|
+
|
64
|
+
Args:
|
65
|
+
imdb_id (str): The IMDb ID of the TV show.
|
66
|
+
|
67
|
+
Returns:
|
68
|
+
Union[ImdbShow, None]: An instance of the ImdbShow class representing the TV show if found,
|
69
|
+
or None if the TV show is not found.
|
70
|
+
|
71
|
+
Raises:
|
72
|
+
RuntimeError: If an HTTP error, connection error, timeout error, or request exception occurs.
|
73
|
+
"""
|
74
|
+
url = f"https://www.imdb.com/title/{imdb_id}"
|
75
|
+
headers = {
|
76
|
+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
|
77
|
+
}
|
78
|
+
|
79
|
+
try:
|
80
|
+
r = requests.get(url=url, headers=headers)
|
81
|
+
r.raise_for_status()
|
82
|
+
except requests.exceptions.RequestException as err:
|
83
|
+
raise RuntimeError("An error occurred during the request.") from err
|
84
|
+
|
85
|
+
soup = BeautifulSoup(r.text, "html.parser")
|
86
|
+
scripts = soup.select("script[type='application/json']")
|
87
|
+
|
88
|
+
for script in scripts:
|
89
|
+
content = script.text
|
90
|
+
data = json.loads(content)
|
91
|
+
imdb_id = data.get("props", {}).get("pageProps", {}).get("tconst")
|
92
|
+
main_data = data.get("props", {}).get("pageProps", {}).get("mainColumnData")
|
93
|
+
data = data.get("props", {}).get("pageProps", {}).get("aboveTheFoldData")
|
94
|
+
|
95
|
+
if data:
|
96
|
+
title = data.get("originalTitleText", {}).get("text")
|
97
|
+
release_day = data.get("releaseDate", {}).get("day")
|
98
|
+
release_month = data.get("releaseDate", {}).get("month")
|
99
|
+
release_year = data.get("releaseDate", {}).get("year")
|
100
|
+
date_str = f"{release_year:04d}-{release_month:02d}-{release_day:02d}"
|
101
|
+
release_date = dt.strptime(date_str, "%Y-%m-%d")
|
102
|
+
episodes = main_data.get("episodes", {}).get("episodes", {}).get("total")
|
103
|
+
seasons = len(set(map(lambda s: s.get("number"), main_data.get("episodes", {}).get("seasons", []))))
|
104
|
+
|
105
|
+
runtime = data.get("runtime", {}).get("seconds")
|
106
|
+
rating = data.get("ratingsSummary", {}).get("aggregateRating")
|
107
|
+
votes = data.get("ratingsSummary", {}).get("voteCount")
|
108
|
+
rank = data.get("meterRanking", {}).get("currentRank")
|
109
|
+
|
110
|
+
return ImdbShow(imdb_id, title, release_date, runtime, rating, votes, rank, episodes, seasons)
|
111
|
+
|
112
|
+
return None
|
File without changes
|
@@ -0,0 +1,111 @@
|
|
1
|
+
from dataclasses import dataclass, field
|
2
|
+
from typing import List, Optional, Dict
|
3
|
+
from dataclasses_json import dataclass_json, Undefined
|
4
|
+
|
5
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
6
|
+
@dataclass
|
7
|
+
class Actor:
|
8
|
+
"""
|
9
|
+
Represents an actor in a movie.
|
10
|
+
|
11
|
+
Attributes:
|
12
|
+
name (Optional[str]): The name of the actor.
|
13
|
+
voice (Optional[bool]): Indicates if the actor provided a voice-over for the movie.
|
14
|
+
_catchall (Dict[str, str]): A catch-all dictionary for any additional attributes.
|
15
|
+
"""
|
16
|
+
name: Optional[str] = None
|
17
|
+
voice: Optional[bool] = None
|
18
|
+
_catchall: Dict[str, str] = field(default_factory=dict)
|
19
|
+
|
20
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
21
|
+
@dataclass
|
22
|
+
class Director:
|
23
|
+
"""
|
24
|
+
Represents a movie director.
|
25
|
+
|
26
|
+
Attributes:
|
27
|
+
name (Optional[str]): The name of the director.
|
28
|
+
id (Optional[str]): The ID of the director.
|
29
|
+
_catchall (Dict[str, str]): A catch-all dictionary for any additional attributes.
|
30
|
+
"""
|
31
|
+
name: Optional[str] = None
|
32
|
+
id: Optional[str] = None
|
33
|
+
_catchall: Dict[str, str] = field(default_factory=dict)
|
34
|
+
|
35
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
36
|
+
@dataclass
|
37
|
+
class Posters:
|
38
|
+
"""
|
39
|
+
Represents the posters of a movie.
|
40
|
+
|
41
|
+
Attributes:
|
42
|
+
thumb (Optional[str]): The URL of the thumbnail-sized poster.
|
43
|
+
small (Optional[str]): The URL of the small-sized poster.
|
44
|
+
regular (Optional[str]): The URL of the regular-sized poster.
|
45
|
+
large (Optional[str]): The URL of the large-sized poster.
|
46
|
+
_catchall (Dict[str, str]): A catch-all dictionary for any additional poster URLs.
|
47
|
+
"""
|
48
|
+
thumb: Optional[str] = None
|
49
|
+
small: Optional[str] = None
|
50
|
+
regular: Optional[str] = None
|
51
|
+
large: Optional[str] = None
|
52
|
+
_catchall: Dict[str, str] = field(default_factory=dict)
|
53
|
+
|
54
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
55
|
+
@dataclass
|
56
|
+
class MovieMeterMovie:
|
57
|
+
"""
|
58
|
+
Represents a movie from MovieMeter.
|
59
|
+
|
60
|
+
Attributes:
|
61
|
+
id (Optional[int]): The ID of the movie.
|
62
|
+
objectID (Optional[int]): The object ID of the movie.
|
63
|
+
url (Optional[str]): The URL of the movie.
|
64
|
+
year (Optional[int]): The year of the movie.
|
65
|
+
imdb (Optional[str]): The IMDb ID of the movie.
|
66
|
+
title (Optional[str]): The title of the movie.
|
67
|
+
display_title (Optional[str]): The display title of the movie.
|
68
|
+
alternative_title (Optional[str]): The alternative title of the movie.
|
69
|
+
plot (Optional[str]): The plot of the movie.
|
70
|
+
duration (Optional[int]): The duration of the movie.
|
71
|
+
votes_count (Optional[int]): The count of votes for the movie.
|
72
|
+
average (Optional[float]): The average rating of the movie.
|
73
|
+
average_x2 (Optional[float]): The average rating multiplied by 2.
|
74
|
+
average_x10 (Optional[float]): The average rating multiplied by 10.
|
75
|
+
posters (Optional[Posters]): The posters of the movie.
|
76
|
+
countries (Optional[List[str]]): The countries of the movie.
|
77
|
+
genres (Optional[List[str]]): The genres of the movie.
|
78
|
+
type (Optional[List[str]]): The type of the movie.
|
79
|
+
actors (Optional[List[Actor]]): The actors in the movie.
|
80
|
+
directors (Optional[List[Director]]): The directors of the movie.
|
81
|
+
user_vote (Optional[str]): The user's vote for the movie.
|
82
|
+
_catchall (Dict[str, str]): A catch-all dictionary for additional attributes.
|
83
|
+
"""
|
84
|
+
|
85
|
+
id: Optional[int] = None
|
86
|
+
objectID: Optional[int] = None
|
87
|
+
url: Optional[str] = None
|
88
|
+
year: Optional[int] = None
|
89
|
+
imdb: Optional[str] = None
|
90
|
+
title: Optional[str] = None
|
91
|
+
display_title: Optional[str] = None
|
92
|
+
alternative_title: Optional[str] = None
|
93
|
+
plot: Optional[str] = None
|
94
|
+
duration: Optional[int] = None
|
95
|
+
votes_count: Optional[int] = None
|
96
|
+
average: Optional[float] = None
|
97
|
+
average_x2: Optional[float] = None
|
98
|
+
average_x10: Optional[float] = None
|
99
|
+
posters: Optional[Posters] = None
|
100
|
+
countries: Optional[List[str]] = None
|
101
|
+
genres: Optional[List[str]] = None
|
102
|
+
type: Optional[List[str]] = None
|
103
|
+
actors: Optional[List[Actor]] = None
|
104
|
+
directors: Optional[List[Director]] = None
|
105
|
+
user_vote: Optional[str] = None
|
106
|
+
_catchall: Dict[str, str] = field(default_factory=dict)
|
107
|
+
|
108
|
+
def __post_init__(self):
|
109
|
+
self.actors = [Actor(**actor) if isinstance(actor, dict) else actor for actor in self.actors]
|
110
|
+
self.directors = [Director(**director) if isinstance(director, dict) else director for director in self.directors]
|
111
|
+
self.posters = Posters(**self.posters) if isinstance(self.posters, dict) else self.posters
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import requests
|
2
|
+
import os
|
3
|
+
from typing import Union
|
4
|
+
from plexflow.core.metadata.providers.moviemeter.datatypes import MovieMeterMovie
|
5
|
+
|
6
|
+
def search_movie_by_imdb(imdb_id: str) -> Union[MovieMeterMovie, None]:
|
7
|
+
"""
|
8
|
+
Search for a movie using its IMDB ID.
|
9
|
+
|
10
|
+
Parameters:
|
11
|
+
- imdb_id (str): The IMDB ID of the movie.
|
12
|
+
|
13
|
+
Returns:
|
14
|
+
- MovieMeterMovie: A MovieMeterMovie object containing all the details of the movie.
|
15
|
+
- None: If the movie is not found or an error occurs.
|
16
|
+
|
17
|
+
Example:
|
18
|
+
>>> search_movie_by_imdb('tt0111161')
|
19
|
+
MovieMeterMovie(title='The Shawshank Redemption', release_date=datetime.datetime(1994, 9, 23, 0, 0), runtime=8520, rating=9.3, votes=2400000, rank=1)
|
20
|
+
"""
|
21
|
+
url = f"https://www.moviemeter.nl/api/film/{imdb_id}"
|
22
|
+
headers = {
|
23
|
+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
|
24
|
+
}
|
25
|
+
params = {
|
26
|
+
"api_key": os.getenv("MOVIEMETER_API_KEY")
|
27
|
+
}
|
28
|
+
|
29
|
+
try:
|
30
|
+
r = requests.get(url=url, headers=headers, params=params)
|
31
|
+
r.raise_for_status()
|
32
|
+
except requests.exceptions.HTTPError as errh:
|
33
|
+
raise RuntimeError("HTTP Error occurred") from errh
|
34
|
+
except requests.exceptions.ConnectionError as errc:
|
35
|
+
raise RuntimeError("Connection Error occurred") from errc
|
36
|
+
except requests.exceptions.Timeout as errt:
|
37
|
+
raise RuntimeError("Timeout Error occurred") from errt
|
38
|
+
except requests.exceptions.RequestException as err:
|
39
|
+
raise RuntimeError("Request Exception occurred") from err
|
40
|
+
|
41
|
+
data = r.json()
|
42
|
+
return MovieMeterMovie.from_dict(data)
|
File without changes
|