You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
178 lines
5.7 KiB
Python
178 lines
5.7 KiB
Python
import os, click, requests, json, time, logging
|
|
|
|
class TMDB_REQUEST_EXCEPTION(Exception):
|
|
def __init__(self, statusCode, statusMessage):
|
|
errorMessage = f"TMDB query failed with status code {statusCode}: {statusMessage}"
|
|
super().__init__(errorMessage)
|
|
|
|
class TMDB_API_KEY_NOT_PRESENT_EXCEPTION(Exception):
|
|
def __str__(self):
|
|
return 'TMDB api key is not available, please set environment variable TMDB_API_KEY'
|
|
|
|
class TMDB_EXCESSIVE_USAGE_EXCEPTION(Exception):
|
|
def __str__(self):
|
|
return 'Rate limit was triggered too often'
|
|
|
|
|
|
class TmdbController():
|
|
|
|
DEFAULT_LANGUAGE = 'de-DE'
|
|
|
|
RATE_LIMIT_WAIT_SECONDS = 10
|
|
RATE_LIMIT_RETRIES = 3
|
|
|
|
def __init__(self, context = None):
|
|
self.__context = context
|
|
self.__logger = (context['logger'] if context is not None and 'logger' in context.keys()
|
|
else logging.getLogger('FFX').addHandler(logging.NullHandler()))
|
|
|
|
self.__tmdbApiKey = os.environ.get('TMDB_API_KEY', None)
|
|
if self.__tmdbApiKey is None:
|
|
raise TMDB_API_KEY_NOT_PRESENT_EXCEPTION
|
|
|
|
self.tmdbLanguage = TmdbController.DEFAULT_LANGUAGE
|
|
|
|
|
|
def getTmdbRequest(self, tmdbUrl):
|
|
retries = TmdbController.RATE_LIMIT_RETRIES
|
|
while True:
|
|
response = requests.get(tmdbUrl)
|
|
if response.status_code == 429:
|
|
if not retries:
|
|
raise TMDB_EXCESSIVE_USAGE_EXCEPTION()
|
|
self.__logger.warning('TMDB Rate limit (status_code 429)')
|
|
time.sleep(TmdbController.RATE_LIMIT_WAIT_SECONDS)
|
|
retries -= 1
|
|
else:
|
|
jsonResult = response.json()
|
|
if ('success' in jsonResult.keys()
|
|
and not jsonResult['success']):
|
|
raise TMDB_REQUEST_EXCEPTION(jsonResult['status_code'], jsonResult['status_message'])
|
|
return jsonResult
|
|
|
|
|
|
def queryShow(self, showId):
|
|
"""
|
|
First level keys in the response object:
|
|
adult bool
|
|
backdrop_path str
|
|
created_by []
|
|
episode_run_time []
|
|
first_air_date str YYYY-MM-DD
|
|
genres []
|
|
homepage str
|
|
id int
|
|
in_production bool
|
|
languages []
|
|
last_air_date str YYYY-MM-DD
|
|
last_episode_to_air {}
|
|
name str
|
|
next_episode_to_air null
|
|
networks []
|
|
number_of_episodes int
|
|
number_of_seasons int
|
|
origin_country []
|
|
original_language str
|
|
original_name str
|
|
overview str
|
|
popularity float
|
|
poster_path str
|
|
production_companies []
|
|
production_countries []
|
|
seasons []
|
|
spoken_languages []
|
|
status str
|
|
tagline str
|
|
type str
|
|
vote_average float
|
|
vote_count int
|
|
"""
|
|
|
|
urlParams = f"?language={self.tmdbLanguage}&api_key={self.__tmdbApiKey}"
|
|
|
|
tmdbUrl = f"https://api.themoviedb.org/3/tv/{showId}{urlParams}"
|
|
|
|
return self.getTmdbRequest(tmdbUrl)
|
|
|
|
|
|
def queryEpisode(self, showId, season, episode):
|
|
"""
|
|
First level keys in the response object:
|
|
air_date str 'YYY-MM-DD'
|
|
crew []
|
|
episode_number int
|
|
guest_stars []
|
|
name str
|
|
overview str
|
|
id int
|
|
production_code
|
|
runtime int
|
|
season_number int
|
|
still_path str '/filename.jpg'
|
|
vote_average float
|
|
vote_count int
|
|
"""
|
|
|
|
urlParams = f"?language={self.tmdbLanguage}&api_key={self.__tmdbApiKey}"
|
|
|
|
tmdbUrl = f"https://api.themoviedb.org/3/tv/{showId}/season/{season}/episode/{episode}{urlParams}"
|
|
|
|
return self.getTmdbRequest(tmdbUrl)
|
|
|
|
|
|
@staticmethod
|
|
def getEpisodeFileBasename(showName,
|
|
episodeName,
|
|
season,
|
|
episode,
|
|
indexSeasonDigits = 2,
|
|
indexEpisodeDigits = 2,
|
|
indicatorSeasonDigits = 2,
|
|
indicatorEpisodeDigits = 2):
|
|
"""
|
|
One Piece:
|
|
indexSeasonDigits = 0,
|
|
indexEpisodeDigits = 4,
|
|
indicatorSeasonDigits = 2,
|
|
indicatorEpisodeDigits = 4
|
|
|
|
Three-Body:
|
|
indexSeasonDigits = 0,
|
|
indexEpisodeDigits = 2,
|
|
indicatorSeasonDigits = 2,
|
|
indicatorEpisodeDigits = 2
|
|
|
|
Dragonball:
|
|
indexSeasonDigits = 0,
|
|
indexEpisodeDigits = 3,
|
|
indicatorSeasonDigits = 2,
|
|
indicatorEpisodeDigits = 3
|
|
|
|
Boruto:
|
|
indexSeasonDigits = 0,
|
|
indexEpisodeDigits = 4,
|
|
indicatorSeasonDigits = 2,
|
|
indicatorEpisodeDigits = 4
|
|
"""
|
|
filenameTokens = [str(showName), ' - ']
|
|
|
|
if indexSeasonDigits:
|
|
filenameTokens += ['{num:{fill}{width}}'.format(num=season, fill='0', width=indexSeasonDigits)]
|
|
if indexEpisodeDigits:
|
|
filenameTokens += ['{num:{fill}{width}}'.format(num=episode, fill='0', width=indexEpisodeDigits)]
|
|
if indexSeasonDigits or indexEpisodeDigits:
|
|
filenameTokens += [' ']
|
|
|
|
filenameTokens += [episodeName]
|
|
|
|
if indicatorSeasonDigits or indicatorEpisodeDigits:
|
|
filenameTokens += [' - ']
|
|
if indicatorSeasonDigits:
|
|
filenameTokens += ['S{num:{fill}{width}}'.format(num=season, fill='0', width=indicatorSeasonDigits)]
|
|
if indicatorEpisodeDigits:
|
|
filenameTokens += ['E{num:{fill}{width}}'.format(num=episode, fill='0', width=indicatorEpisodeDigits)]
|
|
|
|
return ''.join(filenameTokens)
|
|
|
|
def importShow(self, showId: int):
|
|
pass |