mirror of
https://github.com/SickGear/SickGear.git
synced 2025-01-05 17:43:37 +00:00
Merge pull request #942 from JackDandy/feature/ChangeTVDBv2
Change use TVDb API v2.
This commit is contained in:
commit
0cf9cd5052
6 changed files with 194 additions and 232 deletions
|
@ -57,6 +57,7 @@
|
|||
* Add persistent hide/unhide cards to Add show/Trakt and Add show/IMDb Cards
|
||||
* Change simplify dropdowns at all Add show/Cards
|
||||
* Change cosmetic title on shutdown
|
||||
* Change use TVDb API v2
|
||||
|
||||
|
||||
[develop changelog]
|
||||
|
|
|
@ -5,40 +5,36 @@
|
|||
# repository:http://github.com/dbr/tvdb_api
|
||||
# license:unlicense (http://unlicense.org/)
|
||||
|
||||
import traceback
|
||||
from functools import wraps
|
||||
|
||||
__author__ = 'dbr/Ben'
|
||||
__version__ = '1.9'
|
||||
__version__ = '2.0'
|
||||
__api_version__ = '2.1.2'
|
||||
|
||||
import os
|
||||
import time
|
||||
import getpass
|
||||
import StringIO
|
||||
import tempfile
|
||||
import warnings
|
||||
import logging
|
||||
import zipfile
|
||||
import requests
|
||||
import requests.exceptions
|
||||
|
||||
try:
|
||||
import gzip
|
||||
except ImportError:
|
||||
gzip = None
|
||||
import datetime
|
||||
from sickbeard.helpers import getURL
|
||||
|
||||
from lib.dateutil.parser import parse
|
||||
from lib.cachecontrol import CacheControl, caches
|
||||
|
||||
from lib.etreetodict import ConvertXmlToDict
|
||||
from tvdb_ui import BaseUI, ConsoleUI
|
||||
from tvdb_exceptions import (tvdb_error, tvdb_shownotfound,
|
||||
tvdb_seasonnotfound, tvdb_episodenotfound, tvdb_attributenotfound)
|
||||
|
||||
from sickbeard import logger
|
||||
|
||||
def log():
|
||||
return logging.getLogger('tvdb_api')
|
||||
|
||||
|
||||
def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logr=None):
|
||||
def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None):
|
||||
"""Retry calling the decorated function using an exponential backoff.
|
||||
|
||||
http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/
|
||||
|
@ -54,8 +50,8 @@ def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logr=None):
|
|||
:param backoff: backoff multiplier e.g. value of 2 will double the delay
|
||||
each retry
|
||||
:type backoff: int
|
||||
:param logr: logger to use. If None, print
|
||||
:type logr: logging.Logger instance
|
||||
:param logger: logger to use. If None, print
|
||||
:type logger: logging.Logger instance
|
||||
"""
|
||||
|
||||
def deco_retry(f):
|
||||
|
@ -67,9 +63,9 @@ def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logr=None):
|
|||
try:
|
||||
return f(*args, **kwargs)
|
||||
except ExceptionToCheck, e:
|
||||
msg = 'TVDB_API :: %s, Retrying in %d seconds...' % (str(e), mdelay)
|
||||
if logr:
|
||||
logger.log(msg, logger.WARNING)
|
||||
msg = '%s, Retrying in %d seconds...' % (str(e), mdelay)
|
||||
if logger:
|
||||
logger.warning(msg)
|
||||
else:
|
||||
print msg
|
||||
time.sleep(mdelay)
|
||||
|
@ -86,7 +82,8 @@ class ShowContainer(dict):
|
|||
"""Simple dict that holds a series of Show instances
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, **kwargs):
|
||||
super(ShowContainer, self).__init__(**kwargs)
|
||||
self._stack = []
|
||||
self._lastgc = time.time()
|
||||
|
||||
|
@ -145,7 +142,7 @@ class Show(dict):
|
|||
# doesn't exist, so attribute error.
|
||||
raise tvdb_attributenotfound('Cannot find attribute %s' % (repr(key)))
|
||||
|
||||
def airedOn(self, date):
|
||||
def aired_on(self, date):
|
||||
ret = self.search(str(date), 'firstaired')
|
||||
if 0 == len(ret):
|
||||
raise tvdb_episodenotfound('Could not find any episodes that aired on %s' % date)
|
||||
|
@ -166,9 +163,9 @@ class Show(dict):
|
|||
Search terms are converted to lower case (unicode) strings.
|
||||
|
||||
# Examples
|
||||
|
||||
|
||||
These examples assume t is an instance of Tvdb():
|
||||
|
||||
|
||||
>> t = Tvdb()
|
||||
>>
|
||||
|
||||
|
@ -212,9 +209,10 @@ class Show(dict):
|
|||
|
||||
|
||||
class Season(dict):
|
||||
def __init__(self, show=None):
|
||||
def __init__(self, show=None, **kwargs):
|
||||
"""The show attribute points to the parent show
|
||||
"""
|
||||
super(Season, self).__init__(**kwargs)
|
||||
self.show = show
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -251,9 +249,10 @@ class Season(dict):
|
|||
|
||||
|
||||
class Episode(dict):
|
||||
def __init__(self, season=None):
|
||||
def __init__(self, season=None, **kwargs):
|
||||
"""The season attribute points to the parent season
|
||||
"""
|
||||
super(Episode, self).__init__(**kwargs)
|
||||
self.season = season
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -301,7 +300,7 @@ class Episode(dict):
|
|||
raise TypeError('must supply string to search for (contents)')
|
||||
|
||||
term = unicode(term).lower()
|
||||
for cur_key, cur_value in self.items():
|
||||
for cur_key, cur_value in self.iteritems():
|
||||
cur_key, cur_value = unicode(cur_key).lower(), unicode(cur_value).lower()
|
||||
if None is not key and cur_key != key:
|
||||
# Do not search this key
|
||||
|
@ -343,13 +342,12 @@ class Tvdb:
|
|||
debug=False,
|
||||
cache=True,
|
||||
banners=False,
|
||||
fanart=False,
|
||||
actors=False,
|
||||
custom_ui=None,
|
||||
language=None,
|
||||
search_all_languages=False,
|
||||
apikey=None,
|
||||
forceConnect=False,
|
||||
useZip=False,
|
||||
dvdorder=False,
|
||||
proxy=None):
|
||||
|
||||
|
@ -413,16 +411,6 @@ class Tvdb:
|
|||
tvdb_api in a larger application)
|
||||
See http://thetvdb.com/?tab=apiregister to get your own key
|
||||
|
||||
forceConnect (bool):
|
||||
If true it will always try to connect to theTVDB.com even if we
|
||||
recently timed out. By default it will wait one minute before
|
||||
trying again, and any requests within that one minute window will
|
||||
return an exception immediately.
|
||||
|
||||
useZip (bool):
|
||||
Download the zip archive where possibale, instead of the xml.
|
||||
This is only used when all episodes are pulled.
|
||||
And only the main language xml is used, the actor and banner xml are lost.
|
||||
"""
|
||||
|
||||
self.shows = ShowContainer() # Holds all Show classes
|
||||
|
@ -435,6 +423,8 @@ class Tvdb:
|
|||
else:
|
||||
self.config['apikey'] = '0629B785CE550C8D' # tvdb_api's API key
|
||||
|
||||
self.token = {'token': None, 'datetime': datetime.datetime.fromordinal(1)}
|
||||
|
||||
self.config['debug_enabled'] = debug # show debugging messages
|
||||
|
||||
self.config['custom_ui'] = custom_ui
|
||||
|
@ -445,8 +435,6 @@ class Tvdb:
|
|||
|
||||
self.config['search_all_languages'] = search_all_languages
|
||||
|
||||
self.config['useZip'] = useZip
|
||||
|
||||
self.config['dvdorder'] = dvdorder
|
||||
|
||||
self.config['proxy'] = proxy
|
||||
|
@ -463,6 +451,7 @@ class Tvdb:
|
|||
raise ValueError('Invalid value for Cache %r (type was %s)' % (cache, type(cache)))
|
||||
|
||||
self.config['banners_enabled'] = banners
|
||||
self.config['fanart_enabled'] = fanart
|
||||
self.config['actors_enabled'] = actors
|
||||
|
||||
if self.config['debug_enabled']:
|
||||
|
@ -498,26 +487,37 @@ class Tvdb:
|
|||
|
||||
# The following url_ configs are based of the
|
||||
# http://thetvdb.com/wiki/index.php/Programmers_API
|
||||
self.config['base_url'] = 'http://thetvdb.com'
|
||||
self.config['base_url'] = 'https://api.thetvdb.com/'
|
||||
|
||||
if self.config['search_all_languages']:
|
||||
self.config['url_get_series'] = u'%(base_url)s/api/GetSeries.php' % self.config
|
||||
self.config['params_get_series'] = {'seriesname': '', 'language': 'all'}
|
||||
else:
|
||||
self.config['url_get_series'] = u'%(base_url)s/api/GetSeries.php' % self.config
|
||||
self.config['params_get_series'] = {'seriesname': '', 'language': self.config['language']}
|
||||
self.config['url_get_series'] = '%(base_url)s/search/series' % self.config
|
||||
self.config['params_get_series'] = {'name': ''}
|
||||
|
||||
self.config['url_epInfo'] = u'%(base_url)s/api/%(apikey)s/series/%%s/all/%%s.xml' % self.config
|
||||
self.config['url_epInfo_zip'] = u'%(base_url)s/api/%(apikey)s/series/%%s/all/%%s.zip' % self.config
|
||||
self.config['url_epInfo'] = '%(base_url)sseries/%%s/episodes?page=%%s' % self.config
|
||||
|
||||
self.config['url_seriesInfo'] = u'%(base_url)s/api/%(apikey)s/series/%%s/%%s.xml' % self.config
|
||||
self.config['url_actorsInfo'] = u'%(base_url)s/api/%(apikey)s/series/%%s/actors.xml' % self.config
|
||||
self.config['url_seriesInfo'] = '%(base_url)sseries/%%s' % self.config
|
||||
self.config['url_actorsInfo'] = '%(base_url)sseries/%%s/actors' % self.config
|
||||
|
||||
self.config['url_seriesBanner'] = u'%(base_url)s/api/%(apikey)s/series/%%s/banners.xml' % self.config
|
||||
self.config['url_artworkPrefix'] = u'%(base_url)s/banners/%%s' % self.config
|
||||
self.config['url_seriesBanner'] = '%(base_url)sseries/%%s/images/query?keyType=%%s' % self.config
|
||||
self.config['url_artworkPrefix'] = 'https://thetvdb.com/banners/%s'
|
||||
|
||||
def log(self, msg, log_level=logger.DEBUG):
|
||||
logger.log('TVDB_API :: %s' % (msg.replace(self.config['apikey'], '<apikey>')), log_level=log_level)
|
||||
def get_new_token(self):
|
||||
token = None
|
||||
url = '%s%s' % (self.config['base_url'], 'login')
|
||||
params = {'apikey': self.config['apikey']}
|
||||
resp = getURL(url.strip(), post_json=params, json=True)
|
||||
if resp:
|
||||
if 'token' in resp:
|
||||
token = resp['token']
|
||||
|
||||
return {'token': token, 'datetime': datetime.datetime.now()}
|
||||
|
||||
def get_token(self):
|
||||
if self.token.get('token') is None or datetime.datetime.now() - self.token.get(
|
||||
'datetime', datetime.datetime.fromordinal(1)) > datetime.timedelta(hours=23):
|
||||
self.token = self.get_new_token()
|
||||
if not self.token.get('token'):
|
||||
raise tvdb_error('Could not get Authentification Token')
|
||||
return self.token.get('token')
|
||||
|
||||
@staticmethod
|
||||
def _get_temp_dir():
|
||||
|
@ -537,7 +537,7 @@ class Tvdb:
|
|||
|
||||
@retry(tvdb_error)
|
||||
def _load_url(self, url, params=None, language=None):
|
||||
self.log('Retrieving URL %s' % url)
|
||||
log().debug('Retrieving URL %s' % url)
|
||||
|
||||
session = requests.session()
|
||||
|
||||
|
@ -545,56 +545,60 @@ class Tvdb:
|
|||
session = CacheControl(session, cache=caches.FileCache(self.config['cache_location']))
|
||||
|
||||
if self.config['proxy']:
|
||||
self.log('Using proxy for URL: %s' % url)
|
||||
log().debug('Using proxy for URL: %s' % url)
|
||||
session.proxies = {'http': self.config['proxy'], 'https': self.config['proxy']}
|
||||
|
||||
session.headers.update({'Accept-Encoding': 'gzip,deflate'})
|
||||
session.headers.update({'Accept-Encoding': 'gzip,deflate', 'Authorization': 'Bearer %s' % self.get_token(),
|
||||
'Accept': 'application/vnd.thetvdb.v%s' % __api_version__})
|
||||
|
||||
try:
|
||||
resp = session.get(url.strip(), params=params)
|
||||
except requests.exceptions.HTTPError, e:
|
||||
raise tvdb_error('HTTP error %s while loading URL %s' % (e.errno, url))
|
||||
except requests.exceptions.ConnectionError, e:
|
||||
raise tvdb_error('Connection error %s while loading URL %s' % (e.message, url))
|
||||
except requests.exceptions.Timeout, e:
|
||||
raise tvdb_error('Connection timed out %s while loading URL %s' % (e.message, url))
|
||||
except Exception:
|
||||
raise tvdb_error('Unknown exception while loading URL %s: %s' % (url, traceback.format_exc()))
|
||||
if None is not language and language in self.config['valid_languages']:
|
||||
session.headers.update({'Accept-Language': language})
|
||||
|
||||
def process_data(data):
|
||||
te = ConvertXmlToDict(data)
|
||||
if isinstance(te, dict) and 'Data' in te and isinstance(te['Data'], dict) \
|
||||
and 'Series' in te['Data'] and isinstance(te['Data']['Series'], dict) \
|
||||
and 'FirstAired' in te['Data']['Series']:
|
||||
try:
|
||||
value = parse(te['Data']['Series']['FirstAired'], fuzzy=True).strftime('%Y-%m-%d')
|
||||
except (StandardError, Exception):
|
||||
value = None
|
||||
te['Data']['Series']['firstaired'] = value
|
||||
return te
|
||||
resp = getURL(url.strip(), params=params, session=session, json=True)
|
||||
|
||||
if resp.ok:
|
||||
if 'application/zip' in resp.headers.get('Content-Type', ''):
|
||||
try:
|
||||
# TODO: The zip contains actors.xml and banners.xml, which are currently ignored [GH-20]
|
||||
self.log('We received a zip file unpacking now ...')
|
||||
zipdata = StringIO.StringIO()
|
||||
zipdata.write(resp.content)
|
||||
myzipfile = zipfile.ZipFile(zipdata)
|
||||
return process_data(myzipfile.read('%s.xml' % language))
|
||||
except zipfile.BadZipfile:
|
||||
raise tvdb_error('Bad zip file received from thetvdb.com, could not read it')
|
||||
else:
|
||||
try:
|
||||
return process_data(resp.content.strip())
|
||||
except (StandardError, Exception):
|
||||
return dict([(u'data', None)])
|
||||
map_show = {'airstime': 'airs_time', 'airsdayofweek': 'airs_dayofweek', 'imdbid': 'imdb_id'}
|
||||
|
||||
def map_show_keys(data):
|
||||
for k, v in data.iteritems():
|
||||
k_org = k
|
||||
k = k.lower()
|
||||
if None is not v:
|
||||
if k in ['banner', 'fanart', 'poster']:
|
||||
v = self.config['url_artworkPrefix'] % v
|
||||
elif 'genre' == k:
|
||||
v = '|%s|' % '|'.join([self._clean_data(c) for c in v if isinstance(c, basestring)])
|
||||
elif 'firstaired' == k:
|
||||
if v:
|
||||
try:
|
||||
v = parse(v, fuzzy=True).strftime('%Y-%m-%d')
|
||||
except (StandardError, Exception):
|
||||
v = None
|
||||
else:
|
||||
v = None
|
||||
else:
|
||||
v = self._clean_data(v)
|
||||
if k in map_show:
|
||||
k = map_show[k]
|
||||
if k_org is not k:
|
||||
del(data[k_org])
|
||||
data[k] = v
|
||||
return data
|
||||
|
||||
if resp:
|
||||
if isinstance(resp['data'], dict):
|
||||
resp['data'] = map_show_keys(resp['data'])
|
||||
elif isinstance(resp['data'], list):
|
||||
for idx, row in enumerate(resp['data']):
|
||||
if isinstance(row, dict):
|
||||
resp['data'][idx] = map_show_keys(row)
|
||||
return resp
|
||||
return dict([(u'data', None)])
|
||||
|
||||
def _getetsrc(self, url, params=None, language=None):
|
||||
"""Loads a URL using caching, returns an ElementTree of the source
|
||||
"""Loads a URL using caching
|
||||
"""
|
||||
try:
|
||||
src = self._load_url(url, params=params, language=language).values()[0]
|
||||
src = self._load_url(url, params=params, language=language)
|
||||
return src
|
||||
except (StandardError, Exception):
|
||||
return []
|
||||
|
@ -622,12 +626,15 @@ class Tvdb:
|
|||
self.shows[sid][seas][ep] = Episode(season=self.shows[sid][seas])
|
||||
self.shows[sid][seas][ep][attrib] = value
|
||||
|
||||
def _set_show_data(self, sid, key, value):
|
||||
def _set_show_data(self, sid, key, value, add=False):
|
||||
"""Sets self.shows[sid] to a new Show instance, or sets the data
|
||||
"""
|
||||
if sid not in self.shows:
|
||||
self.shows[sid] = Show()
|
||||
self.shows[sid].data[key] = value
|
||||
if add and isinstance(self.shows[sid].data, dict) and key in self.shows[sid].data:
|
||||
self.shows[sid].data[key].update(value)
|
||||
else:
|
||||
self.shows[sid].data[key] = value
|
||||
|
||||
@staticmethod
|
||||
def _clean_data(data):
|
||||
|
@ -639,23 +646,18 @@ class Tvdb:
|
|||
"""
|
||||
return data if not isinstance(data, basestring) else data.strip().replace(u'&', u'&')
|
||||
|
||||
def _get_url_artwork(self, image):
|
||||
return image and (self.config['url_artworkPrefix'] % image) or image
|
||||
|
||||
def search(self, series):
|
||||
"""This searches TheTVDB.com for the series name
|
||||
and returns the result list
|
||||
"""
|
||||
series = series.encode('utf-8')
|
||||
self.log('Searching for show %s' % series)
|
||||
self.config['params_get_series']['seriesname'] = series
|
||||
self.config['params_get_series']['name'] = series
|
||||
log().debug('Searching for show %s' % series)
|
||||
|
||||
try:
|
||||
series_found = self._getetsrc(self.config['url_get_series'], self.config['params_get_series'])
|
||||
series_found = self._getetsrc(self.config['url_get_series'], params=self.config['params_get_series'],
|
||||
language=self.config['language'])
|
||||
if series_found:
|
||||
if not isinstance(series_found['Series'], list):
|
||||
series_found['Series'] = [series_found['Series']]
|
||||
series_found['Series'] = [{k.lower(): v for k, v in s.iteritems()} for s in series_found['Series']]
|
||||
return series_found.values()[0]
|
||||
except (StandardError, Exception):
|
||||
pass
|
||||
|
@ -673,50 +675,31 @@ class Tvdb:
|
|||
all_series = [all_series]
|
||||
|
||||
if 0 == len(all_series):
|
||||
self.log('Series result returned zero')
|
||||
log().debug('Series result returned zero')
|
||||
raise tvdb_shownotfound('Show-name search returned zero results (cannot find show on TVDB)')
|
||||
|
||||
if None is not self.config['custom_ui']:
|
||||
self.log('Using custom UI %s' % (repr(self.config['custom_ui'])))
|
||||
log().debug('Using custom UI %s' % (repr(self.config['custom_ui'])))
|
||||
custom_ui = self.config['custom_ui']
|
||||
ui = custom_ui(config=self.config)
|
||||
else:
|
||||
if not self.config['interactive']:
|
||||
self.log('Auto-selecting first search result using BaseUI')
|
||||
log().debug('Auto-selecting first search result using BaseUI')
|
||||
ui = BaseUI(config=self.config)
|
||||
else:
|
||||
self.log('Interactively selecting show using ConsoleUI')
|
||||
log().debug('Interactively selecting show using ConsoleUI')
|
||||
ui = ConsoleUI(config=self.config)
|
||||
|
||||
return ui.selectSeries(all_series)
|
||||
|
||||
def _parse_banners(self, sid):
|
||||
"""Parses banners XML, from
|
||||
http://thetvdb.com/api/[APIKEY]/series/[SERIES ID]/banners.xml
|
||||
|
||||
Banners are retrieved using t['show name]['_banners'], for example:
|
||||
|
||||
>> t = Tvdb(banners = True)
|
||||
>> t['scrubs']['_banners'].keys()
|
||||
['fanart', 'poster', 'series', 'season']
|
||||
>> t['scrubs']['_banners']['poster']['680x1000']['35308']['_bannerpath']
|
||||
u'http://thetvdb.com/banners/posters/76156-2.jpg'
|
||||
>>
|
||||
|
||||
Any key starting with an underscore has been processed (not the raw
|
||||
data from the XML)
|
||||
|
||||
This interface will be improved in future versions.
|
||||
"""
|
||||
self.log('Getting season banners for %s' % sid)
|
||||
banners_et = self._getetsrc(self.config['url_seriesBanner'] % sid)
|
||||
def _parse_banners(self, sid, img_list):
|
||||
banners = {}
|
||||
|
||||
try:
|
||||
for cur_banner in banners_et['banner']:
|
||||
for cur_banner in img_list:
|
||||
bid = cur_banner['id']
|
||||
btype = cur_banner['bannertype']
|
||||
btype2 = cur_banner['bannertype2']
|
||||
btype = cur_banner['keytype']
|
||||
btype2 = cur_banner['resolution']
|
||||
if None is btype or None is btype2:
|
||||
continue
|
||||
if btype not in banners:
|
||||
|
@ -726,60 +709,37 @@ class Tvdb:
|
|||
if bid not in banners[btype][btype2]:
|
||||
banners[btype][btype2][bid] = {}
|
||||
|
||||
for k, v in cur_banner.items():
|
||||
for k, v in cur_banner.iteritems():
|
||||
if None is k or None is v:
|
||||
continue
|
||||
|
||||
k, v = k.lower(), v.lower()
|
||||
k, v = k.lower(), v.lower() if isinstance(v, (str, unicode)) else v
|
||||
if k == 'filename':
|
||||
k = 'bannerpath'
|
||||
banners[btype][btype2][bid]['_bannerpath'] = self.config['url_artworkPrefix'] % v
|
||||
elif k == 'thumbnail':
|
||||
k = 'thumbnailpath'
|
||||
banners[btype][btype2][bid]['_thumbnailpath'] = self.config['url_artworkPrefix'] % v
|
||||
elif k == 'keytype':
|
||||
k = 'bannertype'
|
||||
banners[btype][btype2][bid][k] = v
|
||||
|
||||
for k, v in banners[btype][btype2][bid].items():
|
||||
if k.endswith('path'):
|
||||
new_key = '_%s' % k
|
||||
self.log('Transforming %s to %s' % (k, new_key))
|
||||
new_url = self._get_url_artwork(v)
|
||||
banners[btype][btype2][bid][new_key] = new_url
|
||||
except (StandardError, Exception):
|
||||
pass
|
||||
|
||||
self._set_show_data(sid, '_banners', banners)
|
||||
self._set_show_data(sid, '_banners', banners, add=True)
|
||||
|
||||
def _parse_actors(self, sid):
|
||||
"""Parsers actors XML, from
|
||||
http://thetvdb.com/api/[APIKEY]/series/[SERIES ID]/actors.xml
|
||||
|
||||
Actors are retrieved using t['show name]['_actors'], for example:
|
||||
|
||||
>> t = Tvdb(actors = True)
|
||||
>> actors = t['scrubs']['_actors']
|
||||
>> type(actors)
|
||||
<class 'tvdb_api.Actors'>
|
||||
>> type(actors[0])
|
||||
<class 'tvdb_api.Actor'>
|
||||
>> actors[0]
|
||||
<Actor "Zach Braff">
|
||||
>> sorted(actors[0].keys())
|
||||
['id', 'image', 'name', 'role', 'sortorder']
|
||||
>> actors[0]['name']
|
||||
u'Zach Braff'
|
||||
>> actors[0]['image']
|
||||
u'http://thetvdb.com/banners/actors/43640.jpg'
|
||||
|
||||
Any key starting with an underscore has been processed (not the raw
|
||||
data from the XML)
|
||||
"""
|
||||
self.log('Getting actors for %s' % sid)
|
||||
actors_et = self._getetsrc(self.config['url_actorsInfo'] % sid)
|
||||
def _parse_actors(self, sid, actor_list):
|
||||
|
||||
cur_actors = Actors()
|
||||
try:
|
||||
for curActorItem in actors_et['actor']:
|
||||
for curActorItem in actor_list:
|
||||
cur_actor = Actor()
|
||||
for k, v in curActorItem.items():
|
||||
for k, v in curActorItem.iteritems():
|
||||
k = k.lower()
|
||||
if None is not v:
|
||||
if 'image' == k:
|
||||
v = self._get_url_artwork(v)
|
||||
v = self.config['url_artworkPrefix'] % v
|
||||
else:
|
||||
v = self._clean_data(v)
|
||||
cur_actor[k] = v
|
||||
|
@ -795,96 +755,94 @@ class Tvdb:
|
|||
shows[series_id][season_number][episode_number]
|
||||
"""
|
||||
|
||||
if None is self.config['language']:
|
||||
self.log('Config language is none, using show language')
|
||||
if None is language:
|
||||
raise tvdb_error('config[\'language\'] was None, this should not happen')
|
||||
get_show_in_language = language
|
||||
else:
|
||||
self.log('Configured language %s override show language of %s' % (self.config['language'], language))
|
||||
get_show_in_language = self.config['language']
|
||||
|
||||
# Parse show information
|
||||
self.log('Getting all series data for %s' % sid)
|
||||
url = (self.config['url_seriesInfo'] % (sid, language), self.config['url_epInfo%s' % ('', '_zip')[self.config['useZip']]] % (sid, language))[get_ep_info]
|
||||
show_data = self._getetsrc(url, language=get_show_in_language)
|
||||
log().debug('Getting all series data for %s' % sid)
|
||||
url = self.config['url_seriesInfo'] % sid
|
||||
show_data = self._getetsrc(url, language=language)
|
||||
|
||||
# check and make sure we have data to process and that it contains a series name
|
||||
if not len(show_data) or (isinstance(show_data, dict) and 'SeriesName' not in show_data['Series']):
|
||||
if not isinstance(show_data, dict) or 'seriesname' not in show_data['data']:
|
||||
return False
|
||||
|
||||
for k, v in show_data['Series'].iteritems():
|
||||
if None is not v:
|
||||
if k in ['banner', 'fanart', 'poster']:
|
||||
v = self._get_url_artwork(v)
|
||||
else:
|
||||
v = self._clean_data(v)
|
||||
for k, v in show_data['data'].iteritems():
|
||||
self._set_show_data(sid, k, v)
|
||||
|
||||
self._set_show_data(sid, k.lower(), v)
|
||||
if self.config['banners_enabled']:
|
||||
poster_data = self._getetsrc(self.config['url_seriesBanner'] % (sid, 'poster'), language=language)
|
||||
self._set_show_data(sid, u'poster',
|
||||
('', self.config['url_artworkPrefix'] % poster_data['data'][0]['filename'])[
|
||||
poster_data and 'data' in poster_data and len(poster_data['data']) > 0])
|
||||
if poster_data and 'data' in poster_data and len(poster_data['data']) > 1:
|
||||
self._parse_banners(sid, poster_data['data'])
|
||||
|
||||
if self.config['fanart_enabled']:
|
||||
fanart_data = self._getetsrc(self.config['url_seriesBanner'] % (sid, 'fanart'), language=language)
|
||||
self._set_show_data(sid, u'fanart',
|
||||
('', self.config['url_artworkPrefix'] % fanart_data['data'][0]['filename'])[
|
||||
fanart_data and 'data' in fanart_data and len(fanart_data['data']) > 0])
|
||||
if fanart_data and 'data' in fanart_data and len(fanart_data['data']) > 1:
|
||||
self._parse_banners(sid, fanart_data['data'])
|
||||
|
||||
if self.config['actors_enabled']:
|
||||
actor_data = self._getetsrc(self.config['url_actorsInfo'] % sid, language=language)
|
||||
self._set_show_data(sid, u'actors',
|
||||
('||', '|%s|' % '|'.join([n.get('name', '') for n in sorted(
|
||||
actor_data['data'], key=lambda x: x['sortorder'])]))[
|
||||
actor_data and 'data' in actor_data and len(actor_data['data']) > 0])
|
||||
if actor_data and 'data' in actor_data and len(actor_data['data']) > 0:
|
||||
self._parse_actors(sid, actor_data['data'])
|
||||
|
||||
if get_ep_info:
|
||||
# Parse banners
|
||||
if self.config['banners_enabled']:
|
||||
self._parse_banners(sid)
|
||||
|
||||
# Parse actors
|
||||
if self.config['actors_enabled']:
|
||||
self._parse_actors(sid)
|
||||
|
||||
# Parse episode data
|
||||
self.log('Getting all episodes of %s' % sid)
|
||||
log().debug('Getting all episodes of %s' % sid)
|
||||
|
||||
if 'Episode' not in show_data:
|
||||
return False
|
||||
page = 1
|
||||
episodes = []
|
||||
while page is not None:
|
||||
episode_data = self._getetsrc(self.config['url_epInfo'] % (sid, page), language=language)
|
||||
if isinstance(episode_data, dict) and episode_data['data'] is not None:
|
||||
episodes.extend(episode_data['data'])
|
||||
page = episode_data['links']['next'] if isinstance(episode_data, dict) \
|
||||
and 'links' in episode_data and 'next' in episode_data['links'] else None
|
||||
|
||||
episodes = show_data['Episode']
|
||||
if not isinstance(episodes, list):
|
||||
episodes = [episodes]
|
||||
ep_map_keys = {'absolutenumber': u'absolute_number', 'airedepisodenumber': u'episodenumber',
|
||||
'airedseason': u'seasonnumber', 'airedseasonid': u'seasonid',
|
||||
'dvdepisodenumber': u'dvd_episodenumber', 'dvdseason': u'dvd_season'}
|
||||
|
||||
dvd_order = {'dvd': [], 'network': []}
|
||||
for cur_ep in episodes:
|
||||
if self.config['dvdorder']:
|
||||
use_dvd = cur_ep['DVD_season'] not in (None, '') and cur_ep['DVD_episodenumber'] not in (None, '')
|
||||
log().debug('Using DVD ordering.')
|
||||
use_dvd = None is not cur_ep.get('dvdseason') and None is not cur_ep.get('dvdepisodenumber')
|
||||
else:
|
||||
use_dvd = False
|
||||
|
||||
if use_dvd:
|
||||
elem_seasnum, elem_epno = cur_ep['DVD_season'], cur_ep['DVD_episodenumber']
|
||||
elem_seasnum, elem_epno = cur_ep.get('dvdseason'), cur_ep.get('dvdepisodenumber')
|
||||
else:
|
||||
elem_seasnum, elem_epno = cur_ep['SeasonNumber'], cur_ep['EpisodeNumber']
|
||||
elem_seasnum, elem_epno = cur_ep.get('airedseason'), cur_ep.get('airedepisodenumber')
|
||||
|
||||
if None is elem_seasnum or None is elem_epno:
|
||||
self.log('An episode has incomplete season/episode number (season: %r, episode: %r)' % (
|
||||
elem_seasnum, elem_epno), logger.WARNING)
|
||||
log().warning('An episode has incomplete season/episode number (season: %r, episode: %r)' % (
|
||||
elem_seasnum, elem_epno))
|
||||
continue # Skip to next episode
|
||||
|
||||
# float() is because https://github.com/dbr/tvnamer/issues/95 - should probably be fixed in TVDB data
|
||||
seas_no = int(float(elem_seasnum))
|
||||
ep_no = int(float(elem_epno))
|
||||
|
||||
if self.config['dvdorder']:
|
||||
dvd_order[('network', 'dvd')[use_dvd]] += ['S%02dE%02d' % (seas_no, ep_no)]
|
||||
|
||||
for k, v in cur_ep.items():
|
||||
for k, v in cur_ep.iteritems():
|
||||
k = k.lower()
|
||||
|
||||
if None is not v:
|
||||
if 'filename' == k:
|
||||
v = self._get_url_artwork(v)
|
||||
v = self.config['url_artworkPrefix'] % v
|
||||
else:
|
||||
v = self._clean_data(v)
|
||||
|
||||
if k in ep_map_keys:
|
||||
k = ep_map_keys[k]
|
||||
self._set_item(sid, seas_no, ep_no, k, v)
|
||||
|
||||
if self.config['dvdorder']:
|
||||
num_dvd, num_network = [len(dvd_order[x]) for x in 'dvd', 'network']
|
||||
num_all = num_dvd + num_network
|
||||
if num_all:
|
||||
self.log('Of %s episodes, %s use the DVD order, and %s use the network aired order' % (
|
||||
num_all, num_dvd, num_network))
|
||||
for ep_numbers in [', '.join(dvd_order['dvd'][i:i + 5]) for i in xrange(0, num_dvd, 5)]:
|
||||
self.log('Using DVD order: %s' % ep_numbers)
|
||||
|
||||
return True
|
||||
|
||||
def _name_to_sid(self, name):
|
||||
|
@ -893,10 +851,10 @@ class Tvdb:
|
|||
the correct SID.
|
||||
"""
|
||||
if name in self.corrections:
|
||||
self.log('Correcting %s to %s' % (name, self.corrections[name]))
|
||||
log().debug('Correcting %s to %s' % (name, self.corrections[name]))
|
||||
return self.corrections[name]
|
||||
else:
|
||||
self.log('Getting show %s' % name)
|
||||
log().debug('Getting show %s' % name)
|
||||
selected_series = self._get_series(name)
|
||||
if isinstance(selected_series, dict):
|
||||
selected_series = [selected_series]
|
||||
|
@ -926,7 +884,7 @@ class Tvdb:
|
|||
selected_series = self._get_series(key)
|
||||
if isinstance(selected_series, dict):
|
||||
selected_series = [selected_series]
|
||||
[[self._set_show_data(show['id'], k, v) for k, v in show.items()] for show in selected_series]
|
||||
[[self._set_show_data(show['id'], k, v) for k, v in show.iteritems()] for show in selected_series]
|
||||
return selected_series
|
||||
|
||||
def __repr__(self):
|
||||
|
|
|
@ -151,7 +151,7 @@ class Show(dict):
|
|||
# doesn't exist, so attribute error.
|
||||
raise tvrage_attributenotfound("Cannot find attribute %s" % (repr(key)))
|
||||
|
||||
def airedOn(self, date):
|
||||
def aired_on(self, date):
|
||||
ret = self.search(str(date), 'firstaired')
|
||||
if len(ret) == 0:
|
||||
raise tvrage_episodenotfound("Could not find any episodes that aired on %s" % date)
|
||||
|
|
|
@ -23,7 +23,7 @@ indexerConfig = {
|
|||
id=INDEXER_TVDB,
|
||||
name='TheTVDB',
|
||||
module=Tvdb,
|
||||
api_params=dict(apikey='F9C450E78D99172E', language='en', useZip=True),
|
||||
api_params=dict(apikey='F9C450E78D99172E', language='en'),
|
||||
active=True,
|
||||
dupekey='',
|
||||
mapped_only=False,
|
||||
|
|
|
@ -761,7 +761,10 @@ class GenericMetadata():
|
|||
# There's gotta be a better way of doing this but we don't wanna
|
||||
# change the language value elsewhere
|
||||
lINDEXER_API_PARMS = sickbeard.indexerApi(show_obj.indexer).api_params.copy()
|
||||
lINDEXER_API_PARMS['banners'] = True
|
||||
if image_type.startswith('fanart'):
|
||||
lINDEXER_API_PARMS['fanart'] = True
|
||||
else:
|
||||
lINDEXER_API_PARMS['banners'] = True
|
||||
lINDEXER_API_PARMS['dvdorder'] = 0 != show_obj.dvdorder
|
||||
|
||||
if indexer_lang and not 'en' == indexer_lang:
|
||||
|
|
|
@ -272,7 +272,7 @@ class NameParser(object):
|
|||
|
||||
t = sickbeard.indexerApi(show.indexer).indexer(**lindexer_api_parms)
|
||||
|
||||
ep_obj = t[show.indexerid].airedOn(best_result.air_date)[0]
|
||||
ep_obj = t[show.indexerid].aired_on(best_result.air_date)[0]
|
||||
|
||||
season_number = int(ep_obj['seasonnumber'])
|
||||
episode_numbers = [int(ep_obj['episodenumber'])]
|
||||
|
|
Loading…
Reference in a new issue