示例#1
0
    def search_show(self,
                    show_name: str = None,
                    imdb_name: str = None,
                    *,
                    timeout: float = 10.0) -> List[Show]:
        """Search for shows matching the name supplied.

        If no matching show is found, a NotFoundException will be thrown.
        """

        if not show_name and not imdb_name:
            return []

        params = encoded_name = ""

        if show_name:
            params = "name={}".format(show_name)
            encoded_name = urllib.parse.quote(show_name)
        elif imdb_name:
            params = "imdbId={}".format(imdb_name)
            encoded_name = urllib.parse.quote(imdb_name)

        Log.info(f"Searching for show: {show_name} {imdb_name}")

        shows_data = self.get(f"search/series?{params}", timeout=timeout)

        shows = []

        for show_data in shows_data:
            show = deserialize.deserialize(Show, show_data)
            shows.append(show)

        return shows
示例#2
0
    def _check_errors(response: requests.Response) -> Any:
        """Check an API response for errors."""

        if response.status_code >= 200 and response.status_code < 300:
            return

        Log.error(f"Bad response code from API: {response.status_code}")

        # Try and read the JSON. If we don't have it, we return the generic
        # exception type
        try:
            data = response.json()
        except json.JSONDecodeError:
            raise TVDBException(
                f"Could not decode error response: {response.text}")

        # Try and get the error message so we can use it
        error = data.get("Error")

        # If we don't have it, just return the generic exception type
        if error is None:
            raise TVDBException(
                f"Could not get error information: {response.text}")

        if error == "Resource not found":
            raise NotFoundException(f"Could not find resource: {response.url}")

        raise TVDBException(f"Unknown error: {response.text}")
示例#3
0
    def search_show(self,
                    show_name: str,
                    *,
                    timeout: float = 10.0) -> List[Show]:
        """Search for shows matching the name supplied."""

        if show_name is None or show_name == "":
            return []

        encoded_name = urllib.parse.quote(show_name)

        Log.info(f"Searching for show: {show_name}")

        shows_data = self.get(f"search?type=series&query={encoded_name}",
                              timeout=timeout)

        shows = []

        for show_data in shows_data:
            show = deserialize.deserialize(Show,
                                           show_data,
                                           throw_on_unhandled=True)
            shows.append(show)

        return shows
示例#4
0
    def get(self, url_path: str, *, timeout: float) -> Any:
        """Search for shows matching the name supplied.

        If no matching show is found, a NotFoundException will be thrown.
        """

        if url_path is None or url_path == "":
            raise AttributeError("An invalid URL path was supplied")

        self.authenticate()

        Log.info(f"GET: {url_path}")

        response = requests.get(self._expand_url(url_path),
                                headers=self._construct_headers(),
                                timeout=timeout)

        TVDBClient._check_errors(response)

        content = response.json()

        data = content.get("data")

        if data is None:
            raise NotFoundException(f"Could not get data for path: {url_path}")

        return data
示例#5
0
    def show_info(self, show_identifier: int, *, timeout: float = 10.0) -> Optional[Show]:
        """Get the full information for the show with the given identifier."""

        Log.info(f"Fetching data for show: {show_identifier}")

        show_data = self.get(f"series/{show_identifier}", timeout=timeout)

        return deserialize.deserialize(Show, show_data)
示例#6
0
    def episode_by_id(self, episode_identifier: int, timeout: float = 10.0) -> Episode:
        """Get the episode information from its ID."""

        Log.info(f"Fetching info for episode id: {episode_identifier}")

        episode_data = self.get(f"episodes/{episode_identifier}", timeout=timeout)

        print(episode_data)

        return deserialize.deserialize(Episode, episode_data)
示例#7
0
    def get_paged(self,
                  url_path: str,
                  *,
                  timeout: float,
                  key: Optional[str] = None) -> List[Any]:
        """Get paged data."""

        if url_path is None or url_path == "":
            raise AttributeError("An invalid URL path was supplied")

        self.authenticate()

        url_path = self._expand_url(url_path)

        all_results: List[Any] = []

        while True:

            Log.info(f"GET: {url_path}")

            response = requests.get(
                url_path,
                headers=self._construct_headers(),
                timeout=timeout,
            )

            TVDBClient._check_errors(response)

            content = response.json()

            data = content.get("data")

            if data is None:
                raise NotFoundException(
                    f"Could not get data for path: {url_path}")

            if key is None:
                all_results += data
            else:
                all_results += data[key]

            links = content.get("links")

            if links is None:
                break

            if links.get("next"):
                Log.debug("Fetching next page")
                url_path = links["next"]
            else:
                break

        return all_results
示例#8
0
    def episodes_from_show_id(self, show_identifier: int, timeout: float = 10.0) -> List[Episode]:
        """Get the episodes in the given show."""

        Log.info(f"Fetching episodes for show id: {show_identifier}")

        episode_data = self.get_paged(f"series/{show_identifier}/episodes", timeout=timeout)

        episodes: List[Episode] = []

        for episode_data_item in episode_data:
            episodes.append(deserialize.deserialize(Episode, episode_data_item))

        return episodes
