Change use TVDb API v2.

Add complete actor parsing into _actors.
Make returned _banners compatible with old v1 api lib.
Add all poster + fanart loading (optional) into poster, fanart subdicts of _banners subdict.
Depending on requested image_type set fanart or banners to true.
Readd banners parameter to tvdb lib.
Add new parameter fanart to tvdb lib.
If no firstaired for an episode return None.
Remove old imports.
This commit is contained in:
Prinz23 2017-04-24 23:38:27 +01:00
parent a8fd974afd
commit 06599ac708
6 changed files with 194 additions and 232 deletions

View file

@ -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]

View file

@ -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'&amp;', 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):

View file

@ -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)

View file

@ -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,

View file

@ -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:

View file

@ -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'])]