From 1fc742391409413da9224fc8bcfc5b8f107b556b Mon Sep 17 00:00:00 2001 From: Jakub Molinski Date: Sat, 30 Mar 2019 21:07:10 +0100 Subject: [PATCH 1/2] Add search interface --- trakt/api.py | 4 + trakt/core/paths/__init__.py | 1 + trakt/core/paths/endpoint_mappings/search.py | 89 ++++++++++++++++++++ trakt/core/paths/response_structs.py | 11 +++ 4 files changed, 105 insertions(+) create mode 100644 trakt/core/paths/endpoint_mappings/search.py diff --git a/trakt/api.py b/trakt/api.py index 6d461d0..2f3f83b 100644 --- a/trakt/api.py +++ b/trakt/api.py @@ -18,6 +18,7 @@ MoviesI, NetworksI, PeopleI, + SearchI, ShowsI, ) @@ -45,6 +46,7 @@ def __init__( people_interface: Optional[Type[PeopleI]] = None, networks_interface: Optional[Type[NetworksI]] = None, comments_interface: Optional[Type[CommentsI]] = None, + search_interface: Optional[Type[SearchI]] = None, user: Optional[TraktCredentials] = None, auto_refresh_token: bool = False, **config: str @@ -79,6 +81,7 @@ def __init__( self.people = (people_interface or PeopleI)(self, Executor) self.networks = (networks_interface or NetworksI)(self, Executor) self.comments = (comments_interface or CommentsI)(self, Executor) + self.search = (search_interface or SearchI)(self, Executor) def request(self, params: Union[str, List[str]], **kwargs: Any) -> Any: if isinstance(params, str): @@ -111,4 +114,5 @@ def _get_executor_paths(self) -> List[SuiteInterface]: self.movies, self.people, self.networks, + self.search, ] diff --git a/trakt/core/paths/__init__.py b/trakt/core/paths/__init__.py index 27acc1c..75a94cc 100644 --- a/trakt/core/paths/__init__.py +++ b/trakt/core/paths/__init__.py @@ -14,3 +14,4 @@ ) from trakt.core.paths.endpoint_mappings.movies import MoviesI from trakt.core.paths.endpoint_mappings.people import PeopleI +from trakt.core.paths.endpoint_mappings.search import SearchI diff --git a/trakt/core/paths/endpoint_mappings/search.py b/trakt/core/paths/endpoint_mappings/search.py new file mode 100644 index 0000000..b680dde --- /dev/null +++ b/trakt/core/paths/endpoint_mappings/search.py @@ -0,0 +1,89 @@ +from typing import Iterable, List, Optional, Union + +from trakt.core.paths.path import Path +from trakt.core.paths.response_structs import SearchResult +from trakt.core.paths.suite_interface import SuiteInterface +from trakt.core.paths.validators import ALL_FILTERS, PerArgValidator + +MEDIA_TYPES = ["movie", "show", "episode", "person", "list"] +ID_TYPES = ["trakt", "imdb", "tmdb", "tvdb"] +POSSIBLE_FIELDS = { + "title", + "tagline", + "overview", + "people", + "translations", + "aliases", + "name", + "biography", + "description", +} + + +class SearchI(SuiteInterface): + name = "search" + + paths = { + "text_query": Path( # type: ignore + "search/!type", + [SearchResult], + extended=["full"], + filters=ALL_FILTERS, + pagination=True, + validators=[ + PerArgValidator("type", lambda t: t in MEDIA_TYPES), + PerArgValidator("query", lambda q: isinstance(q, str) and q), + PerArgValidator( + "fields", lambda f: [x in POSSIBLE_FIELDS for x in f.split(",")] + ), + ], + qargs=["fields"], + ), + "id_lookup": Path( # type: ignore + "search/!id_type/!id", + [SearchResult], + extended=["full"], + filters=ALL_FILTERS, + pagination=True, + validators=[ + PerArgValidator("id", lambda t: isinstance(t, (int, str))), + PerArgValidator("id_type", lambda it: it in ID_TYPES), + PerArgValidator( + "type", lambda f: [x in MEDIA_TYPES for x in f.split(",")] + ), + ], + qargs=["type"], + ), + } + + def text_query( + self, + type: Union[str, List[str]], + query: str, + fields: Optional[Union[str, List[str]]] = None, + **kwargs + ) -> Iterable[SearchResult]: + type = [type] if isinstance(type, str) else type + type = ",".join(type) + req = {"type": type, "query": query} + + if fields: + fields = [fields] if isinstance(fields, str) else fields + req["fields"] = ",".join(fields) + + return self.run("text_query", **kwargs, **req) + + def id_lookup( + self, + id_type: str, + id: Union[str, int], + type: Optional[Union[str, List[str]]] = None, + **kwargs + ) -> Iterable[SearchResult]: + req = {"id_type": type, "id": id} + + if type: + type = [type] if isinstance(type, str) else type + req["type"] = ",".join(type) + + return self.run("id_lookup", **kwargs, **req) diff --git a/trakt/core/paths/response_structs.py b/trakt/core/paths/response_structs.py index 83b74ba..316c3c1 100644 --- a/trakt/core/paths/response_structs.py +++ b/trakt/core/paths/response_structs.py @@ -281,3 +281,14 @@ class CommentAndItem: episode: Optional[Episode] = None show: Optional[Show] = None season: Optional[Season] = None + + +@dataclass +class SearchResult: + type: str + score: Optional[float] + movie: Optional[Movie] = None + list: Optional[TraktList] = None + person: Optional[Person] = None + episode: Optional[Episode] = None + show: Optional[Show] = None From 3ab0a4da19aff85c5f68ac78a9fb21b0d3b1583a Mon Sep 17 00:00:00 2001 From: Jakub Molinski Date: Sun, 31 Mar 2019 01:43:16 +0100 Subject: [PATCH 2/2] isort --- trakt/core/paths/response_structs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trakt/core/paths/response_structs.py b/trakt/core/paths/response_structs.py index 40a3e31..c68fe94 100644 --- a/trakt/core/paths/response_structs.py +++ b/trakt/core/paths/response_structs.py @@ -293,7 +293,8 @@ class SearchResult: episode: Optional[Episode] = None show: Optional[Show] = None -@dataclass + +@dataclass class MovieScrobble: id: int action: str @@ -310,4 +311,3 @@ class EpisodeScrobble: sharing: Sharing episode: Episode show: Show -