From dba4d17603b779803a561e0332ced7ac0648c4d3 Mon Sep 17 00:00:00 2001 From: "M. Adam Kendall" Date: Sun, 25 Jan 2015 15:14:57 -0500 Subject: [PATCH 1/7] Update to Trakt.tv API 2.0 --- .../default/config_notifications.tmpl | 10 --- .../interfaces/default/trendingShows.tmpl | 16 ++-- gui/slick/js/configNotifications.js | 14 +--- gui/slick/js/recommendedShows.js | 1 + lib/trakt/trakt.py | 51 +++++++++--- sickbeard/__init__.py | 7 +- sickbeard/notifiers/trakt.py | 79 +++++++++---------- sickbeard/traktChecker.py | 66 +++++++++++----- sickbeard/webserve.py | 28 ++++--- 9 files changed, 151 insertions(+), 121 deletions(-) diff --git a/gui/slick/interfaces/default/config_notifications.tmpl b/gui/slick/interfaces/default/config_notifications.tmpl index f1919d9d..87076a76 100644 --- a/gui/slick/interfaces/default/config_notifications.tmpl +++ b/gui/slick/interfaces/default/config_notifications.tmpl @@ -1390,16 +1390,6 @@ password of your Trakt account. -
- - -
#else #for $cur_show in $trending_shows: - #set $image = re.sub(r'(?im)(.*)(\..*?)$', r'\1-300\2', $cur_show['images']['poster'], 0) + #set $show_url = 'http://www.trakt.tv/shows/%s' % $cur_show['show']['ids']['slug'] -
+
- +
- <%= (cur_show['title'], ' ')[ '' == cur_show['title']] %> + <%= (cur_show['show']['title'], ' ')[ '' == cur_show['show']['title']] %>
-

$cur_show['ratings']['percentage']%

- $cur_show['ratings']['votes'] votes - +

$cur_show['show']['rating']

