Source code for mymeco.tmdb.utils

# coding: utf-8
"""Utility module to connect and retrieve information from TMDb."""
import os
import typing
import logging
import json
import datetime
import urllib.parse
import requests


[docs] class MovieSearch(typing.NamedTuple): """Basic information on a movie.""" title: str original_title: str release_date: typing.Union[datetime.date, None] overview: typing.Optional[str] poster_path: typing.Optional[str] id: int
[docs] class Tmdb: """Main class to request data on TMDb API v3.""" _log: logging.Logger = logging.getLogger(__file__) __api_version: int = 3 __base_url: str = 'https://api.themoviedb.org/' + str(__api_version) def __build_request_url(self, route: str, **kwargs: typing.Optional[str]) -> str: kwargs['api_key'] = self.__apikey kwargs['language'] = self.__language api_url = ( self.__base_url + route + '?' + urllib.parse.urlencode(kwargs) ) self._log.debug(api_url) return api_url def __init__(self, apikey: str, lang: typing.Optional[str] = None): """ Build a new TMDb client instance. :param apikey: TMDb API key. A new API key could be obtains through https://www.themoviedb.org/settings/api. :param lang: Optional request language. If not set, class will fallback to language as described by *LANG* environment variable. """ self.__apikey = apikey if lang is not None: self.__language = lang else: self.__language = os.environ.get('LANG', default='en_US.utf-8') self.__language = self.__language.split('.')[0].replace('_', '-') self.__configuration = requests.get( self.__build_request_url('/configuration'), headers={ 'Content-Type': 'Application/json; charset=utf-8' } ).json() self._log.debug(json.dumps(self.__configuration, indent=4)) def __repr__(self) -> str: """Get a representation of instance.""" return '{}({!r}, lang={!r})'.format( type(self).__name__, self.__apikey, self.__language )
[docs] def search_movie( self, title: str, year: typing.Union[int, None] = None, page: int = 1 ) -> typing.Generator[MovieSearch, None, None]: """ Search a movie, given its title and optionally its release year. :param title: Movie title, could be original title or localized title. Could also be a partial title. :param year: Movie release date. :param page: Requested page. :return: List generator with all matched movie. Each result consists in a named tuple with the following keys: - *title*: localized movie title - *original_title*: original movie title - *release_date*: movie release date - *overview*: short movie summary - *poster_path*: URL to retrieve small poster - *id*: TMDb movie ID, useful to retrieve full movie details """ results = requests.get( self.__build_request_url('/search/movie', query=title, page=page), headers={ 'Content-Type': 'Application/json; charset=utf-8' } ).json() movie: typing.Mapping[str, str] for movie in results['results']: self._log.debug(json.dumps(movie, indent=4)) # Compute release date and filter result if needed release_date: typing.Union[datetime.date, None] try: release_date = datetime.date.fromisoformat( movie.get('release_date', '0000-00-00') ) except ValueError: self._log.warning('No release_date given, ignore') release_date = None if (year is not None and ( release_date is None or year != release_date.year )): continue # Compute title out_title: str out_title = movie.get('title', '') # Compute original title original_title: str original_title = movie.get('original_title', title) # Compute poster path self._update_path(movie, 'poster_path', 'poster_sizes') poster: typing.Optional[str] poster = movie.get('poster_path', None) # Compute overview overview: typing.Optional[str] overview = movie.get('overview', None) # Compute movie id movie_id: int movie_id = int(movie.get('id', 0)) yield MovieSearch( out_title, original_title, release_date, overview, poster, movie_id ) if page < results['total_pages']: yield from self.search_movie(title, year, page + 1)
def _update_path(self, data, key, base): path: typing.Optional[str] path = data.get(key, None) if path is not None: data[key] = ( self.__configuration['images']['secure_base_url'] + self.__configuration['images'][base][-1] + path )
[docs] def get_movie(self, movie_id: int): """Get all information about a given movie.""" result = requests.get( self.__build_request_url('/movie/{}'.format(movie_id)), headers={ 'Content-Type': 'Application/json; charset=utf-8' } ).json() self._update_path(result, 'poster_path', 'poster_sizes') self._log.debug(json.dumps(result, indent=4)) return result
[docs] def get_credits(self, movie_id: int): """Get cast and crew for a movie.""" result = requests.get( self.__build_request_url('/movie/{}/credits'.format(movie_id)), headers={ 'Content-Type': 'Application/json; charset=utf-8' } ).json() for cast in result['cast']: self._update_path(cast, 'profile_path', 'profile_sizes') self._log.debug(json.dumps(result, indent=4)) return result