示例#9
0
    def actors_from_show_id(self, show_identifier: int, timeout: float = 10.0) -> List[Actor]:
        """Get the actors in the given show."""

        Log.info(f"Fetching actors for show id: {show_identifier}")

        actor_data = self.get(f"series/{show_identifier}/actors", timeout=timeout)

        actors: List[Actor] = []

        for actor_data_item in actor_data:
            actors.append(deserialize.deserialize(Actor, actor_data_item))

        return actors
示例#10
0
    def episode_by_id(self,
                      episode_identifier: int,
                      timeout: float = 10.0) -> Episode:
        """Get the episode information from its ID."""

        Log.info(f"Fetching info for episode id: {episode_identifier}")

        episode_data = self.get(f"episodes/{episode_identifier}/extended",
                                timeout=timeout)

        return deserialize.deserialize(Episode,
                                       episode_data,
                                       throw_on_unhandled=True)
示例#11
0
    def get_paged(self, url_path: str, *, timeout: float) -> List[Any]:
        """Get paged data."""

        if url_path is None or url_path == "":
            raise AttributeError("An invalid URL path was supplied")

        self.authenticate()

        page = 0

        all_results: List[Any] = []

        while True:

            if page != 0:
                url_path += f"?page={page}"

            Log.info(f"GET: {url_path}")

            response = requests.get(
                self._expand_url(url_path),
                headers=self._construct_headers(),
                timeout=timeout
            )

            TVDBClient._check_errors(response)

            content = response.json()

            data = content.get('data')

            if data is None:
                raise NotFoundException(f"Could not get data for path: {url_path}")

            all_results += data

            links = content.get('links')

            if links is None:
                break

            if links.get('next'):
                Log.debug("Fetching next page")
                page = links["next"]
            else:
                break

        return all_results
示例#12
0
    def search_show(self, show_name: str, *, timeout: float = 10.0) -> List[Show]:
        """Search for shows matching the name supplied.

        If no matching show is found, a NotFoundException will be thrown.
        """

        if show_name is None or show_name == "":
            return []

        encoded_name = urllib.parse.quote(show_name)

        Log.info(f"Searching for show: {show_name}")

        shows_data = self.get(f"search/series?name={encoded_name}", timeout=timeout)

        shows = []

        for show_data in shows_data:
            show = deserialize.deserialize(Show, show_data)
            shows.append(show)

        return shows
示例#13
0
    def episodes_from_show_id(self,
                              show_identifier: Union[int, str],
                              timeout: float = 10.0) -> List[Episode]:
        """Get the episodes in the given show."""

        Log.info(f"Fetching episodes for show id: {show_identifier}")

        episode_data = self.get_paged(
            f"series/{show_identifier}/episodes/default",
            timeout=timeout,
            key="episodes",
        )

        episodes: List[Episode] = []

        for episode_data_item in episode_data:
            episodes.append(
                deserialize.deserialize(Episode,
                                        episode_data_item,
                                        throw_on_unhandled=True))

        return episodes
示例#14
0
    def authenticate(self):
        """Authenticate the client with the API.

        This will exit early if we are already authenticated. It does not need
        to be called. All calls requiring that the client is authenticated will
        call this.
        """

        if self.auth_token is not None:
            Log.debug("Already authenticated, skipping")
            return

        Log.info("Authenticating...")

        login_body = {
            "apikey": self.api_key,
            "userkey": self.user_key,
            "username": self.user_name,
        }

        for i in range(0, TVDBClient.Constants.MAX_AUTH_RETRY_COUNT):
            try:
                response = requests.post(
                    self._expand_url("login"),
                    json=login_body,
                    headers=self._construct_headers(),
                    timeout=TVDBClient.Constants.AUTH_TIMEOUT,
                )

                # Since we authenticated successfully, we can break out of the
                # retry loop
                break
            except requests.exceptions.Timeout:
                will_retry = i < (TVDBClient.Constants.MAX_AUTH_RETRY_COUNT -
                                  1)
                if will_retry:
                    Log.warning("Authentication timed out, but will retry.")
                else:
                    Log.error(
                        "Authentication timed out maximum number of times.")
                    raise Exception(
                        "Authentication timed out maximum number of times.")

        if response.status_code < 200 or response.status_code >= 300:
            Log.error(
                f"Authentication failed withs status code: {response.status_code}"
            )
            raise TVDBAuthenticationException(
                f"Authentication failed with status code: {response.status_code}"
            )

        content = response.json()
        token = content.get("token")

        if token is None:
            Log.error("Failed to get token from login request")
            raise TVDBAuthenticationException(
                "Failed to get token from login request")

        self.auth_token = token

        Log.info("Authenticated successfully")