+ $cur_show['show']['votes'] votes + $cur_show['watchers'] watchers
diff --git a/gui/slick/js/configNotifications.js b/gui/slick/js/configNotifications.js index d62ceed4..f85806c6 100644 --- a/gui/slick/js/configNotifications.js +++ b/gui/slick/js/configNotifications.js @@ -295,18 +295,12 @@ $(document).ready(function(){ $('#testFreeMobile').prop('disabled', false); }); }); - + $('#testTrakt').click(function () { - var trakt_api = $.trim($('#trakt_api').val()); var trakt_username = $.trim($('#trakt_username').val()); var trakt_password = $.trim($('#trakt_password').val()); - if (!trakt_api || !trakt_username || !trakt_password) { + if (!trakt_username || !trakt_password) { $('#testTrakt-result').html('Please fill out the necessary fields above.'); - if (!trakt_api) { - $('#trakt_api').addClass('warning'); - } else { - $('#trakt_api').removeClass('warning'); - } if (!trakt_username) { $('#trakt_username').addClass('warning'); } else { @@ -319,10 +313,10 @@ $(document).ready(function(){ } return; } - $('#trakt_api,#trakt_username,#trakt_password').removeClass('warning'); + $('#trakt_username,#trakt_password').removeClass('warning'); $(this).prop('disabled', true); $('#testTrakt-result').html(loading); - $.get(sbRoot + '/home/testTrakt', {'api': trakt_api, 'username': trakt_username, 'password': trakt_password}) + $.get(sbRoot + '/home/testTrakt', {'username': trakt_username, 'password': trakt_password}) .done(function (data) { $('#testTrakt-result').html(data); $('#testTrakt').prop('disabled', false); diff --git a/gui/slick/js/recommendedShows.js b/gui/slick/js/recommendedShows.js index caec10d9..9f7165b3 100644 --- a/gui/slick/js/recommendedShows.js +++ b/gui/slick/js/recommendedShows.js @@ -1,4 +1,5 @@ $(document).ready(function () { + $('#searchResults').html(' loading recommended shows...'); function getRecommendedShows() { $.getJSON(sbRoot + '/home/addShows/getRecommendedShows', {}, function (data) { var firstResult = true; diff --git a/lib/trakt/trakt.py b/lib/trakt/trakt.py index c802cd40..86004bf1 100644 --- a/lib/trakt/trakt.py +++ b/lib/trakt/trakt.py @@ -1,27 +1,52 @@ import requests +import json +from sickbeard import logger -from requests.auth import HTTPBasicAuth from exceptions import traktException, traktAuthException, traktServerBusy class TraktAPI(): - def __init__(self, apikey, username=None, password=None, use_https=False, timeout=5): - self.apikey = apikey + def __init__(self, apikey, username=None, password=None, timeout=5): self.username = username self.password = password - - self.protocol = 'https://' if use_https else 'http://' self.timeout = timeout + self.api_url = 'https://api.trakt.tv/' + self.headers = { + 'Content-Type': 'application/json', + 'trakt-api-version': '2', + 'trakt-api-key': apikey, + } def validateAccount(self): - return self.traktRequest("account/test/%APIKEY%", method='POST') + if hasattr(self, 'token'): + del(self.token) + data = { + 'login': self.username, + 'password': self.password + } + try: + resp = requests.request('POST', self.api_url+"auth/login", + headers=self.headers, data=json.dumps(data)) + resp.raise_for_status() + resp = resp.json() + except (requests.HTTPError, requests.ConnectionError) as e: + if e.response.status_code == 401: + raise traktAuthException(e) + if 'token' in resp: + self.token = resp['token'] + return True + return False - def traktRequest(self, url, data=None, method='GET'): - base_url = self.protocol + 'api.trakt.tv/%s' % url.replace('%APIKEY%', self.apikey).replace('%USER%', - self.username) + def traktRequest(self, path, data=None, method='GET'): + url = self.api_url + path + headers = self.headers + if not getattr(self, 'token', None): + self.validateAccount() + headers['trakt-user-login'] = self.username + headers['trakt-user-token'] = self.token # request the URL from trakt and parse the result as json try: - resp = requests.request(method, base_url, auth=HTTPBasicAuth(self.username, self.password), data=data if data else []) + resp = requests.request(method, url, headers=headers, data=json.dumps(data) if data else []) # check for http errors and raise if any are present resp.raise_for_status() @@ -29,7 +54,11 @@ class TraktAPI(): # convert response to json resp = resp.json() except (requests.HTTPError, requests.ConnectionError) as e: - if e.response.status_code == 401: + if e.response.status_code == 502: + # Retry the request, cloudflare had a proxying issue + logger.log(u"Retrying trakt api request: %s" % path, logger.WARNING) + self.traktRequest(path, data, method) + elif e.response.status_code == 401: raise traktAuthException(e) elif e.response.status_code == 503: raise traktServerBusy(e) diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 531ac5bb..504bfe89 100755 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -392,7 +392,6 @@ SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD = False USE_TRAKT = False TRAKT_USERNAME = None TRAKT_PASSWORD = None -TRAKT_API = '' TRAKT_REMOVE_WATCHLIST = False TRAKT_REMOVE_SERIESLIST = False TRAKT_USE_WATCHLIST = False @@ -481,7 +480,7 @@ REQUIRE_WORDS = "" CALENDAR_UNPROTECTED = False TMDB_API_KEY = 'edc5f123313769de83a71e157758030b' -TRAKT_API_KEY = 'abd806c54516240c76e4ebc9c5ccf394' +TRAKT_API_KEY = 'd4161a7a106424551add171e5470112e4afdaf2438e6ef2fe0548edc75924868' FANART_API_KEY = '9b3afaf26f6241bdb57d6cc6bd798da7' __INITIALIZED__ = False @@ -502,7 +501,7 @@ def initialize(consoleLogging=True): TORRENT_USERNAME, TORRENT_PASSWORD, TORRENT_HOST, TORRENT_PATH, TORRENT_SEED_TIME, TORRENT_PAUSED, TORRENT_HIGH_BANDWIDTH, TORRENT_LABEL, TORRENT_LABEL_ANIME, TORRENT_VERIFY_CERT, \ USE_KODI, KODI_ALWAYS_ON, KODI_NOTIFY_ONSNATCH, KODI_NOTIFY_ONDOWNLOAD, KODI_NOTIFY_ONSUBTITLEDOWNLOAD, KODI_UPDATE_FULL, KODI_UPDATE_ONLYFIRST, \ KODI_UPDATE_LIBRARY, KODI_HOST, KODI_USERNAME, KODI_PASSWORD, BACKLOG_FREQUENCY, \ - USE_TRAKT, TRAKT_USERNAME, TRAKT_PASSWORD, TRAKT_API, TRAKT_REMOVE_WATCHLIST, TRAKT_USE_WATCHLIST, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, traktCheckerScheduler, TRAKT_USE_RECOMMENDED, TRAKT_SYNC, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, \ + USE_TRAKT, TRAKT_USERNAME, TRAKT_PASSWORD, TRAKT_REMOVE_WATCHLIST, TRAKT_USE_WATCHLIST, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, traktCheckerScheduler, TRAKT_USE_RECOMMENDED, TRAKT_SYNC, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, \ USE_PLEX, PLEX_NOTIFY_ONSNATCH, PLEX_NOTIFY_ONDOWNLOAD, PLEX_NOTIFY_ONSUBTITLEDOWNLOAD, PLEX_UPDATE_LIBRARY, \ PLEX_SERVER_HOST, PLEX_HOST, PLEX_USERNAME, PLEX_PASSWORD, DEFAULT_BACKLOG_FREQUENCY, MIN_BACKLOG_FREQUENCY, BACKLOG_STARTUP, SKIP_REMOVED_FILES, \ showUpdateScheduler, __INITIALIZED__, LAUNCH_BROWSER, UPDATE_SHOWS_ON_START, TRASH_REMOVE_SHOW, TRASH_ROTATE_LOGS, SORT_ARTICLE, showList, loadingShowList, \ @@ -910,7 +909,6 @@ def initialize(consoleLogging=True): USE_TRAKT = bool(check_setting_int(CFG, 'Trakt', 'use_trakt', 0)) TRAKT_USERNAME = check_setting_str(CFG, 'Trakt', 'trakt_username', '', censor_log=True) TRAKT_PASSWORD = check_setting_str(CFG, 'Trakt', 'trakt_password', '', censor_log=True) - TRAKT_API = check_setting_str(CFG, 'Trakt', 'trakt_api', '', censor_log=True) TRAKT_REMOVE_WATCHLIST = bool(check_setting_int(CFG, 'Trakt', 'trakt_remove_watchlist', 0)) TRAKT_REMOVE_SERIESLIST = bool(check_setting_int(CFG, 'Trakt', 'trakt_remove_serieslist', 0)) TRAKT_USE_WATCHLIST = bool(check_setting_int(CFG, 'Trakt', 'trakt_use_watchlist', 0)) @@ -1781,7 +1779,6 @@ def save_config(): new_config['Trakt']['use_trakt'] = int(USE_TRAKT) new_config['Trakt']['trakt_username'] = TRAKT_USERNAME new_config['Trakt']['trakt_password'] = helpers.encrypt(TRAKT_PASSWORD, ENCRYPTION_VERSION) - new_config['Trakt']['trakt_api'] = TRAKT_API new_config['Trakt']['trakt_remove_watchlist'] = int(TRAKT_REMOVE_WATCHLIST) new_config['Trakt']['trakt_remove_serieslist'] = int(TRAKT_REMOVE_SERIESLIST) new_config['Trakt']['trakt_use_watchlist'] = int(TRAKT_USE_WATCHLIST) diff --git a/sickbeard/notifiers/trakt.py b/sickbeard/notifiers/trakt.py index 39f958ca..bf809e40 100644 --- a/sickbeard/notifiers/trakt.py +++ b/sickbeard/notifiers/trakt.py @@ -42,7 +42,7 @@ class TraktNotifier: def update_library(self, ep_obj): """ Sends a request to trakt indicating that the given episode is part of our library. - + ep_obj: The TVEpisode object to add to trakt """ @@ -53,82 +53,75 @@ class TraktNotifier: try: # URL parameters data = { - 'title': ep_obj.show.name, - 'year': ep_obj.show.startyear, - 'episodes': [{ - 'season': ep_obj.season, - 'episode': ep_obj.episode - }] + 'shows': [ + { + 'title': ep_obj.show.name, + 'year': ep_obj.show.startyear, + 'ids': {}, + 'seasons': [ + { + 'number': ep_obj.season, + 'episodes': [ + { + 'number': ep_obj.episode + } + ] + } + ] + } + ] } if trakt_id == 'tvdb_id': - data[trakt_id] = ep_obj.show.indexerid + data['shows'][0]['ids']['tvdb'] = ep_obj.show.indexerid + else: + data['shows'][0]['ids']['tvrage'] = ep_obj.show.indexerid # update library - trakt_api.traktRequest("show/episode/library/%APIKEY%", data, method='POST') + trakt_api.traktRequest("sync/collection", data, method='POST') # remove from watchlist if sickbeard.TRAKT_REMOVE_WATCHLIST: - trakt_api.traktRequest("show/episode/unwatchlist/%APIKEY%", data, method='POST') + trakt_api.traktRequest("sync/watchlist/remove", data, method='POST') if sickbeard.TRAKT_REMOVE_SERIESLIST: data = { 'shows': [ { 'title': ep_obj.show.name, - 'year': ep_obj.show.startyear + 'year': ep_obj.show.startyear, + 'ids': {} } ] } if trakt_id == 'tvdb_id': - data['shows'][0][trakt_id] = ep_obj.show.indexerid + data['shows'][0]['ids']['tvdb'] = ep_obj.show.indexerid + else: + data['shows'][0]['ids']['tvrage'] = ep_obj.show.indexerid - trakt_api.traktRequest("show/unwatchlist/%APIKEY%", data, method='POST') + trakt_api.traktRequest("sync/watchlist/remove", data, method='POST') - # Remove all episodes from episode watchlist - # Start by getting all episodes in the watchlist - watchlist = trakt_api.traktRequest("user/watchlist/episodes.json/%APIKEY%/%USER%") - - # Convert watchlist to only contain current show - if watchlist: - for show in watchlist: - if show[trakt_id] == ep_obj.show.indexerid: - data_show = { - 'title': show['title'], - trakt_id: show[trakt_id], - 'episodes': [] - } - - # Add series and episode (number) to the array - for episodes in show['episodes']: - ep = {'season': episodes['season'], 'episode': episodes['number']} - data_show['episodes'].append(ep) - - trakt_api.traktRequest("show/episode/unwatchlist/%APIKEY%", data_show, method='POST') except (traktException, traktAuthException, traktServerBusy) as e: logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING) - def test_notify(self, api, username, password): + def test_notify(self, username, password): """ Sends a test notification to trakt with the given authentication info and returns a boolean representing success. - + api: The api string to use username: The username to use password: The password to use - + Returns: True if the request succeeded, False otherwise """ - - trakt_api = TraktAPI(api, username, password) - try: - if trakt_api.validateAccount(): - return "Test notice sent successfully to Trakt" + trakt_api = TraktAPI(sickbeard.TRAKT_API_KEY, username, password) + trakt_api.validateAccount() + return "Test notice sent successfully to Trakt" except (traktException, traktAuthException, traktServerBusy) as e: logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING) - - return "Test notice failed to Trakt: %s" % ex(e) + return "Test notice failed to Trakt: %s" % ex(e) notifier = TraktNotifier diff --git a/sickbeard/traktChecker.py b/sickbeard/traktChecker.py index 2c14b069..b29953c4 100644 --- a/sickbeard/traktChecker.py +++ b/sickbeard/traktChecker.py @@ -19,6 +19,7 @@ import os import traceback import datetime +import json import sickbeard from sickbeard import encodingKludge as ek @@ -59,7 +60,7 @@ class TraktChecker(): traktShow = None try: - library = self.trakt_api.traktRequest("user/library/shows/all.json/%APIKEY%/%USER%") + library = self.trakt_api.traktRequest("sync/collection/shows") or [] if not library: logger.log(u"Could not connect to trakt service, aborting library check", logger.ERROR) @@ -69,7 +70,7 @@ class TraktChecker(): logger.log(u"No shows found in your library, aborting library update", logger.DEBUG) return - traktShow = filter(lambda x: int(indexerid) in [int(x['tvdb_id']) or 0, int(x['tvrage_id'])] or 0, library) + traktShow = filter(lambda x: int(indexerid) in [int(x['show']['ids']['tvdb']) or 0, int(x['show']['ids']['tvrage'])] or 0, library) except (traktException, traktAuthException, traktServerBusy) as e: logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING) @@ -83,14 +84,26 @@ class TraktChecker(): def removeShowFromTraktLibrary(self, show_obj): if self.findShow(show_obj.indexer, show_obj.indexerid): - # URL parameters - data = {'tvdb_id': helpers.mapIndexersToShow(show_obj)[1], 'title': show_obj.name, - 'year': show_obj.startyear} + trakt_id = sickbeard.indexerApi(show_obj.indexer).config['trakt_id'] + # URL parameters + data = { + 'shows': [ + { + 'title': show_obj.name, + 'year': show_obj.startyear, + 'ids': {} + } + ] + } + if trakt_id == 'tvdb_id': + data['shows'][0]['ids']['tvdb'] = show_obj.indexerid + else: + data['shows'][0]['ids']['tvrage'] = show_obj.indexerid logger.log(u"Removing " + show_obj.name + " from trakt.tv library", logger.DEBUG) try: - self.trakt_api.traktRequest("show/unlibrary/%APIKEY%", data, method='POST') + self.trakt_api.traktRequest("sync/collection/remove", data, method='POST') except (traktException, traktAuthException, traktServerBusy) as e: logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING) pass @@ -105,16 +118,27 @@ class TraktChecker(): data = {} if not self.findShow(show_obj.indexer, show_obj.indexerid): + trakt_id = sickbeard.indexerApi(show_obj.indexer).config['trakt_id'] # URL parameters - data['tvdb_id'] = helpers.mapIndexersToShow(show_obj)[1] - data['title'] = show_obj.name - data['year'] = show_obj.startyear + data = { + 'shows': [ + { + 'title': show_obj.name, + 'year': show_obj.startyear, + 'ids': {} + } + ] + } + if trakt_id == 'tvdb_id': + data['shows'][0]['ids']['tvdb'] = show_obj.indexerid + else: + data['shows'][0]['ids']['tvrage'] = show_obj.indexerid if len(data): logger.log(u"Adding " + show_obj.name + " to trakt.tv library", logger.DEBUG) try: - self.trakt_api.traktRequest("show/library/%APIKEY%", data, method='POST') + self.trakt_api.traktRequest("sync/collection", data, method='POST') except (traktException, traktAuthException, traktServerBusy) as e: logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING) return @@ -123,7 +147,7 @@ class TraktChecker(): logger.log(u"Starting trakt show watchlist check", logger.DEBUG) try: - watchlist = self.trakt_api.traktRequest("user/watchlist/shows.json/%APIKEY%/%USER%") + watchlist = self.trakt_api.traktRequest("sync/watchlist/shows") except (traktException, traktAuthException, traktServerBusy) as e: logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING) return @@ -135,14 +159,14 @@ class TraktChecker(): for show in watchlist: indexer = int(sickbeard.TRAKT_DEFAULT_INDEXER) if indexer == 2: - indexer_id = int(show["tvrage_id"]) + indexer_id = int(show["show"]["ids"]["tvrage"]) else: - indexer_id = int(show["tvdb_id"]) + indexer_id = int(show["show"]["ids"]["tvdb"]) if int(sickbeard.TRAKT_METHOD_ADD) != 2: - self.addDefaultShow(indexer, indexer_id, show["title"], SKIPPED) + self.addDefaultShow(indexer, indexer_id, show["show"]["title"], SKIPPED) else: - self.addDefaultShow(indexer, indexer_id, show["title"], WANTED) + self.addDefaultShow(indexer, indexer_id, show["show"]["title"], WANTED) if int(sickbeard.TRAKT_METHOD_ADD) == 1: newShow = helpers.findCertainShow(sickbeard.showList, indexer_id) @@ -158,7 +182,7 @@ class TraktChecker(): logger.log(u"Starting trakt episode watchlist check", logger.DEBUG) try: - watchlist = self.trakt_api.traktRequest("user/watchlist/episodes.json/%APIKEY%/%USER%") + watchlist = self.trakt_api.traktRequest("sync/watchlist/episodes") except (traktException, traktAuthException, traktServerBusy) as e: logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING) return @@ -170,22 +194,22 @@ class TraktChecker(): for show in watchlist: indexer = int(sickbeard.TRAKT_DEFAULT_INDEXER) if indexer == 2: - indexer_id = int(show["tvrage_id"]) + indexer_id = int(show["show"]["ids"]["tvrage"]) else: - indexer_id = int(show["tvdb_id"]) + indexer_id = int(show["show"]["ids"]["tvdb"]) - self.addDefaultShow(indexer, indexer_id, show["title"], SKIPPED) + self.addDefaultShow(indexer, indexer_id, show["show"]["title"], SKIPPED) newShow = helpers.findCertainShow(sickbeard.showList, indexer_id) try: if newShow and newShow.indexer == indexer: - for episode in show["episodes"]: + for episode in show["episode"]: if newShow is not None: self.setEpisodeToWanted(newShow, episode["season"], episode["number"]) else: self.todoWanted.append((indexer_id, episode["season"], episode["number"])) except TypeError: - logger.log(u"Could not parse the output from trakt for " + show["title"], logger.DEBUG) + logger.log(u"Could not parse the output from trakt for " + show["show"]["title"], logger.DEBUG) def addDefaultShow(self, indexer, indexer_id, name, status): """ diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index b6f023f2..41b179e5 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -928,9 +928,9 @@ class Home(WebRoot): "dbloc": dbloc} - def testTrakt(self, api=None, username=None, password=None): + def testTrakt(self, username=None, password=None): # self.set_header('Cache-Control', 'max-age=0,no-cache,no-store') - return notifiers.trakt_notifier.test_notify(api, username, password) + return notifiers.trakt_notifier.test_notify(username, password) def loadShowNotifyLists(self): @@ -2221,16 +2221,17 @@ class HomeAddShows(Home): trakt_api = TraktAPI(sickbeard.TRAKT_API_KEY, sickbeard.TRAKT_USERNAME, sickbeard.TRAKT_PASSWORD) try: - recommendedlist = trakt_api.traktRequest("recommendations/shows.json/%APIKEY%", method='POST') + recommendedlist = trakt_api.traktRequest("recommendations/shows?extended=full,images") if recommendedlist: - indexers = ['tvdb_id', 'tvrage_id'] + indexers = ['tvdb', 'tvrage'] map(final_results.append, ( - [int(show[indexers[sickbeard.TRAKT_DEFAULT_INDEXER - 1]]), show['url'], show['title'], - show['overview'], - datetime.date.fromtimestamp(int(show['first_aired']) / 1000.0).strftime('%Y%m%d')] + [int(show['show']['ids'][indexers[sickbeard.TRAKT_DEFAULT_INDEXER - 1]]), + 'http://www.trakt.tv/shows/%s' % show['show']['ids']['slug'], show['show']['title'], + show['show']['overview'], + datetime.date.fromtimestamp(int(show['show']['first_aired']) / 1000.0).strftime('%Y%m%d')] for show in recommendedlist if not helpers.findCertainShow(sickbeard.showList, [ - int(show[indexers[sickbeard.TRAKT_DEFAULT_INDEXER - 1]])]))) + int(show['show']['ids'][indexers[sickbeard.TRAKT_DEFAULT_INDEXER - 1]])]))) except (traktException, traktAuthException, traktServerBusy) as e: logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING) @@ -2276,11 +2277,13 @@ class HomeAddShows(Home): trakt_api = TraktAPI(sickbeard.TRAKT_API_KEY, sickbeard.TRAKT_USERNAME, sickbeard.TRAKT_PASSWORD) try: - shows = trakt_api.traktRequest("shows/trending.json/%APIKEY%") or [] + shows = trakt_api.traktRequest("shows/trending?limit=50&extended=full,images") or [] for show in shows: try: + tvdb_id = int(show['show']['ids']['tvdb']) + tvrage_id = int(show['show']['ids']['tvrage'] or 0) if not helpers.findCertainShow(sickbeard.showList, - [int(show['tvdb_id']), int(show['tvrage_id'])]): + [tvdb_id, tvrage_id]): t.trending_shows += [show] except exceptions.MultipleShowObjectsException: continue @@ -4374,7 +4377,7 @@ class ConfigNotifications(Config): libnotify_notify_onsubtitledownload=None, use_nmj=None, nmj_host=None, nmj_database=None, nmj_mount=None, use_synoindex=None, use_nmjv2=None, nmjv2_host=None, nmjv2_dbloc=None, nmjv2_database=None, - use_trakt=None, trakt_username=None, trakt_password=None, trakt_api=None, + use_trakt=None, trakt_username=None, trakt_password=None, trakt_remove_watchlist=None, trakt_use_watchlist=None, trakt_method_add=None, trakt_start_paused=None, trakt_use_recommended=None, trakt_sync=None, trakt_default_indexer=None, trakt_remove_serieslist=None, @@ -4425,7 +4428,7 @@ class ConfigNotifications(Config): sickbeard.GROWL_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(growl_notify_onsubtitledownload) sickbeard.GROWL_HOST = config.clean_host(growl_host, default_port=23053) sickbeard.GROWL_PASSWORD = growl_password - + sickbeard.USE_FREEMOBILE = config.checkbox_to_value(use_freemobile) sickbeard.FREEMOBILE_NOTIFY_ONSNATCH = config.checkbox_to_value(freemobile_notify_onsnatch) sickbeard.FREEMOBILE_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(freemobile_notify_ondownload) @@ -4490,7 +4493,6 @@ class ConfigNotifications(Config): sickbeard.USE_TRAKT = config.checkbox_to_value(use_trakt) sickbeard.TRAKT_USERNAME = trakt_username sickbeard.TRAKT_PASSWORD = trakt_password - sickbeard.TRAKT_API = trakt_api sickbeard.TRAKT_REMOVE_WATCHLIST = config.checkbox_to_value(trakt_remove_watchlist) sickbeard.TRAKT_REMOVE_SERIESLIST = config.checkbox_to_value(trakt_remove_serieslist) sickbeard.TRAKT_USE_WATCHLIST = config.checkbox_to_value(trakt_use_watchlist) From a3bb636df27d9363ad4cde5ef0eeae77a5afab77 Mon Sep 17 00:00:00 2001 From: "M. Adam Kendall" Date: Sun, 25 Jan 2015 19:14:17 -0500 Subject: [PATCH 2/7] Fix misplaced or to prevent Nonetype errors --- sickbeard/traktChecker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sickbeard/traktChecker.py b/sickbeard/traktChecker.py index b29953c4..00e26ced 100644 --- a/sickbeard/traktChecker.py +++ b/sickbeard/traktChecker.py @@ -70,7 +70,7 @@ class TraktChecker(): logger.log(u"No shows found in your library, aborting library update", logger.DEBUG) return - traktShow = filter(lambda x: int(indexerid) in [int(x['show']['ids']['tvdb']) or 0, int(x['show']['ids']['tvrage'])] or 0, library) + traktShow = filter(lambda x: int(indexerid) in [int(x['show']['ids']['tvdb'] or 0), int(x['show']['ids']['tvrage'] or 0)], library) except (traktException, traktAuthException, traktServerBusy) as e: logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING) From 9cfc3e378821e3ba5c8491137fb81dc1d839b8b8 Mon Sep 17 00:00:00 2001 From: "M. Adam Kendall" Date: Sun, 25 Jan 2015 21:33:57 -0500 Subject: [PATCH 3/7] Make sure we're returning the result of the recursive request --- lib/trakt/trakt.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/trakt/trakt.py b/lib/trakt/trakt.py index 86004bf1..a66f3659 100644 --- a/lib/trakt/trakt.py +++ b/lib/trakt/trakt.py @@ -29,7 +29,11 @@ class TraktAPI(): resp.raise_for_status() resp = resp.json() except (requests.HTTPError, requests.ConnectionError) as e: - if e.response.status_code == 401: + if e.response.status_code == 502: + # Retry the request, cloudflare had a proxying issue + logger.log(u"Retrying trakt api request: auth/login", logger.WARNING) + return self.validateAccount() + elif e.response.status_code == 401: raise traktAuthException(e) if 'token' in resp: self.token = resp['token'] @@ -57,7 +61,7 @@ class TraktAPI(): if e.response.status_code == 502: # Retry the request, cloudflare had a proxying issue logger.log(u"Retrying trakt api request: %s" % path, logger.WARNING) - self.traktRequest(path, data, method) + return self.traktRequest(path, data, method) elif e.response.status_code == 401: raise traktAuthException(e) elif e.response.status_code == 503: From a1871e2d43ece9d85e1be98a5aca6c97b88b2686 Mon Sep 17 00:00:00 2001 From: "M. Adam Kendall" Date: Sun, 25 Jan 2015 22:31:53 -0500 Subject: [PATCH 4/7] Format the rating in whole percentage --- gui/slick/interfaces/default/trendingShows.tmpl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gui/slick/interfaces/default/trendingShows.tmpl b/gui/slick/interfaces/default/trendingShows.tmpl index 68a25f3d..89492837 100644 --- a/gui/slick/interfaces/default/trendingShows.tmpl +++ b/gui/slick/interfaces/default/trendingShows.tmpl @@ -88,9 +88,8 @@
-

$cur_show['show']['rating']

+

<%= int(cur_show['show']['rating']*10) %>%

$cur_show['show']['votes'] votes - $cur_show['watchers'] watchers From 3c9adc4590649a3051d8f80ed0bc085f1c36598e Mon Sep 17 00:00:00 2001 From: "M. Adam Kendall" Date: Tue, 27 Jan 2015 09:41:02 -0500 Subject: [PATCH 5/7] Try to catch situation where no response is set on error --- lib/trakt/trakt.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/trakt/trakt.py b/lib/trakt/trakt.py index a66f3659..84c31584 100644 --- a/lib/trakt/trakt.py +++ b/lib/trakt/trakt.py @@ -29,12 +29,21 @@ class TraktAPI(): resp.raise_for_status() resp = resp.json() except (requests.HTTPError, requests.ConnectionError) as e: - if e.response.status_code == 502: + code = getattr(e.response, 'status_code', None) + if not code: + # This is pretty much a fatal error if there is no status_code + # It means there basically was no response at all + raise traktException(e) + elif code == 502: # Retry the request, cloudflare had a proxying issue logger.log(u"Retrying trakt api request: auth/login", logger.WARNING) return self.validateAccount() - elif e.response.status_code == 401: + elif code == 401: raise traktAuthException(e) + elif code == 503: + raise traktServerBusy(e) + else: + raise traktException(e) if 'token' in resp: self.token = resp['token'] return True @@ -58,13 +67,18 @@ class TraktAPI(): # convert response to json resp = resp.json() except (requests.HTTPError, requests.ConnectionError) as e: - if e.response.status_code == 502: + code = getattr(e.response, 'status_code', None) + if not code: + # This is pretty much a fatal error if there is no status_code + # It means there basically was no response at all + raise traktException(e) + elif code == 502: # Retry the request, cloudflare had a proxying issue logger.log(u"Retrying trakt api request: %s" % path, logger.WARNING) return self.traktRequest(path, data, method) - elif e.response.status_code == 401: + elif code == 401: raise traktAuthException(e) - elif e.response.status_code == 503: + elif code == 503: raise traktServerBusy(e) else: raise traktException(e) From 94d17c286ef25b37358cf7eb3a8a2ceb6e2a6917 Mon Sep 17 00:00:00 2001 From: "M. Adam Kendall" Date: Tue, 27 Jan 2015 14:26:53 -0500 Subject: [PATCH 6/7] Update to provide option to disable SSL certificate verification --- .../default/config_notifications.tmpl | 53 +++++++++++-------- gui/slick/js/configNotifications.js | 21 ++++---- lib/trakt/trakt.py | 10 ++-- sickbeard/__init__.py | 5 +- sickbeard/notifiers/trakt.py | 6 +-- sickbeard/traktChecker.py | 2 +- sickbeard/webserve.py | 15 ++++-- 7 files changed, 66 insertions(+), 46 deletions(-) diff --git a/gui/slick/interfaces/default/config_notifications.tmpl b/gui/slick/interfaces/default/config_notifications.tmpl index 87076a76..1c64516c 100644 --- a/gui/slick/interfaces/default/config_notifications.tmpl +++ b/gui/slick/interfaces/default/config_notifications.tmpl @@ -12,9 +12,9 @@ -#if $varExists('header') +#if $varExists('header')

$header

-#else +#else

$title

#end if @@ -30,7 +30,7 @@
- +

KODI

@@ -110,7 +110,7 @@

only send library updates to the first active host ?

-
+
- +
@@ -232,7 +232,7 @@ +
+ -
+
- +
@@ -430,7 +430,7 @@ @@ -446,8 +446,8 @@
- - + +
@@ -563,7 +563,7 @@
- +
@@ -576,7 +576,7 @@ @@ -587,7 +587,7 @@ @@ -1203,7 +1203,7 @@ @@ -1214,7 +1214,7 @@ @@ -1266,7 +1266,7 @@
- +
@@ -1390,6 +1390,15 @@ password of your Trakt account.
+
+ +
-
+
-
-
+
+