Fixed issue with theTVDB IndexerAPI _parseActors method improperly parsing the data returned from the api causing an exception to occur during metadata refreshes.

Fixed issues with Backup/Restore.

Fixed issues with banners, posters, images via showPoster method.

Fixed issues with WebAPI apikeys.

Fixed issues with WebUI and url pattern matching.
This commit is contained in:
echel0n 2014-12-13 04:14:29 -08:00
parent 4fd92a8631
commit 831248b412
8 changed files with 79 additions and 86 deletions

View File

@ -5,7 +5,7 @@ $(document).ready(function(){
$("#Backup").attr("disabled", true); $("#Backup").attr("disabled", true);
$('#Backup-result').html(loading); $('#Backup-result').html(loading);
var backupDir = $("#backupDir").val(); var backupDir = $("#backupDir").val();
$.get(sbRoot + "/config/backup", {'backupDir': backupDir}) $.get(sbRoot + "/config/backup/", {'backupDir': backupDir})
.done(function (data) { .done(function (data) {
$('#Backup-result').html(data); $('#Backup-result').html(data);
$("#Backup").attr("disabled", false); $("#Backup").attr("disabled", false);
@ -15,7 +15,7 @@ $(document).ready(function(){
$("#Restore").attr("disabled", true); $("#Restore").attr("disabled", true);
$('#Restore-result').html(loading); $('#Restore-result').html(loading);
var backupFile = $("#backupFile").val(); var backupFile = $("#backupFile").val();
$.get(sbRoot + "/config/restore", {'backupFile': backupFile}) $.get(sbRoot + "/config/restore/", {'backupFile': backupFile})
.done(function (data) { .done(function (data) {
$('#Restore-result').html(data); $('#Restore-result').html(data);
$("#Restore").attr("disabled", false); $("#Restore").attr("disabled", false);

View File

@ -797,18 +797,20 @@ class Tvdb:
return return
cur_actors = Actors() cur_actors = Actors()
for curActorItem in actorsEt["actor"].items(): for cur_actor in actorsEt['actor']:
curActor = Actor() curActor = Actor()
for k, v in curActorItem: for k, v in cur_actor.items():
if k is None or v is None:
continue
k = k.lower() k = k.lower()
if v is not None: if k == "image":
if k == "image": v = self.config['url_artworkPrefix'] % (v)
v = self.config['url_artworkPrefix'] % (v) else:
else: v = self._cleanData(v)
v = self._cleanData(v)
curActor[k] = v curActor[k] = v
cur_actors.append(curActor) cur_actors.append(curActor)
self._setShowData(sid, '_actors', cur_actors) self._setShowData(sid, '_actors', cur_actors)
def _getShowData(self, sid, language, getEpInfo=False): def _getShowData(self, sid, language, getEpInfo=False):

View File

@ -189,19 +189,16 @@ class KODI_12PlusMetadata(generic.GenericMetadata):
cur_actor = etree.SubElement(tv_node, "actor") cur_actor = etree.SubElement(tv_node, "actor")
cur_actor_name = etree.SubElement(cur_actor, "name") cur_actor_name = etree.SubElement(cur_actor, "name")
cur_actor_name_text = actor['name'] if getattr(actor, 'name', None) is not None:
if isinstance(cur_actor_name_text, basestring): cur_actor_name.text = actor['name'].strip()
cur_actor_name.text = cur_actor_name_text.strip()
cur_actor_role = etree.SubElement(cur_actor, "role") cur_actor_role = etree.SubElement(cur_actor, "role")
cur_actor_role_text = actor['role'] if getattr(actor, 'role', None) is not None:
if cur_actor_role_text != None: cur_actor_role.text = actor['role']
cur_actor_role.text = cur_actor_role_text
cur_actor_thumb = etree.SubElement(cur_actor, "thumb") cur_actor_thumb = etree.SubElement(cur_actor, "thumb")
cur_actor_thumb_text = actor['image'] if getattr(actor, 'image', None) is not None:
if cur_actor_thumb_text != None: cur_actor_thumb.text = actor['image']
cur_actor_thumb.text = cur_actor_thumb_text
# Make it purdy # Make it purdy
helpers.indentXML(tv_node) helpers.indentXML(tv_node)

View File

@ -199,8 +199,7 @@ class Mede8erMetadata(mediabrowser.MediaBrowserMetadata):
if getattr(myShow, '_actors', None) is not None: if getattr(myShow, '_actors', None) is not None:
for actor in myShow['_actors']: for actor in myShow['_actors']:
cur_actor_name_text = actor['name'] cur_actor_name_text = getattr(actor, 'name', None)
if cur_actor_name_text != None and cur_actor_name_text.strip(): if cur_actor_name_text != None and cur_actor_name_text.strip():
cur_actor = etree.SubElement(cast, "actor") cur_actor = etree.SubElement(cast, "actor")
cur_actor.text = cur_actor_name_text.strip() cur_actor.text = cur_actor_name_text.strip()

View File

@ -362,14 +362,17 @@ class MediaBrowserMetadata(generic.GenericMetadata):
if getattr(myShow, 'actors', None) is not None: if getattr(myShow, 'actors', None) is not None:
for actor in myShow['_actors']: for actor in myShow['_actors']:
cur_actor = etree.SubElement(Persons, "Person") cur_actor = etree.SubElement(Persons, "Person")
cur_actor_name = etree.SubElement(cur_actor, "Name") cur_actor_name = etree.SubElement(cur_actor, "Name")
cur_actor_name.text = actor['name'].strip() if getattr(actor, 'name', None):
cur_actor_name.text = actor['name'].strip()
cur_actor_type = etree.SubElement(cur_actor, "Type") cur_actor_type = etree.SubElement(cur_actor, "Type")
cur_actor_type.text = "Actor" cur_actor_type.text = "Actor"
cur_actor_role = etree.SubElement(cur_actor, "Role") cur_actor_role = etree.SubElement(cur_actor, "Role")
cur_actor_role_text = actor['role'] if getattr(actor, 'role', None):
if cur_actor_role_text != None: cur_actor_role.text = actor['role']
cur_actor_role.text = cur_actor_role_text
helpers.indentXML(tv_node) helpers.indentXML(tv_node)

View File

@ -275,12 +275,14 @@ class WDTVMetadata(generic.GenericMetadata):
if getattr(myShow, '_actors', None) is not None: if getattr(myShow, '_actors', None) is not None:
for actor in myShow['_actors']: for actor in myShow['_actors']:
cur_actor = etree.SubElement(episode, "actor") cur_actor = etree.SubElement(episode, "actor")
cur_actor_name = etree.SubElement(cur_actor, "name") cur_actor_name = etree.SubElement(cur_actor, "name")
cur_actor_name.text = actor['name'] if getattr(actor, 'name', None):
cur_actor_name.text = actor['name']
cur_actor_role = etree.SubElement(cur_actor, "role") cur_actor_role = etree.SubElement(cur_actor, "role")
cur_actor_role_text = actor['role'] if getattr(actor, 'role', None):
if cur_actor_role_text != None: cur_actor_role.text = actor['role']
cur_actor_role.text = cur_actor_role_text
overview = etree.SubElement(episode, "overview") overview = etree.SubElement(episode, "overview")
if curEpToWrite.description != None: if curEpToWrite.description != None:

View File

@ -24,6 +24,7 @@ import time
import urllib import urllib
import re import re
import datetime import datetime
import urlparse
import sickbeard import sickbeard
from sickbeard import config, sab from sickbeard import config, sab
@ -134,16 +135,14 @@ class BaseHandler(RequestHandler):
def write_error(self, status_code, **kwargs): def write_error(self, status_code, **kwargs):
# handle 404 http errors # handle 404 http errors
if status_code == 404: if status_code == 404:
index_url = sickbeard.WEB_ROOT url = self.request.uri
url = self.request.uri[len(index_url):] if self.request.uri.startswith(sickbeard.WEB_ROOT):
url = url[len(sickbeard.WEB_ROOT)+1:]
if url[1:4] != 'api': if url[:3] != 'api':
return self.redirect(url) return self.redirect(url)
else: else:
if url.endswith('/'): self.finish('Wrong API key used')
self.finish('Wrong API key used')
else:
return self.redirect(url + '/')
elif self.settings.get("debug") and "exc_info" in kwargs: elif self.settings.get("debug") and "exc_info" in kwargs:
exc_info = kwargs["exc_info"] exc_info = kwargs["exc_info"]
@ -166,7 +165,7 @@ class BaseHandler(RequestHandler):
</html>""" % (error, error, </html>""" % (error, error,
trace_info, request_info)) trace_info, request_info))
def redirect(self, url, permanent=True, status=None): def redirect(self, url, permanent=False, status=None):
if not url.startswith(sickbeard.WEB_ROOT): if not url.startswith(sickbeard.WEB_ROOT):
url = sickbeard.WEB_ROOT + url url = sickbeard.WEB_ROOT + url
@ -371,7 +370,7 @@ class WebRoot(WebHandler):
default_image_name = 'banner.png' default_image_name = 'banner.png'
#image_path = ek.ek(os.path.join, sickbeard.PROG_DIR, 'gui', 'slick', 'images', default_image_name) #image_path = ek.ek(os.path.join, sickbeard.PROG_DIR, 'gui', 'slick', 'images', default_image_name)
static_image_path = '/images/' + default_image_name static_image_path = os.path.join('/images', default_image_name)
if show and sickbeard.helpers.findCertainShow(sickbeard.showList, int(show)): if show and sickbeard.helpers.findCertainShow(sickbeard.showList, int(show)):
cache_obj = image_cache.ImageCache() cache_obj = image_cache.ImageCache()
@ -386,9 +385,9 @@ class WebRoot(WebHandler):
image_file_name = cache_obj.banner_thumb_path(show) image_file_name = cache_obj.banner_thumb_path(show)
if ek.ek(os.path.isfile, image_file_name): if ek.ek(os.path.isfile, image_file_name):
image_path = image_file_name static_image_path = os.path.normpath(image_file_name.replace(sickbeard.CACHE_DIR, '/cache'))
static_image_path = '/cache' + image_path.replace(sickbeard.CACHE_DIR, '')
static_image_path = static_image_path.replace('\\', '/')
return self.redirect(static_image_path) return self.redirect(static_image_path)
def setHomeLayout(self, layout): def setHomeLayout(self, layout):
@ -605,7 +604,7 @@ class WebRoot(WebHandler):
return ical return ical
@route('/ui/(.*)(/?)') @route('/ui(/?.*)')
class UI(WebRoot): class UI(WebRoot):
def add_message(self): def add_message(self):
ui.notifications.message('Test 1', 'This is test number 1') ui.notifications.message('Test 1', 'This is test number 1')
@ -625,7 +624,7 @@ class UI(WebRoot):
return json.dumps(messages) return json.dumps(messages)
@route('/browser/(.*)(/?)') @route('/browser(/?.*)')
class WebFileBrowser(WebRoot): class WebFileBrowser(WebRoot):
def index(self, path='', includeFiles=False, *args, **kwargs): def index(self, path='', includeFiles=False, *args, **kwargs):
self.set_header("Content-Type", "application/json") self.set_header("Content-Type", "application/json")
@ -639,7 +638,7 @@ class WebFileBrowser(WebRoot):
return json.dumps(paths) return json.dumps(paths)
@route('/home/(.*)(/?)') @route('/home(/?.*)')
class Home(WebRoot): class Home(WebRoot):
def HomeMenu(self): def HomeMenu(self):
menu = [ menu = [
@ -1743,11 +1742,11 @@ class Home(WebRoot):
episodes = [] episodes = []
# Queued Searches def getEpisodes(searchThread, searchstatus):
for searchThread in sickbeard.searchQueueScheduler.action.get_all_ep_from_queue(show): results = []
searchstatus = 'queued'
if isinstance(searchThread, sickbeard.search_queue.ManualSearchQueueItem): if isinstance(searchThread, sickbeard.search_queue.ManualSearchQueueItem):
episodes.append({'episode': searchThread.segment.episode, results.append({'episode': searchThread.segment.episode,
'episodeindexid': searchThread.segment.indexerid, 'episodeindexid': searchThread.segment.indexerid,
'season': searchThread.segment.season, 'season': searchThread.segment.season,
'searchstatus': searchstatus, 'searchstatus': searchstatus,
@ -1755,13 +1754,19 @@ class Home(WebRoot):
'quality': self.getQualityClass(searchThread.segment)}) 'quality': self.getQualityClass(searchThread.segment)})
else: else:
for epObj in searchThread.segment: for epObj in searchThread.segment:
episodes.append({'episode': epObj.episode, results.append({'episode': epObj.episode,
'episodeindexid': epObj.indexerid, 'episodeindexid': epObj.indexerid,
'season': epObj.season, 'season': epObj.season,
'searchstatus': searchstatus, 'searchstatus': searchstatus,
'status': statusStrings[epObj.status], 'status': statusStrings[epObj.status],
'quality': self.getQualityClass(epObj)}) 'quality': self.getQualityClass(epObj)})
return results
# Queued Searches
for searchThread in sickbeard.searchQueueScheduler.action.get_all_ep_from_queue(show):
episodes += getEpisodes(searchThread, 'queued')
# Running Searches # Running Searches
if (sickbeard.searchQueueScheduler.action.is_manualsearch_in_progress()): if (sickbeard.searchQueueScheduler.action.is_manualsearch_in_progress()):
searchThread = sickbeard.searchQueueScheduler.action.currentItem searchThread = sickbeard.searchQueueScheduler.action.currentItem
@ -1769,12 +1774,7 @@ class Home(WebRoot):
searchstatus = 'finished' searchstatus = 'finished'
else: else:
searchstatus = 'searching' searchstatus = 'searching'
episodes.append({'episode': searchThread.segment.episode, episodes += getEpisodes(searchThread, searchstatus)
'episodeindexid': searchThread.segment.indexerid,
'season': searchThread.segment.season,
'searchstatus': searchstatus,
'status': statusStrings[searchThread.segment.status],
'quality': self.getQualityClass(searchThread.segment)})
# Finished Searches # Finished Searches
for searchThread in sickbeard.search_queue.MANUAL_SEARCH_HISTORY: for searchThread in sickbeard.search_queue.MANUAL_SEARCH_HISTORY:
@ -1782,24 +1782,14 @@ class Home(WebRoot):
if str(searchThread.show.indexerid) == show and not [x for x in episodes if x[ if str(searchThread.show.indexerid) == show and not [x for x in episodes if x[
'episodeindexid'] == searchThread.segment.indexerid]: 'episodeindexid'] == searchThread.segment.indexerid]:
searchstatus = 'finished' searchstatus = 'finished'
episodes.append({'episode': searchThread.segment.episode, episodes += getEpisodes(searchThread, searchstatus)
'episodeindexid': searchThread.segment.indexerid,
'season': searchThread.segment.season,
'searchstatus': searchstatus,
'status': statusStrings[searchThread.segment.status],
'quality': self.getQualityClass(searchThread.segment)})
else: else:
### These are only Failed Downloads/Retry SearchThreadItems.. lets loop through the segement/episodes ### These are only Failed Downloads/Retry SearchThreadItems.. lets loop through the segement/episodes
if str(searchThread.show.indexerid) == show: if str(searchThread.show.indexerid) == show:
for epObj in searchThread.segment: for epObj in searchThread.segment:
if not [x for x in episodes if x['episodeindexid'] == epObj.indexerid]: if not [x for x in episodes if x['episodeindexid'] == epObj.indexerid]:
searchstatus = 'finished' searchstatus = 'finished'
episodes.append({'episode': epObj.episode, episodes += getEpisodes(searchThread, searchstatus)
'episodeindexid': epObj.indexerid,
'season': epObj.season,
'searchstatus': searchstatus,
'status': statusStrings[epObj.status],
'quality': self.getQualityClass(epObj)})
return json.dumps({'show': show, 'episodes': episodes}) return json.dumps({'show': show, 'episodes': episodes})
@ -1936,7 +1926,7 @@ class Home(WebRoot):
return json.dumps({'result': 'failure'}) return json.dumps({'result': 'failure'})
@route('/home/postprocess/(.*)(/?)') @route('/home/postprocess(/?.*)')
class HomePostProcess(Home): class HomePostProcess(Home):
def index(self): def index(self):
t = PageTemplate(rh=self, file="home_postprocess.tmpl") t = PageTemplate(rh=self, file="home_postprocess.tmpl")
@ -1973,7 +1963,7 @@ class HomePostProcess(Home):
return self._genericMessage("Postprocessing results", result) return self._genericMessage("Postprocessing results", result)
@route('/home/addShows/(.*)(/?)') @route('/home/addShows(/?.*)')
class HomeAddShows(Home): class HomeAddShows(Home):
def index(self): def index(self):
t = PageTemplate(rh=self, file="home_addShows.tmpl") t = PageTemplate(rh=self, file="home_addShows.tmpl")
@ -2475,7 +2465,7 @@ class HomeAddShows(Home):
return self.newShow(dirs_only[0], dirs_only[1:]) return self.newShow(dirs_only[0], dirs_only[1:])
@route('/manage/(.*)(/?)') @route('/manage(/?.*)')
class Manage(WebRoot): class Manage(WebRoot):
def ManageMenu(self): def ManageMenu(self):
menu = [ menu = [
@ -3169,7 +3159,7 @@ class Manage(WebRoot):
return t return t
@route('/manage/manageSearches/(.*)(/?)') @route('/manage/manageSearches(/?.*)')
class ManageSearches(Manage): class ManageSearches(Manage):
def index(self): def index(self):
t = PageTemplate(rh=self, file="manage_manageSearches.tmpl") t = PageTemplate(rh=self, file="manage_manageSearches.tmpl")
@ -3224,7 +3214,7 @@ class ManageSearches(Manage):
return self.redirect("/manage/manageSearches/") return self.redirect("/manage/manageSearches/")
@route('/history/(.*)(/?)') @route('/history(/?.*)')
class History(WebRoot): class History(WebRoot):
def index(self, limit=100): def index(self, limit=100):
@ -3316,7 +3306,7 @@ class History(WebRoot):
return self.redirect("/history/") return self.redirect("/history/")
@route('/config/(.*)(/?)') @route('/config(/?.*)')
class Config(WebRoot): class Config(WebRoot):
def ConfigMenu(self): def ConfigMenu(self):
menu = [ menu = [
@ -3339,7 +3329,7 @@ class Config(WebRoot):
return t return t
@route('/config/general/(.*)(/?)') @route('/config/general(/?.*)')
class ConfigGeneral(Config): class ConfigGeneral(Config):
def index(self): def index(self):
t = PageTemplate(rh=self, file="config_general.tmpl") t = PageTemplate(rh=self, file="config_general.tmpl")
@ -3474,7 +3464,7 @@ class ConfigGeneral(Config):
return self.redirect("/config/general/") return self.redirect("/config/general/")
@route('/config/backuprestore/(.*)(/?)') @route('/config/backuprestore(/?.*)')
class ConfigBackupRestore(Config): class ConfigBackupRestore(Config):
def index(self): def index(self):
t = PageTemplate(rh=self, file="config_backuprestore.tmpl") t = PageTemplate(rh=self, file="config_backuprestore.tmpl")
@ -3522,7 +3512,7 @@ class ConfigBackupRestore(Config):
return finalResult return finalResult
@route('/config/search/(.*)(/?)') @route('/config/search(/?.*)')
class ConfigSearch(Config): class ConfigSearch(Config):
def index(self): def index(self):
@ -3615,7 +3605,7 @@ class ConfigSearch(Config):
return self.redirect("/config/search/") return self.redirect("/config/search/")
@route('/config/postProcessing/(.*)(/?)') @route('/config/postProcessing(/?.*)')
class ConfigPostProcessing(Config): class ConfigPostProcessing(Config):
def index(self): def index(self):
@ -3815,7 +3805,7 @@ class ConfigPostProcessing(Config):
return 'not supported' return 'not supported'
@route('/config/providers/(.*)(/?)') @route('/config/providers(/?.*)')
class ConfigProviders(Config): class ConfigProviders(Config):
def index(self): def index(self):
t = PageTemplate(rh=self, file="config_providers.tmpl") t = PageTemplate(rh=self, file="config_providers.tmpl")
@ -4254,7 +4244,7 @@ class ConfigProviders(Config):
return self.redirect("/config/providers/") return self.redirect("/config/providers/")
@route('/config/notifications/(.*)(/?)') @route('/config/notifications(/?.*)')
class ConfigNotifications(Config): class ConfigNotifications(Config):
def index(self): def index(self):
t = PageTemplate(rh=self, file="config_notifications.tmpl") t = PageTemplate(rh=self, file="config_notifications.tmpl")
@ -4464,7 +4454,7 @@ class ConfigNotifications(Config):
return self.redirect("/config/notifications/") return self.redirect("/config/notifications/")
@route('/config/subtitles/(.*)(/?)') @route('/config/subtitles(/?.*)')
class ConfigSubtitles(Config): class ConfigSubtitles(Config):
def index(self): def index(self):
t = PageTemplate(rh=self, file="config_subtitles.tmpl") t = PageTemplate(rh=self, file="config_subtitles.tmpl")
@ -4528,7 +4518,7 @@ class ConfigSubtitles(Config):
return self.redirect("/config/subtitles/") return self.redirect("/config/subtitles/")
@route('/config/anime/(.*)(/?)') @route('/config/anime(/?.*)')
class ConfigAnime(Config): class ConfigAnime(Config):
def index(self): def index(self):
@ -4561,7 +4551,7 @@ class ConfigAnime(Config):
return self.redirect("/config/anime/") return self.redirect("/config/anime/")
@route('/errorlogs/(.*)(/?)') @route('/errorlogs(/?.*)')
class ErrorLogs(WebRoot): class ErrorLogs(WebRoot):
def ErrorLogsMenu(self): def ErrorLogsMenu(self):
menu = [ menu = [

View File

@ -44,7 +44,7 @@ class SRWebServer(threading.Thread):
# api root # api root
if not sickbeard.API_KEY: if not sickbeard.API_KEY:
sickbeard.API_KEY = generateApiKey() sickbeard.API_KEY = generateApiKey()
self.options['api_root'] = r'%s/api/%s/' % (sickbeard.WEB_ROOT, sickbeard.API_KEY) self.options['api_root'] = r'%s/api/%s' % (sickbeard.WEB_ROOT, sickbeard.API_KEY)
# tornado setup # tornado setup
self.enable_https = self.options['enable_https'] self.enable_https = self.options['enable_https']
@ -78,17 +78,17 @@ class SRWebServer(threading.Thread):
# Main Handlers # Main Handlers
self.app.add_handlers('.*$', [ self.app.add_handlers('.*$', [
# webapi handler # webapi handler
(r'%s(/?)' % self.options['api_root'], ApiHandler), (r'%s(/?.*)' % self.options['api_root'], ApiHandler),
# webapi key retrieval # webapi key retrieval
(r'%s/getkey(/?)' % self.options['web_root'], KeyHandler), (r'%s/getkey(/?.*)' % self.options['web_root'], KeyHandler),
# webapi builder redirect # webapi builder redirect
(r'%s/api/builder' % self.options['web_root'], RedirectHandler, {"url": self.options['web_root'] + '/apibuilder/'}), (r'%s/api/builder' % self.options['web_root'], RedirectHandler, {"url": self.options['web_root'] + '/apibuilder/'}),
# webui login/logout handlers # webui login/logout handlers
(r'%s/login(/?)' % self.options['web_root'], LoginHandler), (r'%s/login(/?.*)' % self.options['web_root'], LoginHandler),
(r'%s/logout(/?)' % self.options['web_root'], LogoutHandler), (r'%s/logout(/?.*)' % self.options['web_root'], LogoutHandler),
# webui redirect # webui redirect
(r'/', RedirectHandler, {"url": self.options['web_root'] + '/home/'}), (r'/', RedirectHandler, {"url": self.options['web_root'] + '/home/'}),