The branch, frodo has been updated via 6e500fe08e9370cf75d3f2e3a564e443809b09f8 (commit) from 7ad4e2a109ae06fdefb8ba39f531b3c18671830f (commit)
- Log ----------------------------------------------------------------- http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/scripts;a=commit;h=6e500fe08e9370cf75d3f2e3a564e443809b09f8 commit 6e500fe08e9370cf75d3f2e3a564e443809b09f8 Author: Martijn Kaijser <mcm.kaij...@gmail.com> Date: Wed Sep 11 09:58:54 2013 +0200 [script.trakt] 2.3.1 diff --git a/script.trakt/addon.xml b/script.trakt/addon.xml index d7aa5e4..853aedf 100644 --- a/script.trakt/addon.xml +++ b/script.trakt/addon.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<addon id="script.trakt" name="trakt" version="2.3.0" provider-name="trakt.tv"> +<addon id="script.trakt" name="trakt" version="2.3.1" provider-name="trakt.tv"> <requires> <import addon="xbmc.python" version="2.1.0"/> <import addon="script.module.simplejson" version="2.0.10"/> diff --git a/script.trakt/changelog.txt b/script.trakt/changelog.txt index f2e037f..eebe516 100644 --- a/script.trakt/changelog.txt +++ b/script.trakt/changelog.txt @@ -1,3 +1,8 @@ +version 2.3.1 + - improved API error handling and debugging + - new context menu action via RunScript(script.trakt,action=contextmenu) + - silent option for manual sync via RunScript(script.trakt,action=sync,silent=True) + version 2.3.0 - moved debug settings to their own menu (nate1280) - new togglewatched action for skins/keymaps (nate1280) diff --git a/script.trakt/resources/language/English/strings.xml b/script.trakt/resources/language/English/strings.xml index 94baf2e..18f9fda 100644 --- a/script.trakt/resources/language/English/strings.xml +++ b/script.trakt/resources/language/English/strings.xml @@ -7,6 +7,7 @@ <string id="1017">Number of Retries</string> <string id="1018">Startup Delay</string> <string id="1019">Force a reload of settings from trakt.tv, save settings when finished</string> + <string id="1020">Default script action</string> <!-- Rating Settings --> <string id="1030">Rating</string> @@ -23,6 +24,8 @@ <string id="1043">Minimum percent watched to scrobble</string> <string id="1044">During scrobbling, update ID for library movie if ID is missing</string> <string id="1045">During scrobbling, update ID for library tv show if ID is missing</string> + <string id="1046">Send watching call on seek</string> + <string id="1047">Send watching call on resume</string> <string id="1050">Exclusions</string> <string id="1051">Exclude Live TV</string> <string id="1052">Exclude HTTP sources</string> @@ -216,4 +219,17 @@ <string id="1704">Simulate tagging</string> <string id="1720">Logging</string> <string id="1721">Simulate settings, if enabled will not make changes in XBMC or trakt.tv</string> + + <!-- Context Menu Entries --> + <string id="2000">Manage Movie's Lists</string> + <string id="2001">Manage Show's Lists</string> + <string id="2010">Remove from Watchlist</string> + <string id="2020">Add to Watchlist</string> + <string id="2030">Rate this Movie</string> + <string id="2031">Rate this Show</string> + <string id="2032">Rate this Episode</string> + <string id="2040">Toggle Watched</string> + <string id="2050">Manage All Lists</string> + <string id="2060">Update All Tags</string> + <string id="2070">Synchronize Library</string> </strings> diff --git a/script.trakt/resources/settings.xml b/script.trakt/resources/settings.xml index 5538e42..006a434 100644 --- a/script.trakt/resources/settings.xml +++ b/script.trakt/resources/settings.xml @@ -5,6 +5,7 @@ <setting id="password" type="text" option="hidden" label="1014" default="" /> <setting id="retries" type="slider" label="1017" range="1,1,10" default="5" option="int"/> <setting id="startup_delay" type="slider" label="1018" range="0,30" default="0" option="int"/> + <setting id="default_action" type="enum" label="1020" values="Manual Sync|Manage Lists" default="0" /> <setting type="action" label="1019" action="RunScript(script.trakt,action=loadsettings)"/> </category> <category label="1050"><!-- Exclusions --> @@ -23,6 +24,8 @@ <setting id="scrobble_min_view_time" type="slider" label="1043" range="0,5,100" default="80"/> <setting id="update_imdb_id" type="bool" label="1044" default="false"/> <setting id="update_tvdb_id" type="bool" label="1045" default="false"/> + <setting id="watching_call_on_seek" type="bool" label="1046" default="true"/> + <setting id="watching_call_on_resume" type="bool" label="1047" default="true"/> </category> <category label="1400"><!-- Synchronize --> <setting label="1410" type="lsep"/><!-- Service --> diff --git a/script.trakt/script.py b/script.trakt/script.py index a1ec449..afd9b28 100644 --- a/script.trakt/script.py +++ b/script.trakt/script.py @@ -6,6 +6,7 @@ import sys import queue import tagging import time +from traktContextMenu import traktContextMenu try: import simplejson as json @@ -27,9 +28,10 @@ def getMediaType(): def getArguments(): data = None - + default_actions = {0: "sync", 1: "managelists"} + default = utils.getSettingAsInt('default_action') if len(sys.argv) == 1: - data = {'action': "sync"} + data = {'action': default_actions[default]} else: data = {} for item in sys.argv: @@ -45,8 +47,57 @@ def Main(): args = getArguments() data = {} + if args['action'] == 'contextmenu': + buttons = [] + media_type = getMediaType() + + if utils.getSettingAsBool('tagging_enable'): + if utils.isMovie(media_type): + buttons.append("itemlists") + dbid = int(xbmc.getInfoLabel('ListItem.DBID')) + result = utils.getMovieDetailsFromXbmc(dbid, ['tag']) + if tagging.hasTraktWatchlistTag(result['tag']): + buttons.append("removefromlist") + else: + buttons.append("addtolist") + elif utils.isShow(media_type): + buttons.append("itemlists") + dbid = int(xbmc.getInfoLabel('ListItem.DBID')) + result = utils.getShowDetailsFromXBMC(dbid, ['tag']) + if tagging.hasTraktWatchlistTag(result['tag']): + buttons.append("removefromlist") + else: + buttons.append("addtolist") + + if media_type in ['movie', 'show', 'episode']: + buttons.append("rate") + + if media_type in ['movie', 'show', 'season', 'episode']: + buttons.append("togglewatched") + + if utils.getSettingAsBool('tagging_enable'): + buttons.append("managelists") + buttons.append("updatetags") + buttons.append("sync") + + contextMenu = traktContextMenu(media_type=media_type, buttons=buttons) + contextMenu.doModal() + _action = contextMenu.action + del contextMenu + + if _action is None: + return + + utils.Debug("'%s' selected from trakt.tv action menu" % _action) + args['action'] = _action + if _action in ['addtolist', 'removefromlist']: + args['list'] = "watchlist" + if args['action'] == 'sync': data = {'action': 'manualSync'} + data['silent'] = False + if 'silent' in args: + data['silent'] = (args['silent'].lower() == 'true') elif args['action'] == 'loadsettings': data = {'action': 'loadsettings', 'force': True} diff --git a/script.trakt/scrobbler.py b/script.trakt/scrobbler.py index 7206a88..823d9b3 100755 --- a/script.trakt/scrobbler.py +++ b/script.trakt/scrobbler.py @@ -117,6 +117,7 @@ class Scrobbler(): self.curVideoInfo = utilities.getMovieDetailsFromXbmc(self.curVideo['id'], ['imdbnumber', 'title', 'year']) if utilities.getSettingAsBool('rate_movie'): # pre-get sumamry information, for faster rating dialog. + Debug("[Scrobbler] Movie rating is enabled, pre-fetching summary information.") imdb_id = self.curVideoInfo['imdbnumber'] if imdb_id.startswith("tt") or imdb_id.isdigit(): self.traktSummaryInfo = self.traktapi.getMovieSummary(self.curVideoInfo['imdbnumber']) @@ -135,6 +136,7 @@ class Scrobbler(): self.curVideoInfo = utilities.getEpisodeDetailsFromXbmc(self.curVideo['id'], ['showtitle', 'season', 'episode', 'tvshowid', 'uniqueid']) if utilities.getSettingAsBool('rate_episode'): # pre-get sumamry information, for faster rating dialog. + Debug("[Scrobbler] Episode rating is enabled, pre-fetching summary information.") tvdb_id = self.curVideoInfo['tvdb_id'] if tvdb_id.isdigit() or tvdb_id.startswith("tt"): self.traktSummaryInfo = self.traktapi.getEpisodeSummary(tvdb_id, self.curVideoInfo['season'], self.curVideoInfo['episode']) @@ -173,7 +175,8 @@ class Scrobbler(): self.pausedAt = 0 self.isPaused = False self.update(True) - self.watching() + if utilities.getSettingAsBool('watching_call_on_resume'): + self.watching() def playbackPaused(self): if not self.isPlaying: @@ -191,7 +194,8 @@ class Scrobbler(): Debug("[Scrobbler] playbackSeek()") self.update(True) - self.watching() + if utilities.getSettingAsBool('watching_call_on_seek'): + self.watching() def playbackEnded(self): if not self.isPlaying: @@ -239,9 +243,10 @@ class Scrobbler(): self.curVideoInfo['imdbnumber'] = response['movie']['imdb_id'] if 'id' in self.curVideo and utilities.getSettingAsBool('update_imdb_id'): req = {"jsonrpc": "2.0", "id": 1, "method": "VideoLibrary.SetMovieDetails", "params": {"movieid" : self.curVideoInfo['movieid'], "imdbnumber": self.curVideoInfo['imdbnumber']}} - utils.xbmcJsonRequest(req) + utilities.xbmcJsonRequest(req) # get summary data now if we are rating this movie if utilities.getSettingAsBool('rate_movie') and self.traktSummaryInfo is None: + Debug("[Scrobbler] Movie rating is enabled, pre-fetching summary information.") self.traktSummaryInfo = self.traktapi.getMovieSummary(self.curVideoInfo['imdbnumber']) Debug("[Scrobbler] Watch response: %s" % str(response)) @@ -262,9 +267,10 @@ class Scrobbler(): self.curVideoInfo['tvdb_id'] = response['show']['tvdb_id'] if 'id' in self.curVideo and utilities.getSettingAsBool('update_tvdb_id'): req = {"jsonrpc": "2.0", "id": 1, "method": "VideoLibrary.SetTVShowDetails", "params": {"tvshowid" : self.curVideoInfo['tvshowid'], "imdbnumber": self.curVideoInfo['tvdb_id']}} - utils.xbmcJsonRequest(req) + utilities.xbmcJsonRequest(req) # get summary data now if we are rating this episode if utilities.getSettingAsBool('rate_episode') and self.traktSummaryInfo is None: + Debug("[Scrobbler] Episode rating is enabled, pre-fetching summary information.") self.traktSummaryInfo = self.traktapi.getEpisodeSummary(self.curVideoInfo['tvdb_id'], self.curVideoInfo['season'], self.curVideoInfo['episode']) Debug("[Scrobbler] Watch response: %s" % str(response)) @@ -304,6 +310,8 @@ class Scrobbler(): if response['error'].startswith("scrobbled") and response['error'].endswith("already"): Debug("[Scrobbler] Movie was just recently scrobbled, attempting to cancel watching instead.") self.stoppedWatching() + elif response['error'] == "movie not found": + Debug("[Scrobbler] Movie '%s' was not found on trakt.tv, possible malformed XBMC metadata." % self.curVideoInfo['title']) elif utilities.isEpisode(self.curVideo['type']) and scrobbleEpisodeOption: if self.isMultiPartEpisode: @@ -320,6 +328,8 @@ class Scrobbler(): if response['error'].startswith("scrobbled") and response['error'].endswith("already"): Debug("[Scrobbler] Episode was just recently scrobbled, attempting to cancel watching instead.") self.stoppedWatching() + elif response['error'] == "show not found": + Debug("[Scrobbler] Show '%s' was not found on trakt.tv, possible malformed XBMC metadata." % self.curVideoInfo['showtitle']) def watchlistTagCheck(self): if not utilities.isMovie(self.curVideo['type']): @@ -343,7 +353,7 @@ class Scrobbler(): tagging.xbmcSetTags(id, self.curVideo['type'], s, tags) else: - utils.Debug("No data was returned from XBMC, aborting tag udpate.") + utilities.Debug("No data was returned from XBMC, aborting tag udpate.") def check(self): scrobbleMinViewTimeOption = utilities.getSettingAsFloat("scrobble_min_view_time") diff --git a/script.trakt/service.py b/script.trakt/service.py index 410a109..1144585 100644 --- a/script.trakt/service.py +++ b/script.trakt/service.py @@ -72,7 +72,7 @@ class traktService: elif action == 'manualSync': if not self.syncThread.isAlive(): utilities.Debug("Performing a manual sync.") - self.doSync(manual=True) + self.doSync(manual=True, silent=data['silent']) else: utilities.Debug("There already is a sync in progress.") elif action == 'updatetags': @@ -345,21 +345,22 @@ class traktService: if markedNotification: utilities.notification(utilities.getString(1550), utilities.getString(1552) % (len(params['episodes']), s)) - def doSync(self, manual=False): - self.syncThread = syncThread(manual) + def doSync(self, manual=False, silent=False): + self.syncThread = syncThread(manual, silent) self.syncThread.start() class syncThread(threading.Thread): _isManual = False - def __init__(self, isManual=False): + def __init__(self, isManual=False, runSilent=False): threading.Thread.__init__(self) self.name = "trakt-sync" self._isManual = isManual + self._runSilent = runSilent def run(self): - sync = Sync(show_progress=self._isManual, api=globals.traktapi) + sync = Sync(show_progress=self._isManual, run_silent=self._runSilent, api=globals.traktapi) sync.sync() if utilities.getSettingAsBool('tagging_enable') and utilities.getSettingAsBool('tagging_tag_after_sync'): diff --git a/script.trakt/sync.py b/script.trakt/sync.py index 428deb5..97eeccd 100644 --- a/script.trakt/sync.py +++ b/script.trakt/sync.py @@ -10,9 +10,12 @@ progress = xbmcgui.DialogProgress() class Sync(): - def __init__(self, show_progress=False, api=None): + def __init__(self, show_progress=False, run_silent=False, api=None): self.traktapi = api self.show_progress = show_progress + self.run_silent = run_silent + if self.show_progress and self.run_silent: + Debug("[Sync] Sync is being run silently.") self.notify = utilities.getSettingAsBool('show_sync_notifications') self.simulate = utilities.getSettingAsBool('simulate_sync') if self.simulate: @@ -28,7 +31,7 @@ class Sync(): self.exclusions.append(_path) def isCanceled(self): - if self.show_progress and progress.iscanceled(): + if self.show_progress and not self.run_silent and progress.iscanceled(): Debug("[Sync] Sync was canceled by user.") return True elif xbmc.abortRequested: @@ -38,7 +41,7 @@ class Sync(): return False def updateProgress(self, *args, **kwargs): - if self.show_progress: + if self.show_progress and not self.run_silent: kwargs['percent'] = args[0] progress.update(**kwargs) @@ -417,17 +420,21 @@ class Sync(): def syncEpisodes(self): if not self.show_progress and utilities.getSettingAsBool('sync_on_update') and self.notify: notification('%s %s' % (utilities.getString(1400), utilities.getString(1406)), utilities.getString(1420)) #Sync started - if self.show_progress: + if self.show_progress and not self.run_silent: progress.create("%s %s" % (utilities.getString(1400), utilities.getString(1406)), line1=" ", line2=" ", line3=" ") xbmcShows = self.xbmcLoadShows() if not isinstance(xbmcShows, list) and not xbmcShows: Debug("[Episodes Sync] XBMC show list is empty, aborting tv show Sync.") + if self.show_progress and not self.run_silent: + progress.close() return traktShows = self.traktLoadShows() if not isinstance(traktShows, list): Debug("[Episodes Sync] Error getting trakt.tv show list, aborting tv show sync.") + if self.show_progress and not self.run_silent: + progress.close() return if utilities.getSettingAsBool('add_episodes_to_trakt') and not self.isCanceled(): @@ -443,13 +450,13 @@ class Sync(): self.xbmcUpdateEpisodes(xbmcShowsUpadate) if utilities.getSettingAsBool('clean_trakt_episodes') and not self.isCanceled(): - raktShowsRemove = self.compareShows(traktShows, xbmcShows) - self.traktRemoveEpisodes(raktShowsRemove) + traktShowsRemove = self.compareShows(traktShows, xbmcShows) + self.traktRemoveEpisodes(traktShowsRemove) if not self.show_progress and utilities.getSettingAsBool('sync_on_update') and self.notify: notification('%s %s' % (utilities.getString(1400), utilities.getString(1406)), utilities.getString(1421)) #Sync complete - if not self.isCanceled() and self.show_progress: + if not self.isCanceled() and self.show_progress and not self.run_silent: self.updateProgress(100, line1=" ", line2=utilities.getString(1442), line3=" ") progress.close() @@ -554,6 +561,7 @@ class Sync(): del(movie['imdbnumber']) del(movie['lastplayed']) del(movie['label']) + del(movie['file']) xbmc_movies.append(movie) @@ -699,17 +707,21 @@ class Sync(): def syncMovies(self): if not self.show_progress and utilities.getSettingAsBool('sync_on_update') and self.notify: notification('%s %s' % (utilities.getString(1400), utilities.getString(1402)), utilities.getString(1420)) #Sync started - if self.show_progress: + if self.show_progress and not self.run_silent: progress.create("%s %s" % (utilities.getString(1400), utilities.getString(1402)), line1=" ", line2=" ", line3=" ") xbmcMovies = self.xbmcLoadMovies() if not isinstance(xbmcMovies, list) and not xbmcMovies: Debug("[Movies Sync] XBMC movie list is empty, aborting movie Sync.") + if self.show_progress and not self.run_silent: + progress.close() return traktMovies = self.traktLoadMovies() if not isinstance(traktMovies, list): Debug("[Movies Sync] Error getting trakt.tv movie list, aborting movie Sync.") + if self.show_progress and not self.run_silent: + progress.close() return if utilities.getSettingAsBool('add_movies_to_trakt') and not self.isCanceled(): @@ -728,7 +740,7 @@ class Sync(): traktMoviesToRemove = self.compareMovies(traktMovies, xbmcMovies) self.traktRemoveMovies(traktMoviesToRemove) - if not self.isCanceled() and self.show_progress: + if not self.isCanceled() and self.show_progress and not self.run_silent: self.updateProgress(100, line1=utilities.getString(1431), line2=" ", line3=" ") progress.close() diff --git a/script.trakt/traktapi.py b/script.trakt/traktapi.py index 40079e2..f9a514d 100644 --- a/script.trakt/traktapi.py +++ b/script.trakt/traktapi.py @@ -38,6 +38,7 @@ class traktError(Exception): class traktAuthProblem(traktError): pass class traktServerBusy(traktError): pass class traktUnknownError(traktError): pass +class traktNotFoundError(traktError): pass class traktNetworkError(traktError): def __init__(self, value, timeout): super(traktNetworkError, self).__init__(value) @@ -94,7 +95,12 @@ class traktAPI(object): elif e.code == 503: # server busy problem raise traktServerBusy(error_data) else: - raise traktUnknownError(error_data, e.code) + try: + _data = json.loads(error_data) + if 'status' in _data: + data = error_data + except ValueError: + raise traktUnknownError(error_data, e.code) elif hasattr(e, 'reason'): # usually a read timeout, or unable to reach host raise traktNetworkError(str(e.reason), isinstance(e.reason, socket.timeout)) @@ -161,6 +167,7 @@ class traktAPI(object): except traktError, e: if isinstance(e, traktServerBusy): Debug("[traktAPI] traktRequest(): (%i) Server Busy (%s)" % (i, e.value)) + xbmc.sleep(5000) elif isinstance(e, traktAuthProblem): Debug("[traktAPI] traktRequest(): (%i) Authentication Failure (%s)" % (i, e.value)) setSetting('account_valid', False) @@ -199,12 +206,12 @@ class traktAPI(object): Debug("[traktAPI] traktRequest(): (%i) JSON response: '%s'" % (i, str(data))) except ValueError: # malformed json response - Debug("[traktAPI] traktRequest(): (%i) Bad JSON response: '%s'", (i, raw)) + Debug("[traktAPI] traktRequest(): (%i) Bad JSON response: '%s'" % (i, raw)) if not silent: notification('trakt', getString(1109) + ": Bad response from trakt") # Error # check for the status variable in JSON data - if 'status' in data: + if data and 'status' in data: if data['status'] == 'success': break elif returnOnFailure and data['status'] == 'failure': diff --git a/script.trakt/utilities.py b/script.trakt/utilities.py index e6e2806..ec67d4e 100755 --- a/script.trakt/utilities.py +++ b/script.trakt/utilities.py @@ -16,6 +16,9 @@ except ImportError: # read settings __addon__ = xbmcaddon.Addon('script.trakt') +# make strptime call prior to doing anything, to try and prevent threading errors +time.strptime("1970-01-01 12:00:00", "%Y-%m-%d %H:%M:%S") + def Debug(msg, force = False): if(getSettingAsBool('debug') or force): try: ----------------------------------------------------------------------- Summary of changes: script.trakt/.gitignore | 4 + script.trakt/addon.xml | 2 +- script.trakt/changelog.txt | 5 + .../resources/language/English/strings.xml | 16 ++++ script.trakt/resources/settings.xml | 3 + .../skins/Default/720p/traktContextMenu.xml | 85 ++++++++++++++++++++ script.trakt/script.py | 55 ++++++++++++- script.trakt/scrobbler.py | 20 ++++- script.trakt/service.py | 11 ++- script.trakt/sync.py | 30 +++++-- script.trakt/traktContextMenu.py | 82 +++++++++++++++++++ script.trakt/traktapi.py | 13 +++- script.trakt/utilities.py | 3 + 13 files changed, 304 insertions(+), 25 deletions(-) create mode 100644 script.trakt/.gitignore create mode 100644 script.trakt/resources/skins/Default/720p/traktContextMenu.xml create mode 100644 script.trakt/traktContextMenu.py hooks/post-receive -- Scripts ------------------------------------------------------------------------------ How ServiceNow helps IT people transform IT departments: 1. Consolidate legacy IT systems to a single system of record for IT 2. Standardize and globalize service processes across IT 3. Implement zero-touch automation to replace manual, redundant tasks http://pubads.g.doubleclick.net/gampad/clk?id=51271111&iu=/4140/ostg.clktrk _______________________________________________ Xbmc-addons mailing list Xbmc-addons@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/xbmc-addons