From 831248b412ffe6c9981cd168373f3757800dcd03 Mon Sep 17 00:00:00 2001 From: echel0n Date: Sat, 13 Dec 2014 04:14:29 -0800 Subject: [PATCH] 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. --- gui/slick/js/configBackupRestore.js | 4 +- lib/tvdb_api/tvdb_api.py | 18 +++--- sickbeard/metadata/kodi_12plus.py | 15 ++--- sickbeard/metadata/mede8er.py | 3 +- sickbeard/metadata/mediabrowser.py | 11 ++-- sickbeard/metadata/wdtv.py | 10 +-- sickbeard/webserve.py | 94 +++++++++++++---------------- sickbeard/webserveInit.py | 10 +-- 8 files changed, 79 insertions(+), 86 deletions(-) diff --git a/gui/slick/js/configBackupRestore.js b/gui/slick/js/configBackupRestore.js index 113f5586..3881924b 100644 --- a/gui/slick/js/configBackupRestore.js +++ b/gui/slick/js/configBackupRestore.js @@ -5,7 +5,7 @@ $(document).ready(function(){ $("#Backup").attr("disabled", true); $('#Backup-result').html(loading); var backupDir = $("#backupDir").val(); - $.get(sbRoot + "/config/backup", {'backupDir': backupDir}) + $.get(sbRoot + "/config/backup/", {'backupDir': backupDir}) .done(function (data) { $('#Backup-result').html(data); $("#Backup").attr("disabled", false); @@ -15,7 +15,7 @@ $(document).ready(function(){ $("#Restore").attr("disabled", true); $('#Restore-result').html(loading); var backupFile = $("#backupFile").val(); - $.get(sbRoot + "/config/restore", {'backupFile': backupFile}) + $.get(sbRoot + "/config/restore/", {'backupFile': backupFile}) .done(function (data) { $('#Restore-result').html(data); $("#Restore").attr("disabled", false); diff --git a/lib/tvdb_api/tvdb_api.py b/lib/tvdb_api/tvdb_api.py index e75ed872..638808bb 100644 --- a/lib/tvdb_api/tvdb_api.py +++ b/lib/tvdb_api/tvdb_api.py @@ -797,18 +797,20 @@ class Tvdb: return cur_actors = Actors() - for curActorItem in actorsEt["actor"].items(): + for cur_actor in actorsEt['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() - if v is not None: - if k == "image": - v = self.config['url_artworkPrefix'] % (v) - else: - v = self._cleanData(v) + if k == "image": + v = self.config['url_artworkPrefix'] % (v) + else: + v = self._cleanData(v) + curActor[k] = v cur_actors.append(curActor) - self._setShowData(sid, '_actors', cur_actors) def _getShowData(self, sid, language, getEpInfo=False): diff --git a/sickbeard/metadata/kodi_12plus.py b/sickbeard/metadata/kodi_12plus.py index 17fd0e0c..9a916b6d 100644 --- a/sickbeard/metadata/kodi_12plus.py +++ b/sickbeard/metadata/kodi_12plus.py @@ -189,19 +189,16 @@ class KODI_12PlusMetadata(generic.GenericMetadata): cur_actor = etree.SubElement(tv_node, "actor") cur_actor_name = etree.SubElement(cur_actor, "name") - cur_actor_name_text = actor['name'] - if isinstance(cur_actor_name_text, basestring): - cur_actor_name.text = cur_actor_name_text.strip() + if getattr(actor, 'name', None) is not None: + cur_actor_name.text = actor['name'].strip() cur_actor_role = etree.SubElement(cur_actor, "role") - cur_actor_role_text = actor['role'] - if cur_actor_role_text != None: - cur_actor_role.text = cur_actor_role_text + if getattr(actor, 'role', None) is not None: + cur_actor_role.text = actor['role'] cur_actor_thumb = etree.SubElement(cur_actor, "thumb") - cur_actor_thumb_text = actor['image'] - if cur_actor_thumb_text != None: - cur_actor_thumb.text = cur_actor_thumb_text + if getattr(actor, 'image', None) is not None: + cur_actor_thumb.text = actor['image'] # Make it purdy helpers.indentXML(tv_node) diff --git a/sickbeard/metadata/mede8er.py b/sickbeard/metadata/mede8er.py index e90bca7e..9e3d836e 100644 --- a/sickbeard/metadata/mede8er.py +++ b/sickbeard/metadata/mede8er.py @@ -199,8 +199,7 @@ class Mede8erMetadata(mediabrowser.MediaBrowserMetadata): if getattr(myShow, '_actors', None) is not None: 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(): cur_actor = etree.SubElement(cast, "actor") cur_actor.text = cur_actor_name_text.strip() diff --git a/sickbeard/metadata/mediabrowser.py b/sickbeard/metadata/mediabrowser.py index ef626f92..b35c076f 100644 --- a/sickbeard/metadata/mediabrowser.py +++ b/sickbeard/metadata/mediabrowser.py @@ -362,14 +362,17 @@ class MediaBrowserMetadata(generic.GenericMetadata): if getattr(myShow, 'actors', None) is not None: for actor in myShow['_actors']: cur_actor = etree.SubElement(Persons, "Person") + 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.text = "Actor" + cur_actor_role = etree.SubElement(cur_actor, "Role") - cur_actor_role_text = actor['role'] - if cur_actor_role_text != None: - cur_actor_role.text = cur_actor_role_text + if getattr(actor, 'role', None): + cur_actor_role.text = actor['role'] helpers.indentXML(tv_node) diff --git a/sickbeard/metadata/wdtv.py b/sickbeard/metadata/wdtv.py index f6025796..fe4ed63b 100644 --- a/sickbeard/metadata/wdtv.py +++ b/sickbeard/metadata/wdtv.py @@ -275,12 +275,14 @@ class WDTVMetadata(generic.GenericMetadata): if getattr(myShow, '_actors', None) is not None: for actor in myShow['_actors']: cur_actor = etree.SubElement(episode, "actor") + 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_text = actor['role'] - if cur_actor_role_text != None: - cur_actor_role.text = cur_actor_role_text + if getattr(actor, 'role', None): + cur_actor_role.text = actor['role'] overview = etree.SubElement(episode, "overview") if curEpToWrite.description != None: diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 895e1534..3dd6b741 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -24,6 +24,7 @@ import time import urllib import re import datetime +import urlparse import sickbeard from sickbeard import config, sab @@ -134,16 +135,14 @@ class BaseHandler(RequestHandler): def write_error(self, status_code, **kwargs): # handle 404 http errors if status_code == 404: - index_url = sickbeard.WEB_ROOT - url = self.request.uri[len(index_url):] + url = self.request.uri + 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) else: - if url.endswith('/'): - self.finish('Wrong API key used') - else: - return self.redirect(url + '/') + self.finish('Wrong API key used') elif self.settings.get("debug") and "exc_info" in kwargs: exc_info = kwargs["exc_info"] @@ -166,7 +165,7 @@ class BaseHandler(RequestHandler): """ % (error, error, 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): url = sickbeard.WEB_ROOT + url @@ -371,7 +370,7 @@ class WebRoot(WebHandler): default_image_name = 'banner.png' #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)): cache_obj = image_cache.ImageCache() @@ -386,9 +385,9 @@ class WebRoot(WebHandler): image_file_name = cache_obj.banner_thumb_path(show) if ek.ek(os.path.isfile, image_file_name): - image_path = image_file_name - static_image_path = '/cache' + image_path.replace(sickbeard.CACHE_DIR, '') + static_image_path = os.path.normpath(image_file_name.replace(sickbeard.CACHE_DIR, '/cache')) + static_image_path = static_image_path.replace('\\', '/') return self.redirect(static_image_path) def setHomeLayout(self, layout): @@ -605,7 +604,7 @@ class WebRoot(WebHandler): return ical -@route('/ui/(.*)(/?)') +@route('/ui(/?.*)') class UI(WebRoot): def add_message(self): ui.notifications.message('Test 1', 'This is test number 1') @@ -625,7 +624,7 @@ class UI(WebRoot): return json.dumps(messages) -@route('/browser/(.*)(/?)') +@route('/browser(/?.*)') class WebFileBrowser(WebRoot): def index(self, path='', includeFiles=False, *args, **kwargs): self.set_header("Content-Type", "application/json") @@ -639,7 +638,7 @@ class WebFileBrowser(WebRoot): return json.dumps(paths) -@route('/home/(.*)(/?)') +@route('/home(/?.*)') class Home(WebRoot): def HomeMenu(self): menu = [ @@ -1743,11 +1742,11 @@ class Home(WebRoot): episodes = [] - # Queued Searches - for searchThread in sickbeard.searchQueueScheduler.action.get_all_ep_from_queue(show): - searchstatus = 'queued' + def getEpisodes(searchThread, searchstatus): + results = [] + if isinstance(searchThread, sickbeard.search_queue.ManualSearchQueueItem): - episodes.append({'episode': searchThread.segment.episode, + results.append({'episode': searchThread.segment.episode, 'episodeindexid': searchThread.segment.indexerid, 'season': searchThread.segment.season, 'searchstatus': searchstatus, @@ -1755,13 +1754,19 @@ class Home(WebRoot): 'quality': self.getQualityClass(searchThread.segment)}) else: for epObj in searchThread.segment: - episodes.append({'episode': epObj.episode, + results.append({'episode': epObj.episode, 'episodeindexid': epObj.indexerid, 'season': epObj.season, 'searchstatus': searchstatus, 'status': statusStrings[epObj.status], '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 if (sickbeard.searchQueueScheduler.action.is_manualsearch_in_progress()): searchThread = sickbeard.searchQueueScheduler.action.currentItem @@ -1769,12 +1774,7 @@ class Home(WebRoot): searchstatus = 'finished' else: searchstatus = 'searching' - episodes.append({'episode': searchThread.segment.episode, - 'episodeindexid': searchThread.segment.indexerid, - 'season': searchThread.segment.season, - 'searchstatus': searchstatus, - 'status': statusStrings[searchThread.segment.status], - 'quality': self.getQualityClass(searchThread.segment)}) + episodes += getEpisodes(searchThread, searchstatus) # Finished Searches 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[ 'episodeindexid'] == searchThread.segment.indexerid]: searchstatus = 'finished' - episodes.append({'episode': searchThread.segment.episode, - 'episodeindexid': searchThread.segment.indexerid, - 'season': searchThread.segment.season, - 'searchstatus': searchstatus, - 'status': statusStrings[searchThread.segment.status], - 'quality': self.getQualityClass(searchThread.segment)}) + episodes += getEpisodes(searchThread, searchstatus) else: ### These are only Failed Downloads/Retry SearchThreadItems.. lets loop through the segement/episodes if str(searchThread.show.indexerid) == show: for epObj in searchThread.segment: if not [x for x in episodes if x['episodeindexid'] == epObj.indexerid]: searchstatus = 'finished' - episodes.append({'episode': epObj.episode, - 'episodeindexid': epObj.indexerid, - 'season': epObj.season, - 'searchstatus': searchstatus, - 'status': statusStrings[epObj.status], - 'quality': self.getQualityClass(epObj)}) + episodes += getEpisodes(searchThread, searchstatus) return json.dumps({'show': show, 'episodes': episodes}) @@ -1936,7 +1926,7 @@ class Home(WebRoot): return json.dumps({'result': 'failure'}) -@route('/home/postprocess/(.*)(/?)') +@route('/home/postprocess(/?.*)') class HomePostProcess(Home): def index(self): t = PageTemplate(rh=self, file="home_postprocess.tmpl") @@ -1973,7 +1963,7 @@ class HomePostProcess(Home): return self._genericMessage("Postprocessing results", result) -@route('/home/addShows/(.*)(/?)') +@route('/home/addShows(/?.*)') class HomeAddShows(Home): def index(self): t = PageTemplate(rh=self, file="home_addShows.tmpl") @@ -2475,7 +2465,7 @@ class HomeAddShows(Home): return self.newShow(dirs_only[0], dirs_only[1:]) -@route('/manage/(.*)(/?)') +@route('/manage(/?.*)') class Manage(WebRoot): def ManageMenu(self): menu = [ @@ -3169,7 +3159,7 @@ class Manage(WebRoot): return t -@route('/manage/manageSearches/(.*)(/?)') +@route('/manage/manageSearches(/?.*)') class ManageSearches(Manage): def index(self): t = PageTemplate(rh=self, file="manage_manageSearches.tmpl") @@ -3224,7 +3214,7 @@ class ManageSearches(Manage): return self.redirect("/manage/manageSearches/") -@route('/history/(.*)(/?)') +@route('/history(/?.*)') class History(WebRoot): def index(self, limit=100): @@ -3316,7 +3306,7 @@ class History(WebRoot): return self.redirect("/history/") -@route('/config/(.*)(/?)') +@route('/config(/?.*)') class Config(WebRoot): def ConfigMenu(self): menu = [ @@ -3339,7 +3329,7 @@ class Config(WebRoot): return t -@route('/config/general/(.*)(/?)') +@route('/config/general(/?.*)') class ConfigGeneral(Config): def index(self): t = PageTemplate(rh=self, file="config_general.tmpl") @@ -3474,7 +3464,7 @@ class ConfigGeneral(Config): return self.redirect("/config/general/") -@route('/config/backuprestore/(.*)(/?)') +@route('/config/backuprestore(/?.*)') class ConfigBackupRestore(Config): def index(self): t = PageTemplate(rh=self, file="config_backuprestore.tmpl") @@ -3522,7 +3512,7 @@ class ConfigBackupRestore(Config): return finalResult -@route('/config/search/(.*)(/?)') +@route('/config/search(/?.*)') class ConfigSearch(Config): def index(self): @@ -3615,7 +3605,7 @@ class ConfigSearch(Config): return self.redirect("/config/search/") -@route('/config/postProcessing/(.*)(/?)') +@route('/config/postProcessing(/?.*)') class ConfigPostProcessing(Config): def index(self): @@ -3815,7 +3805,7 @@ class ConfigPostProcessing(Config): return 'not supported' -@route('/config/providers/(.*)(/?)') +@route('/config/providers(/?.*)') class ConfigProviders(Config): def index(self): t = PageTemplate(rh=self, file="config_providers.tmpl") @@ -4254,7 +4244,7 @@ class ConfigProviders(Config): return self.redirect("/config/providers/") -@route('/config/notifications/(.*)(/?)') +@route('/config/notifications(/?.*)') class ConfigNotifications(Config): def index(self): t = PageTemplate(rh=self, file="config_notifications.tmpl") @@ -4464,7 +4454,7 @@ class ConfigNotifications(Config): return self.redirect("/config/notifications/") -@route('/config/subtitles/(.*)(/?)') +@route('/config/subtitles(/?.*)') class ConfigSubtitles(Config): def index(self): t = PageTemplate(rh=self, file="config_subtitles.tmpl") @@ -4528,7 +4518,7 @@ class ConfigSubtitles(Config): return self.redirect("/config/subtitles/") -@route('/config/anime/(.*)(/?)') +@route('/config/anime(/?.*)') class ConfigAnime(Config): def index(self): @@ -4561,7 +4551,7 @@ class ConfigAnime(Config): return self.redirect("/config/anime/") -@route('/errorlogs/(.*)(/?)') +@route('/errorlogs(/?.*)') class ErrorLogs(WebRoot): def ErrorLogsMenu(self): menu = [ diff --git a/sickbeard/webserveInit.py b/sickbeard/webserveInit.py index f866ee39..8d154dcf 100644 --- a/sickbeard/webserveInit.py +++ b/sickbeard/webserveInit.py @@ -44,7 +44,7 @@ class SRWebServer(threading.Thread): # api root if not sickbeard.API_KEY: 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 self.enable_https = self.options['enable_https'] @@ -78,17 +78,17 @@ class SRWebServer(threading.Thread): # Main Handlers self.app.add_handlers('.*$', [ # webapi handler - (r'%s(/?)' % self.options['api_root'], ApiHandler), + (r'%s(/?.*)' % self.options['api_root'], ApiHandler), # webapi key retrieval - (r'%s/getkey(/?)' % self.options['web_root'], KeyHandler), + (r'%s/getkey(/?.*)' % self.options['web_root'], KeyHandler), # webapi builder redirect (r'%s/api/builder' % self.options['web_root'], RedirectHandler, {"url": self.options['web_root'] + '/apibuilder/'}), # webui login/logout handlers - (r'%s/login(/?)' % self.options['web_root'], LoginHandler), - (r'%s/logout(/?)' % self.options['web_root'], LogoutHandler), + (r'%s/login(/?.*)' % self.options['web_root'], LoginHandler), + (r'%s/logout(/?.*)' % self.options['web_root'], LogoutHandler), # webui redirect (r'/', RedirectHandler, {"url": self.options['web_root'] + '/home/'}),