def __scrobble(self, status): if not self.curVideoInfo: return logger.debug("scrobble()") scrobbleMovieOption = kodiUtilities.getSettingAsBool('scrobble_movie') scrobbleEpisodeOption = kodiUtilities.getSettingAsBool('scrobble_episode') watchedPercent = self.__calculateWatchedPercent() if utilities.isMovie(self.curVideo['type']) and scrobbleMovieOption: response = self.traktapi.scrobbleMovie(self.curVideoInfo, watchedPercent, status) if response is not None: self.__scrobbleNotification(response) logger.debug("Scrobble response: %s" % str(response)) return response else: logger.debug("Failed to scrobble movie: %s | %s | %s" % (self.curVideoInfo, watchedPercent, status)) elif utilities.isEpisode(self.curVideo['type']) and scrobbleEpisodeOption: if self.isMultiPartEpisode: logger.debug("Multi-part episode, scrobbling part %d of %d." % (self.curMPEpisode + 1, self.curVideo['multi_episode_count'])) adjustedDuration = int(self.videoDuration / self.curVideo['multi_episode_count']) watchedPercent = ((self.watchedTime - (adjustedDuration * self.curMPEpisode)) / adjustedDuration) * 100 response = self.traktapi.scrobbleEpisode(self.traktShowSummary, self.curVideoInfo, watchedPercent, status) if response is not None: self.__scrobbleNotification(response) logger.debug("Scrobble response: %s" % str(response)) return response else: logger.debug("Failed to scrobble episode: %s | %s | %s | %s" % (self.traktShowSummary, self.curVideoInfo, watchedPercent, status))
def __preFetchUserRatings(self, result): if result: if utilities.isMovie( self.curVideo['type']) and kodiUtilities.getSettingAsBool( 'rate_movie'): # pre-get summary information, for faster rating dialog. logger.debug( "Movie rating is enabled, pre-fetching summary information." ) self.curVideoInfo = result['movie'] self.curVideoInfo['user'] = { 'ratings': self.traktapi.getMovieRatingForUser( result['movie']['ids']['trakt'], 'trakt') } elif utilities.isEpisode( self.curVideo['type']) and kodiUtilities.getSettingAsBool( 'rate_episode'): # pre-get summary information, for faster rating dialog. logger.debug( "Episode rating is enabled, pre-fetching summary information." ) self.curVideoInfo = result['episode'] self.curVideoInfo['user'] = { 'ratings': self.traktapi.getEpisodeRatingForUser( result['show']['ids']['trakt'], self.curVideoInfo['season'], self.curVideoInfo['number'], 'trakt') } logger.debug('Pre-Fetch result: %s; Info: %s' % (result, self.curVideoInfo))
def __scrobble(self, status): if not self.curVideoInfo: return logger.debug("scrobble()") scrobbleMovieOption = kodiUtilities.getSettingAsBool('scrobble_movie') scrobbleEpisodeOption = kodiUtilities.getSettingAsBool('scrobble_episode') watchedPercent = self.__calculateWatchedPercent() if utilities.isMovie(self.curVideo['type']) and scrobbleMovieOption: response = self.traktapi.scrobbleMovie(self.curVideoInfo, watchedPercent, status) if response is not None: self.__scrobbleNotification(response) logger.debug("Scrobble response: %s" % str(response)) return response else: logger.debug("Failed to scrobble movie: %s | %s | %s" % (self.curVideoInfo, watchedPercent, status)) elif utilities.isEpisode(self.curVideo['type']) and scrobbleEpisodeOption: if self.isMultiPartEpisode: logger.debug("Multi-part episode, scrobbling part %d of %d." % (self.curMPEpisode + 1, self.curVideo['multi_episode_count'])) adjustedDuration = int(self.videoDuration / self.curVideo['multi_episode_count']) watchedPercent = ((self.watchedTime - (adjustedDuration * self.curMPEpisode)) / adjustedDuration) * 100 logger.debug("scrobble sending show object: %s" % str(self.traktShowSummary)) logger.debug("scrobble sending episode object: %s" % str(self.curVideoInfo)) response = self.traktapi.scrobbleEpisode(self.traktShowSummary, self.curVideoInfo, watchedPercent, status) if (kodiUtilities.getSettingAsBool('scrobble_secondary_title')): logger.debug('[traktPlayer] Setting is enabled to try secondary show title, if necessary.') # If there is an empty response, the reason might be that the title we have isn't the actual show title, # but rather an alternative title. To handle this case, call the Trakt search function. if response is None: logger.debug("Searching for show title: %s" % self.traktShowSummary['title']) # This text query API is basically the same as searching on the website. Works with alternative # titles, unlike the scrobble function. newResp = self.traktapi.getTextQuery(self.traktShowSummary['title'], "show", None) if not newResp: logger.debug("Empty Response from getTextQuery, giving up") else: logger.debug("Got Response from getTextQuery: %s" % str(newResp)) # We got something back. Have to assume the first show found is the right one; if there's more than # one, there's no way to know which to use. Pull the primary title from the response (and the year, # just because it's there). showObj = {'title': newResp[0].title, 'year': newResp[0].year} logger.debug("scrobble sending getTextQuery first show object: %s" % str(showObj)) # Now we can attempt the scrobble again, using the primary title this time. response = self.traktapi.scrobbleEpisode(showObj, self.curVideoInfo, watchedPercent, status) if response is not None: self.__scrobbleNotification(response) logger.debug("Scrobble response: %s" % str(response)) return response else: logger.debug("Failed to scrobble episode: %s | %s | %s | %s" % (self.traktShowSummary, self.curVideoInfo, watchedPercent, status))
def __init__(self, show_progress=False, run_silent=False, library="all", api=None): self.traktapi = api self.show_progress = show_progress self.run_silent = run_silent self.library = library if self.show_progress and self.run_silent: logger.debug("Sync is being run silently.") self.sync_on_update = getSettingAsBool('sync_on_update') self.notify = getSettingAsBool('show_sync_notifications') self.notify_during_playback = not (xbmc.Player().isPlayingVideo() and getSettingAsBool("hide_notifications_playback"))
def _dispatch(self, data): try: logger.debug("Dispatch: %s" % data) action = data['action'] if action == 'started': del data['action'] self.scrobbler.playbackStarted(data) elif action == 'ended' or action == 'stopped': self.scrobbler.playbackEnded() elif action == 'paused': self.scrobbler.playbackPaused() elif action == 'resumed': self.scrobbler.playbackResumed() elif action == 'seek' or action == 'seekchapter': self.scrobbler.playbackSeek() elif action == 'scanFinished': if kodiUtilities.getSettingAsBool('sync_on_update'): logger.debug("Performing sync after library update.") self.doSync() elif action == 'databaseCleaned': if kodiUtilities.getSettingAsBool('sync_on_update') and ( kodiUtilities.getSettingAsBool('clean_trakt_movies') or kodiUtilities.getSettingAsBool('clean_trakt_episodes') ): logger.debug("Performing sync after library clean.") self.doSync() elif action == 'markWatched': del data['action'] self.doMarkWatched(data) elif action == 'manualRating': ratingData = data['ratingData'] self.doManualRating(ratingData) elif action == 'addtowatchlist': # add to watchlist del data['action'] self.doAddToWatchlist(data) elif action == 'manualSync': if not self.syncThread.isAlive(): logger.debug("Performing a manual sync.") self.doSync(manual=True, silent=data['silent'], library=data['library']) else: logger.debug("There already is a sync in progress.") elif action == 'settings': kodiUtilities.showSettings() elif action == 'auth_info': xbmc.executebuiltin('Dialog.Close(all, true)') # init traktapi class globals.traktapi = traktAPI(True) else: logger.debug("Unknown dispatch action, '%s'." % action) except Exception as ex: message = utilities.createError(ex) logger.fatal(message)
def __preFetchUserRatings(self, result): if result: if utilities.isMovie(self.curVideo['type']) and kodiUtilities.getSettingAsBool('rate_movie'): # pre-get summary information, for faster rating dialog. logger.debug("Movie rating is enabled, pre-fetching summary information.") self.curVideoInfo = result['movie'] self.curVideoInfo['user'] = {'ratings': self.traktapi.getMovieRatingForUser(result['movie']['ids']['trakt'], 'trakt')} elif utilities.isEpisode(self.curVideo['type']) and kodiUtilities.getSettingAsBool('rate_episode'): # pre-get summary information, for faster rating dialog. logger.debug("Episode rating is enabled, pre-fetching summary information.") self.curVideoInfo = result['episode'] self.curVideoInfo['user'] = {'ratings': self.traktapi.getEpisodeRatingForUser(result['show']['ids']['trakt'], self.curVideoInfo['season'], self.curVideoInfo['number'], 'trakt')} logger.debug('Pre-Fetch result: %s; Info: %s' % (result, self.curVideoInfo))
def _dispatch(self, data): try: logger.debug("Dispatch: %s" % data) action = data['action'] if action == 'started': del data['action'] self.scrobbler.playbackStarted(data) elif action == 'ended' or action == 'stopped': self.scrobbler.playbackEnded() elif action == 'paused': self.scrobbler.playbackPaused() elif action == 'resumed': self.scrobbler.playbackResumed() elif action == 'seek' or action == 'seekchapter': self.scrobbler.playbackSeek() elif action == 'scanFinished': if kodiUtilities.getSettingAsBool('sync_on_update'): logger.debug("Performing sync after library update.") self.doSync() elif action == 'databaseCleaned': if kodiUtilities.getSettingAsBool('sync_on_update') and (kodiUtilities.getSettingAsBool('clean_trakt_movies') or kodiUtilities.getSettingAsBool('clean_trakt_episodes')): logger.debug("Performing sync after library clean.") self.doSync() elif action == 'markWatched': del data['action'] self.doMarkWatched(data) elif action == 'manualRating': ratingData = data['ratingData'] self.doManualRating(ratingData) elif action == 'addtowatchlist': # add to watchlist del data['action'] self.doAddToWatchlist(data) elif action == 'manualSync': if not self.syncThread.isAlive(): logger.debug("Performing a manual sync.") self.doSync(manual=True, silent=data['silent'], library=data['library']) else: logger.debug("There already is a sync in progress.") elif action == 'settings': kodiUtilities.showSettings() elif action == 'auth_info': xbmc.executebuiltin('Dialog.Close(all, true)') # init traktapi class globals.traktapi = traktAPI(True) else: logger.debug("Unknown dispatch action, '%s'." % action) except Exception as ex: message = utilities.createError(ex) logger.fatal(message)
def __deleteEpisodesFromTraktCollection(self, traktShows, kodiShows, fromPercent, toPercent): if kodiUtilities.getSettingAsBool('clean_trakt_episodes') and not self.sync.IsCanceled(): removeTraktShows = copy.deepcopy(traktShows) removeKodiShows = copy.deepcopy(kodiShows) traktShowsRemove = self.__compareEpisodes(removeTraktShows, removeKodiShows) utilities.sanitizeShows(traktShowsRemove) if len(traktShowsRemove['shows']) == 0: self.sync.UpdateProgress(toPercent, line1=kodiUtilities.getString(32077), line2=kodiUtilities.getString(32110)) logger.debug('[Episodes Sync] Trakt.tv episode collection is clean, no episodes to remove.') return logger.debug("[Episodes Sync] %i show(s) will have episodes removed from Trakt.tv collection." % len(traktShowsRemove['shows'])) for show in traktShowsRemove['shows']: logger.debug("[Episodes Sync] Episodes removed: %s" % self.__getShowAsString(show, short=True)) self.sync.UpdateProgress(fromPercent, line1=kodiUtilities.getString(32077), line2=kodiUtilities.getString(32111) % self.__countEpisodes(traktShowsRemove)) logger.debug("[traktRemoveEpisodes] Shows to remove %s" % traktShowsRemove) try: self.sync.traktapi.removeFromCollection(traktShowsRemove) except Exception as ex: message = utilities.createError(ex) logging.fatal(message) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32112) % self.__countEpisodes(traktShowsRemove))
def __addMovieProgressToKodi(self, traktMovies, kodiMovies, fromPercent, toPercent): if kodiUtilities.getSettingAsBool('trakt_movie_playback') and traktMovies and not self.sync.IsCanceled(): updateKodiTraktMovies = copy.deepcopy(traktMovies) updateKodiKodiMovies = copy.deepcopy(kodiMovies) kodiMoviesToUpdate = utilities.compareMovies(updateKodiTraktMovies['movies'], updateKodiKodiMovies, restrict=True, playback=True) if len(kodiMoviesToUpdate) == 0: self.sync.UpdateProgress(toPercent, line1='', line2=kodiUtilities.getString(32125)) logger.debug("[Movies Sync] Kodi movie playbacks are up to date.") return logger.debug("[Movies Sync] %i movie(s) playbacks will be updated in Kodi" % len(kodiMoviesToUpdate)) self.sync.UpdateProgress(fromPercent, line1='', line2=kodiUtilities.getString(32126) % len(kodiMoviesToUpdate)) # need to calculate the progress in int from progress in percent from Trakt # split movie list into chunks of 50 chunksize = 50 chunked_movies = utilities.chunks([{"jsonrpc": "2.0", "id": i, "method": "VideoLibrary.SetMovieDetails", "params": {"movieid": kodiMoviesToUpdate[i]['movieid'], "resume": {"position": kodiMoviesToUpdate[i]['runtime'] / 100.0 * kodiMoviesToUpdate[i]['progress'], "total": kodiMoviesToUpdate[i]['runtime']}}} for i in range(len(kodiMoviesToUpdate))], chunksize) i = 0 x = float(len(kodiMoviesToUpdate)) for chunk in chunked_movies: if self.sync.IsCanceled(): return i += 1 y = ((i / x) * (toPercent-fromPercent)) + fromPercent self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString(32127) % ((i) * chunksize if (i) * chunksize < x else x, x)) kodiUtilities.kodiJsonRequest(chunk) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32128) % len(kodiMoviesToUpdate))
def __scrobbleNotification(self, info): if not self.curVideoInfo: return if kodiUtilities.getSettingAsBool("scrobble_notification"): s = utilities.getFormattedItemName(self.curVideo['type'], info[self.curVideo['type']]) kodiUtilities.notification(kodiUtilities.getString(32015), s)
def __addMoviesToKodiWatched(self, traktMovies, kodiMovies, fromPercent, toPercent): if kodiUtilities.getSettingAsBool('kodi_movie_playcount') and not self.sync.IsCanceled(): updateKodiTraktMovies = copy.deepcopy(traktMovies) updateKodiKodiMovies = copy.deepcopy(kodiMovies) kodiMoviesToUpdate = utilities.compareMovies(updateKodiTraktMovies, updateKodiKodiMovies, watched=True, restrict=True) if len(kodiMoviesToUpdate) == 0: self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32088)) logger.debug("[Movies Sync] Kodi movie playcount is up to date.") return titles = ", ".join(["%s" % (m['title']) for m in kodiMoviesToUpdate]) logger.debug("[Movies Sync] %i movie(s) playcount will be updated in Kodi" % len(kodiMoviesToUpdate)) logger.debug("[Movies Sync] Movies to add: %s" % titles) self.sync.UpdateProgress(fromPercent, line2=kodiUtilities.getString(32065) % len(kodiMoviesToUpdate)) # split movie list into chunks of 50 chunksize = 50 chunked_movies = utilities.chunks([{"jsonrpc": "2.0", "method": "VideoLibrary.SetMovieDetails", "params": {"movieid": kodiMoviesToUpdate[i]['movieid'], "playcount": kodiMoviesToUpdate[i]['plays'], "lastplayed": utilities.convertUtcToDateTime(kodiMoviesToUpdate[i]['last_watched_at'])}, "id": i} for i in range(len(kodiMoviesToUpdate))], chunksize) i = 0 x = float(len(kodiMoviesToUpdate)) for chunk in chunked_movies: if self.sync.IsCanceled(): return i += 1 y = ((i / x) * (toPercent-fromPercent)) + fromPercent self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString(32089) % ((i) * chunksize if (i) * chunksize < x else x, x)) kodiUtilities.kodiJsonRequest(chunk) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32090) % len(kodiMoviesToUpdate))
def __traktLoadShowsPlaybackProgress(self, fromPercent, toPercent): if kodiUtilities.getSettingAsBool('trakt_episode_playback') and not self.sync.IsCanceled(): self.sync.UpdateProgress(fromPercent, line1=kodiUtilities.getString(1485), line2=kodiUtilities.getString(32119)) logger.debug('[Playback Sync] Getting playback progress from Trakt.tv') try: traktProgressShows = self.sync.traktapi.getEpisodePlaybackProgress() except Exception as ex: logger.debug("[Playback Sync] Invalid Trakt.tv progress list, possible error getting data from Trakt, aborting Trakt.tv playback update. Error: %s" % ex) return False i = 0 x = float(len(traktProgressShows)) showsProgress = {'shows': []} for show in traktProgressShows: i += 1 y = ((i / x) * (toPercent-fromPercent)) + fromPercent self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString(32120) % (i, x)) # will keep the data in python structures - just like the KODI response show = show.to_dict() showsProgress['shows'].append(show) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32121)) return showsProgress
def __traktLoadMoviesPlaybackProgress(self, fromPercent, toPercent): if kodiUtilities.getSettingAsBool('trakt_movie_playback') and not self.sync.IsCanceled(): self.sync.UpdateProgress(fromPercent, line2=kodiUtilities.getString(32122)) logger.debug('[Movies Sync] Getting playback progress from Trakt.tv') try: traktProgressMovies = self.sync.traktapi.getMoviePlaybackProgress() except Exception: logger.debug("[Movies Sync] Invalid Trakt.tv playback progress list, possible error getting data from Trakt, aborting Trakt.tv playback update.") return False i = 0 x = float(len(traktProgressMovies)) moviesProgress = {'movies': []} for movie in traktProgressMovies: i += 1 y = ((i / x) * (toPercent-fromPercent)) + fromPercent self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString(32123) % (i, x)) # will keep the data in python structures - just like the KODI response movie = movie.to_dict() moviesProgress['movies'].append(movie) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32124)) return moviesProgress
def __deleteMoviesFromTraktCollection(self, traktMovies, kodiMovies, fromPercent, toPercent): if kodiUtilities.getSettingAsBool('clean_trakt_movies') and not self.sync.IsCanceled(): removeTraktMovies = copy.deepcopy(traktMovies) removeKodiMovies = copy.deepcopy(kodiMovies) logger.debug("[Movies Sync] Starting to remove.") traktMoviesToRemove = utilities.compareMovies(removeTraktMovies, removeKodiMovies) utilities.sanitizeMovies(traktMoviesToRemove) logger.debug("[Movies Sync] Compared movies, found %s to remove." % len(traktMoviesToRemove)) if len(traktMoviesToRemove) == 0: self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32091)) logger.debug("[Movies Sync] Trakt.tv movie collection is clean, no movies to remove.") return titles = ", ".join(["%s" % (m['title']) for m in traktMoviesToRemove]) logger.debug("[Movies Sync] %i movie(s) will be removed from Trakt.tv collection." % len(traktMoviesToRemove)) logger.debug("[Movies Sync] Movies removed: %s" % titles) self.sync.UpdateProgress(fromPercent, line2=kodiUtilities.getString(32076) % len(traktMoviesToRemove)) moviesToRemove = {'movies': traktMoviesToRemove} try: self.sync.traktapi.removeFromCollection(moviesToRemove) except Exception as ex: message = utilities.createError(ex) logging.fatal(message) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32092) % len(traktMoviesToRemove))
def __addMoviesToTraktCollection(self, kodiMovies, traktMovies, fromPercent, toPercent): if kodiUtilities.getSettingAsBool('add_movies_to_trakt') and not self.sync.IsCanceled(): addTraktMovies = copy.deepcopy(traktMovies) addKodiMovies = copy.deepcopy(kodiMovies) traktMoviesToAdd = utilities.compareMovies(addKodiMovies, addTraktMovies) utilities.sanitizeMovies(traktMoviesToAdd) logger.debug("[Movies Sync] Compared movies, found %s to add." % len(traktMoviesToAdd)) if len(traktMoviesToAdd) == 0: self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32084)) logger.debug("[Movies Sync] Trakt.tv movie collection is up to date.") return titles = ", ".join(["%s" % (m['title']) for m in traktMoviesToAdd]) logger.debug("[Movies Sync] %i movie(s) will be added to Trakt.tv collection." % len(traktMoviesToAdd)) logger.debug("[Movies Sync] Movies to add : %s" % titles) self.sync.UpdateProgress(fromPercent, line2=kodiUtilities.getString(32063) % len(traktMoviesToAdd)) moviesToAdd = {'movies': traktMoviesToAdd} # logger.debug("Movies to add: %s" % moviesToAdd) try: self.sync.traktapi.addToCollection(moviesToAdd) except Exception as ex: message = utilities.createError(ex) logging.fatal(message) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32085) % len(traktMoviesToAdd))
def __addMoviesToTraktWatched(self, kodiMovies, traktMovies, fromPercent, toPercent): if kodiUtilities.getSettingAsBool( 'trakt_movie_playcount') and not self.sync.IsCanceled(): updateTraktTraktMovies = copy.deepcopy(traktMovies) updateTraktKodiMovies = copy.deepcopy(kodiMovies) traktMoviesToUpdate = utilities.compareMovies( updateTraktKodiMovies, updateTraktTraktMovies, watched=True) utilities.sanitizeMovies(traktMoviesToUpdate) if len(traktMoviesToUpdate) == 0: self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32086)) logger.debug( "[Movies Sync] Trakt.tv movie playcount is up to date") return titles = ", ".join( ["%s" % (m['title']) for m in traktMoviesToUpdate]) logger.debug( "[Movies Sync] %i movie(s) playcount will be updated on Trakt.tv" % len(traktMoviesToUpdate)) logger.debug("[Movies Sync] Movies updated: %s" % titles) self.sync.UpdateProgress(fromPercent, line2=kodiUtilities.getString(32064) % len(traktMoviesToUpdate)) # Send request to update playcounts on Trakt.tv chunksize = 200 chunked_movies = utilities.chunks( [movie for movie in traktMoviesToUpdate], chunksize) errorcount = 0 i = 0 x = float(len(traktMoviesToUpdate)) for chunk in chunked_movies: if self.sync.IsCanceled(): return i += 1 y = ((i / x) * (toPercent - fromPercent)) + fromPercent self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString(32093) % ((i) * chunksize if (i) * chunksize < x else x, x)) params = {'movies': chunk} # logger.debug("moviechunk: %s" % params) try: self.sync.traktapi.addToHistory(params) except Exception as ex: message = utilities.createError(ex) logging.fatal(message) errorcount += 1 logger.debug("[Movies Sync] Movies updated: %d error(s)" % errorcount) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32087) % len(traktMoviesToUpdate))
def ratingCheck(media_type, summary_info, watched_time, total_time, playlist_length): """Check if a video should be rated and if so launches the rating dialog""" logger.debug("Rating Check called for '%s'" % media_type) if not kodiUtilities.getSettingAsBool("rate_%s" % media_type): logger.debug("'%s' is configured to not be rated." % media_type) return if summary_info is None: logger.debug("Summary information is empty, aborting.") return watched = (watched_time / total_time) * 100 if watched >= kodiUtilities.getSettingAsFloat("rate_min_view_time"): if (playlist_length <= 1) or kodiUtilities.getSettingAsBool("rate_each_playlist_item"): rateMedia(media_type, summary_info) else: logger.debug("Rate each playlist item is disabled.") else: logger.debug("'%s' does not meet minimum view time for rating (watched: %0.2f%%, minimum: %0.2f%%)" % (media_type, watched, kodiUtilities.getSettingAsFloat("rate_min_view_time")))
def __scrobble(self, status): if not self.curVideoInfo: return logger.debug("scrobble()") scrobbleMovieOption = kodiUtilities.getSettingAsBool('scrobble_movie') scrobbleEpisodeOption = kodiUtilities.getSettingAsBool( 'scrobble_episode') watchedPercent = self.__calculateWatchedPercent() if utilities.isMovie(self.curVideo['type']) and scrobbleMovieOption: response = self.traktapi.scrobbleMovie(self.curVideoInfo, watchedPercent, status) if response is not None: self.__scrobbleNotification(response) logger.debug("Scrobble response: %s" % str(response)) return response else: logger.debug("Failed to scrobble movie: %s | %s | %s" % (self.curVideoInfo, watchedPercent, status)) elif utilities.isEpisode( self.curVideo['type']) and scrobbleEpisodeOption: if self.isMultiPartEpisode: logger.debug("Multi-part episode, scrobbling part %d of %d." % (self.curMPEpisode + 1, self.curVideo['multi_episode_count'])) adjustedDuration = int(self.videoDuration / self.curVideo['multi_episode_count']) watchedPercent = ((self.watchedTime - (adjustedDuration * self.curMPEpisode)) / adjustedDuration) * 100 response = self.traktapi.scrobbleEpisode(self.traktShowSummary, self.curVideoInfo, watchedPercent, status) if response is not None: self.__scrobbleNotification(response) logger.debug("Scrobble response: %s" % str(response)) return response else: logger.debug("Failed to scrobble episode: %s | %s | %s | %s" % (self.traktShowSummary, self.curVideoInfo, watchedPercent, status))
def ratingCheck(media_type, summary_info, watched_time, total_time, playlist_length): """Check if a video should be rated and if so launches the rating dialog""" logger.debug("Rating Check called for '%s'" % media_type) if not kodiUtilities.getSettingAsBool("rate_%s" % media_type): logger.debug("'%s' is configured to not be rated." % media_type) return if summary_info is None: logger.debug("Summary information is empty, aborting.") return watched = (watched_time / total_time) * 100 if watched >= kodiUtilities.getSettingAsFloat("rate_min_view_time"): if (playlist_length <= 1 ) or kodiUtilities.getSettingAsBool("rate_each_playlist_item"): rateMedia(media_type, summary_info) else: logger.debug("Rate each playlist item is disabled.") else: logger.debug( "'%s' does not meet minimum view time for rating (watched: %0.2f%%, minimum: %0.2f%%)" % (media_type, watched, kodiUtilities.getSettingAsFloat("rate_min_view_time")))
def emit(self, record): levels = { logging.CRITICAL: xbmc.LOGFATAL, logging.ERROR: xbmc.LOGERROR, logging.WARNING: xbmc.LOGWARNING, logging.INFO: xbmc.LOGINFO, logging.DEBUG: xbmc.LOGDEBUG, logging.NOTSET: xbmc.LOGNONE, } if getSettingAsBool('debug'): try: xbmc.log(self.format(record), levels[record.levelno]) except UnicodeEncodeError: xbmc.log(self.format(record).encode('utf-8', 'ignore'), levels[record.levelno])
def __syncEpisodeRatings(self, traktShows, kodiShows, fromPercent, toPercent): if kodiUtilities.getSettingAsBool('trakt_sync_ratings') and traktShows and not self.sync.IsCanceled(): updateKodiTraktShows = copy.deepcopy(traktShows) updateKodiKodiShows = copy.deepcopy(kodiShows) traktShowsToUpdate = self.__compareEpisodes(updateKodiKodiShows, updateKodiTraktShows, rating=True) if len(traktShowsToUpdate['shows']) == 0: self.sync.UpdateProgress(toPercent, line1='', line2=kodiUtilities.getString(32181)) logger.debug("[Episodes Sync] Trakt episode ratings are up to date.") else: logger.debug("[Episodes Sync] %i show(s) will have episode ratings added on Trakt" % len(traktShowsToUpdate['shows'])) self.sync.UpdateProgress(fromPercent, line1='', line2=kodiUtilities.getString(32182) % len(traktShowsToUpdate['shows'])) self.sync.traktapi.addRating(traktShowsToUpdate) kodiShowsUpdate = self.__compareEpisodes(updateKodiTraktShows, updateKodiKodiShows, restrict=True, rating=True) if len(kodiShowsUpdate['shows']) == 0: self.sync.UpdateProgress(toPercent, line1='', line2=kodiUtilities.getString(32173)) logger.debug("[Episodes Sync] Kodi episode ratings are up to date.") else: logger.debug("[Episodes Sync] %i show(s) will have episode ratings added in Kodi" % len(kodiShowsUpdate['shows'])) for s in ["%s" % self.__getShowAsString(s, short=True) for s in kodiShowsUpdate['shows']]: logger.debug("[Episodes Sync] Episodes updated: %s" % s) episodes = [] for show in kodiShowsUpdate['shows']: for season in show['seasons']: for episode in season['episodes']: episodes.append({'episodeid': episode['ids']['episodeid'], 'rating': episode['rating']}) # split episode list into chunks of 50 chunksize = 50 chunked_episodes = utilities.chunks([{"jsonrpc": "2.0", "id": i, "method": "VideoLibrary.SetEpisodeDetails", "params": {"episodeid": episodes[i]['episodeid'], "userrating": episodes[i]['rating']}} for i in range(len(episodes))], chunksize) i = 0 x = float(len(episodes)) for chunk in chunked_episodes: if self.sync.IsCanceled(): return i += 1 y = ((i / x) * (toPercent-fromPercent)) + fromPercent self.sync.UpdateProgress(int(y), line1='', line2=kodiUtilities.getString(32174) % ((i) * chunksize if (i) * chunksize < x else x, x)) kodiUtilities.kodiJsonRequest(chunk) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32175) % len(episodes))
def emit(self, record): levels = { logging.CRITICAL: xbmc.LOGFATAL, logging.ERROR: xbmc.LOGERROR, logging.WARNING: xbmc.LOGWARNING, logging.INFO: xbmc.LOGINFO, logging.DEBUG: xbmc.LOGDEBUG, logging.NOTSET: xbmc.LOGNONE, } if getSettingAsBool('debug'): try: xbmc.log(self.format(record), levels[record.levelno]) except UnicodeEncodeError: xbmc.log( self.format(record).encode('utf-8', 'ignore'), levels[record.levelno])
def __syncShowsRatings(self, traktShows, kodiShows, fromPercent, toPercent): if kodiUtilities.getSettingAsBool('trakt_sync_ratings') and traktShows and not self.sync.IsCanceled(): updateKodiTraktShows = copy.deepcopy(traktShows) updateKodiKodiShows = copy.deepcopy(kodiShows) traktShowsToUpdate = self.__compareShows(updateKodiKodiShows, updateKodiTraktShows, rating=True) if len(traktShowsToUpdate['shows']) == 0: self.sync.UpdateProgress(toPercent, line1='', line2=kodiUtilities.getString(32181)) logger.debug("[Episodes Sync] Trakt show ratings are up to date.") else: logger.debug("[Episodes Sync] %i show(s) will have show ratings added on Trakt" % len(traktShowsToUpdate['shows'])) self.sync.UpdateProgress(fromPercent, line1='', line2=kodiUtilities.getString(32182) % len(traktShowsToUpdate['shows'])) self.sync.traktapi.addRating(traktShowsToUpdate) # needs to be restricted, because we can't add a rating to an episode which is not in our Kodi collection kodiShowsUpdate = self.__compareShows(updateKodiTraktShows, updateKodiKodiShows, rating=True, restrict = True) if len(kodiShowsUpdate['shows']) == 0: self.sync.UpdateProgress(toPercent, line1='', line2=kodiUtilities.getString(32176)) logger.debug("[Episodes Sync] Kodi show ratings are up to date.") else: logger.debug("[Episodes Sync] %i show(s) will have show ratings added in Kodi" % len(kodiShowsUpdate['shows'])) shows = [] for show in kodiShowsUpdate['shows']: shows.append({'tvshowid': show['tvshowid'], 'rating': show['rating']}) # split episode list into chunks of 50 chunksize = 50 chunked_episodes = utilities.chunks([{"jsonrpc": "2.0", "id": i, "method": "VideoLibrary.SetTVShowDetails", "params": {"tvshowid": shows[i]['tvshowid'], "userrating": shows[i]['rating']}} for i in range(len(shows))], chunksize) i = 0 x = float(len(shows)) for chunk in chunked_episodes: if self.sync.IsCanceled(): return i += 1 y = ((i / x) * (toPercent-fromPercent)) + fromPercent self.sync.UpdateProgress(int(y), line1='', line2=kodiUtilities.getString(32177) % ((i) * chunksize if (i) * chunksize < x else x, x)) kodiUtilities.kodiJsonRequest(chunk) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32178) % len(shows))
def __syncMovieRatings(self, traktMovies, kodiMovies, fromPercent, toPercent): if kodiUtilities.getSettingAsBool('trakt_sync_ratings') and traktMovies and not self.sync.IsCanceled(): updateKodiTraktMovies = copy.deepcopy(traktMovies) updateKodiKodiMovies = copy.deepcopy(kodiMovies) traktMoviesToUpdate = utilities.compareMovies(updateKodiKodiMovies, updateKodiTraktMovies, rating=True) if len(traktMoviesToUpdate) == 0: self.sync.UpdateProgress(toPercent, line1='', line2=kodiUtilities.getString(32179)) logger.debug("[Movies Sync] Trakt movie ratings are up to date.") else: logger.debug("[Movies Sync] %i movie(s) ratings will be updated on Trakt" % len(traktMoviesToUpdate)) self.sync.UpdateProgress(fromPercent, line1='', line2=kodiUtilities.getString(32180) % len(traktMoviesToUpdate)) moviesRatings = {'movies': traktMoviesToUpdate} self.sync.traktapi.addRating(moviesRatings) kodiMoviesToUpdate = utilities.compareMovies(updateKodiTraktMovies, updateKodiKodiMovies, restrict=True, rating=True) if len(kodiMoviesToUpdate) == 0: self.sync.UpdateProgress(toPercent, line1='', line2=kodiUtilities.getString(32169)) logger.debug("[Movies Sync] Kodi movie ratings are up to date.") else: logger.debug("[Movies Sync] %i movie(s) ratings will be updated in Kodi" % len(kodiMoviesToUpdate)) self.sync.UpdateProgress(fromPercent, line1='', line2=kodiUtilities.getString(32170) % len(kodiMoviesToUpdate)) # split movie list into chunks of 50 chunksize = 50 chunked_movies = utilities.chunks([{"jsonrpc": "2.0", "id": i, "method": "VideoLibrary.SetMovieDetails", "params": {"movieid": kodiMoviesToUpdate[i]['movieid'], "userrating": kodiMoviesToUpdate[i]['rating']}} for i in range(len(kodiMoviesToUpdate))], chunksize) i = 0 x = float(len(kodiMoviesToUpdate)) for chunk in chunked_movies: if self.sync.IsCanceled(): return i += 1 y = ((i / x) * (toPercent-fromPercent)) + fromPercent self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString(32171) % ((i) * chunksize if (i) * chunksize < x else x, x)) kodiUtilities.kodiJsonRequest(chunk) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32172) % len(kodiMoviesToUpdate))
def __deleteMoviesFromTraktCollection(self, traktMovies, kodiMovies, fromPercent, toPercent): if kodiUtilities.getSettingAsBool( 'clean_trakt_movies') and not self.sync.IsCanceled(): removeTraktMovies = copy.deepcopy(traktMovies) removeKodiMovies = copy.deepcopy(kodiMovies) logger.debug("[Movies Sync] Starting to remove.") traktMoviesToRemove = utilities.compareMovies( removeTraktMovies, removeKodiMovies) utilities.sanitizeMovies(traktMoviesToRemove) logger.debug("[Movies Sync] Compared movies, found %s to remove." % len(traktMoviesToRemove)) if len(traktMoviesToRemove) == 0: self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32091)) logger.debug( "[Movies Sync] Trakt.tv movie collection is clean, no movies to remove." ) return titles = ", ".join( ["%s" % (m['title']) for m in traktMoviesToRemove]) logger.debug( "[Movies Sync] %i movie(s) will be removed from Trakt.tv collection." % len(traktMoviesToRemove)) logger.debug("[Movies Sync] Movies removed: %s" % titles) self.sync.UpdateProgress(fromPercent, line2=kodiUtilities.getString(32076) % len(traktMoviesToRemove)) moviesToRemove = {'movies': traktMoviesToRemove} try: self.sync.traktapi.removeFromCollection(moviesToRemove) except Exception as ex: message = utilities.createError(ex) logging.fatal(message) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32092) % len(traktMoviesToRemove))
def __addEpisodesToTraktCollection(self, kodiShows, traktShows, fromPercent, toPercent): if kodiUtilities.getSettingAsBool('add_episodes_to_trakt') and not self.sync.IsCanceled(): addTraktShows = copy.deepcopy(traktShows) addKodiShows = copy.deepcopy(kodiShows) tmpTraktShowsAdd = self.__compareEpisodes(addKodiShows, addTraktShows) traktShowsAdd = copy.deepcopy(tmpTraktShowsAdd) utilities.sanitizeShows(traktShowsAdd) # logger.debug("traktShowsAdd %s" % traktShowsAdd) if len(traktShowsAdd['shows']) == 0: self.sync.UpdateProgress(toPercent, line1=kodiUtilities.getString(32068), line2=kodiUtilities.getString(32104)) logger.debug("[Episodes Sync] Trakt.tv episode collection is up to date.") return logger.debug("[Episodes Sync] %i show(s) have episodes (%d) to be added to your Trakt.tv collection." % (len(traktShowsAdd['shows']), self.__countEpisodes(traktShowsAdd))) for show in traktShowsAdd['shows']: logger.debug("[Episodes Sync] Episodes added: %s" % self.__getShowAsString(show, short=True)) self.sync.UpdateProgress(fromPercent, line1=kodiUtilities.getString(32068), line2=kodiUtilities.getString(32067) % (len(traktShowsAdd['shows']))) # split episode list into chunks of 50 chunksize = 1 chunked_episodes = utilities.chunks(traktShowsAdd['shows'], chunksize) errorcount = 0 i = 0 x = float(len(traktShowsAdd['shows'])) for chunk in chunked_episodes: if self.sync.IsCanceled(): return i += 1 y = ((i / x) * (toPercent-fromPercent)) + fromPercent self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString(32069) % ((i) * chunksize if (i) * chunksize < x else x, x)) request = {'shows': chunk} logger.debug("[traktAddEpisodes] Shows to add %s" % request) try: self.sync.traktapi.addToCollection(request) except Exception as ex: message = utilities.createError(ex) logging.fatal(message) errorcount += 1 logger.debug("[traktAddEpisodes] Finished with %d error(s)" % errorcount) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32105) % self.__countEpisodes(traktShowsAdd))
def __addMoviesToTraktWatched(self, kodiMovies, traktMovies, fromPercent, toPercent): if kodiUtilities.getSettingAsBool('trakt_movie_playcount') and not self.sync.IsCanceled(): updateTraktTraktMovies = copy.deepcopy(traktMovies) updateTraktKodiMovies = copy.deepcopy(kodiMovies) traktMoviesToUpdate = utilities.compareMovies(updateTraktKodiMovies, updateTraktTraktMovies, watched=True) utilities.sanitizeMovies(traktMoviesToUpdate) if len(traktMoviesToUpdate) == 0: self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32086)) logger.debug("[Movies Sync] Trakt.tv movie playcount is up to date") return titles = ", ".join(["%s" % (m['title']) for m in traktMoviesToUpdate]) logger.debug("[Movies Sync] %i movie(s) playcount will be updated on Trakt.tv" % len(traktMoviesToUpdate)) logger.debug("[Movies Sync] Movies updated: %s" % titles) self.sync.UpdateProgress(fromPercent, line2=kodiUtilities.getString(32064) % len(traktMoviesToUpdate)) # Send request to update playcounts on Trakt.tv chunksize = 200 chunked_movies = utilities.chunks([movie for movie in traktMoviesToUpdate], chunksize) errorcount = 0 i = 0 x = float(len(traktMoviesToUpdate)) for chunk in chunked_movies: if self.sync.IsCanceled(): return i += 1 y = ((i / x) * (toPercent-fromPercent)) + fromPercent self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString(32093) % ((i) * chunksize if (i) * chunksize < x else x, x)) params = {'movies': chunk} # logger.debug("moviechunk: %s" % params) try: self.sync.traktapi.addToHistory(params) except Exception as ex: message = utilities.createError(ex) logging.fatal(message) errorcount += 1 logger.debug("[Movies Sync] Movies updated: %d error(s)" % errorcount) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32087) % len(traktMoviesToUpdate))
def __addEpisodesToTraktWatched(self, kodiShows, traktShows, fromPercent, toPercent): if kodiUtilities.getSettingAsBool('trakt_episode_playcount') and not self.sync.IsCanceled(): updateTraktTraktShows = copy.deepcopy(traktShows) updateTraktKodiShows = copy.deepcopy(kodiShows) traktShowsUpdate = self.__compareEpisodes(updateTraktKodiShows, updateTraktTraktShows, watched=True) utilities.sanitizeShows(traktShowsUpdate) # logger.debug("traktShowsUpdate %s" % traktShowsUpdate) if len(traktShowsUpdate['shows']) == 0: self.sync.UpdateProgress(toPercent, line1=kodiUtilities.getString(32071), line2=kodiUtilities.getString(32106)) logger.debug("[Episodes Sync] Trakt.tv episode playcounts are up to date.") return logger.debug("[Episodes Sync] %i show(s) are missing playcounts on Trakt.tv" % len(traktShowsUpdate['shows'])) for show in traktShowsUpdate['shows']: logger.debug("[Episodes Sync] Episodes updated: %s" % self.__getShowAsString(show, short=True)) self.sync.UpdateProgress(fromPercent, line1=kodiUtilities.getString(32071), line2=kodiUtilities.getString(32070) % (len(traktShowsUpdate['shows']))) errorcount = 0 i = 0 x = float(len(traktShowsUpdate['shows'])) for show in traktShowsUpdate['shows']: if self.sync.IsCanceled(): return epCount = self.__countEpisodes([show]) title = show['title'].encode('utf-8', 'ignore') i += 1 y = ((i / x) * (toPercent-fromPercent)) + fromPercent self.sync.UpdateProgress(int(y), line2=title, line3=kodiUtilities.getString(32073) % epCount) s = {'shows': [show]} logger.debug("[traktUpdateEpisodes] Shows to update %s" % s) try: self.sync.traktapi.addToHistory(s) except Exception as ex: message = utilities.createError(ex) logging.fatal(message) errorcount += 1 logger.debug("[traktUpdateEpisodes] Finished with %d error(s)" % errorcount) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32072) % (len(traktShowsUpdate['shows'])), line3=" ")
def __addMoviesToTraktCollection(self, kodiMovies, traktMovies, fromPercent, toPercent): if kodiUtilities.getSettingAsBool( 'add_movies_to_trakt') and not self.sync.IsCanceled(): addTraktMovies = copy.deepcopy(traktMovies) addKodiMovies = copy.deepcopy(kodiMovies) traktMoviesToAdd = utilities.compareMovies(addKodiMovies, addTraktMovies) utilities.sanitizeMovies(traktMoviesToAdd) logger.debug("[Movies Sync] Compared movies, found %s to add." % len(traktMoviesToAdd)) if len(traktMoviesToAdd) == 0: self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32084)) logger.debug( "[Movies Sync] Trakt.tv movie collection is up to date.") return titles = ", ".join(["%s" % (m['title']) for m in traktMoviesToAdd]) logger.debug( "[Movies Sync] %i movie(s) will be added to Trakt.tv collection." % len(traktMoviesToAdd)) logger.debug("[Movies Sync] Movies to add : %s" % titles) self.sync.UpdateProgress(fromPercent, line2=kodiUtilities.getString(32063) % len(traktMoviesToAdd)) moviesToAdd = {'movies': traktMoviesToAdd} # logger.debug("Movies to add: %s" % moviesToAdd) try: self.sync.traktapi.addToCollection(moviesToAdd) except Exception as ex: message = utilities.createError(ex) logging.fatal(message) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32085) % len(traktMoviesToAdd))
def __addEpisodesToKodiWatched(self, traktShows, kodiShows, kodiShowsCollected, fromPercent, toPercent): if kodiUtilities.getSettingAsBool('kodi_episode_playcount') and not self.sync.IsCanceled(): updateKodiTraktShows = copy.deepcopy(traktShows) updateKodiKodiShows = copy.deepcopy(kodiShows) kodiShowsUpdate = self.__compareEpisodes(updateKodiTraktShows, updateKodiKodiShows, watched=True, restrict=True, collected=kodiShowsCollected) if len(kodiShowsUpdate['shows']) == 0: self.sync.UpdateProgress(toPercent, line1=kodiUtilities.getString(32074), line2=kodiUtilities.getString(32107)) logger.debug("[Episodes Sync] Kodi episode playcounts are up to date.") return logger.debug("[Episodes Sync] %i show(s) shows are missing playcounts on Kodi" % len(kodiShowsUpdate['shows'])) for s in ["%s" % self.__getShowAsString(s, short=True) for s in kodiShowsUpdate['shows']]: logger.debug("[Episodes Sync] Episodes updated: %s" % s) # logger.debug("kodiShowsUpdate: %s" % kodiShowsUpdate) episodes = [] for show in kodiShowsUpdate['shows']: for season in show['seasons']: for episode in season['episodes']: episodes.append({'episodeid': episode['ids']['episodeid'], 'playcount': episode['plays'], "lastplayed": utilities.convertUtcToDateTime(episode['last_watched_at'])}) # split episode list into chunks of 50 chunksize = 50 chunked_episodes = utilities.chunks([{"jsonrpc": "2.0", "method": "VideoLibrary.SetEpisodeDetails", "params": episodes[i], "id": i} for i in range(len(episodes))], chunksize) i = 0 x = float(len(episodes)) for chunk in chunked_episodes: if self.sync.IsCanceled(): return i += 1 y = ((i / x) * (toPercent-fromPercent)) + fromPercent self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString(32108) % ((i) * chunksize if (i) * chunksize < x else x, x)) logger.debug("[Episodes Sync] chunk %s" % str(chunk)) result = kodiUtilities.kodiJsonRequest(chunk) logger.debug("[Episodes Sync] result %s" % str(result)) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32109) % len(episodes))
def __addEpisodeProgressToKodi(self, traktShows, kodiShows, fromPercent, toPercent): if kodiUtilities.getSettingAsBool('trakt_episode_playback') and traktShows and not self.sync.IsCanceled(): updateKodiTraktShows = copy.deepcopy(traktShows) updateKodiKodiShows = copy.deepcopy(kodiShows) kodiShowsUpdate = self.__compareEpisodes(updateKodiTraktShows, updateKodiKodiShows, restrict=True, playback=True) if len(kodiShowsUpdate['shows']) == 0: self.sync.UpdateProgress(toPercent, line1=kodiUtilities.getString(1441), line2=kodiUtilities.getString(32129)) logger.debug("[Episodes Sync] Kodi episode playbacks are up to date.") return logger.debug("[Episodes Sync] %i show(s) shows are missing playbacks on Kodi" % len(kodiShowsUpdate['shows'])) for s in ["%s" % self.__getShowAsString(s, short=True) for s in kodiShowsUpdate['shows']]: logger.debug("[Episodes Sync] Episodes updated: %s" % s) episodes = [] for show in kodiShowsUpdate['shows']: for season in show['seasons']: for episode in season['episodes']: episodes.append({'episodeid': episode['ids']['episodeid'], 'progress': episode['progress'], 'runtime': episode['runtime']}) # need to calculate the progress in int from progress in percent from Trakt # split episode list into chunks of 50 chunksize = 50 chunked_episodes = utilities.chunks([{"jsonrpc": "2.0", "id": i, "method": "VideoLibrary.SetEpisodeDetails", "params": {"episodeid":episodes[i]['episodeid'], "resume": {"position": episodes[i]['runtime'] / 100.0 * episodes[i]['progress'], "total": episodes[i]['runtime']}}} for i in range(len(episodes))], chunksize) i = 0 x = float(len(episodes)) for chunk in chunked_episodes: if self.sync.IsCanceled(): return i += 1 y = ((i / x) * (toPercent-fromPercent)) + fromPercent self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString(32130) % ((i) * chunksize if (i) * chunksize < x else x, x)) kodiUtilities.kodiJsonRequest(chunk) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32131) % len(episodes))
def __traktLoadMoviesPlaybackProgress(self, fromPercent, toPercent): if kodiUtilities.getSettingAsBool( 'trakt_movie_playback') and not self.sync.IsCanceled(): self.sync.UpdateProgress(fromPercent, line2=kodiUtilities.getString(32122)) logger.debug( '[Movies Sync] Getting playback progress from Trakt.tv') try: traktProgressMovies = self.sync.traktapi.getMoviePlaybackProgress( ) except Exception: logger.debug( "[Movies Sync] Invalid Trakt.tv playback progress list, possible error getting data from Trakt, aborting Trakt.tv playback update." ) return False i = 0 x = float(len(traktProgressMovies)) moviesProgress = {'movies': []} for movie in traktProgressMovies: i += 1 y = ((i / x) * (toPercent - fromPercent)) + fromPercent self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString(32123) % (i, x)) # will keep the data in python structures - just like the KODI response movie = movie.to_dict() moviesProgress['movies'].append(movie) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32124)) return moviesProgress
def playbackStarted(self, data): logger.debug("playbackStarted(data: %s)" % data) if not data: return self.curVideo = data self.curVideoInfo = None self.videosToRate = [] if not kodiUtilities.getSettingAsBool('scrobble_fallback') and 'id' not in self.curVideo and 'video_ids' not in self.curVideo: logger.debug('Aborting scrobble to avoid fallback: %s' % (self.curVideo)) return if 'type' in self.curVideo: logger.debug("Watching: %s" % self.curVideo['type']) if not xbmc.Player().isPlayingVideo(): logger.debug("Suddenly stopped watching item") return xbmc.sleep(1000) # Wait for possible silent seek (caused by resuming) try: self.watchedTime = xbmc.Player().getTime() self.videoDuration = xbmc.Player().getTotalTime() except Exception as e: logger.debug("Suddenly stopped watching item: %s" % e.message) self.curVideo = None return if self.videoDuration == 0: if utilities.isMovie(self.curVideo['type']): self.videoDuration = 90 elif utilities.isEpisode(self.curVideo['type']): self.videoDuration = 30 else: self.videoDuration = 1 self.playlistLength = len(xbmc.PlayList(xbmc.PLAYLIST_VIDEO)) self.playlistIndex = xbmc.PlayList(xbmc.PLAYLIST_VIDEO).getposition() if self.playlistLength == 0: logger.debug("Warning: Cant find playlist length, assuming that this item is by itself") self.playlistLength = 1 self.isMultiPartEpisode = False if utilities.isMovie(self.curVideo['type']): if 'id' in self.curVideo: self.curVideoInfo = kodiUtilities.kodiRpcToTraktMediaObject('movie', kodiUtilities.getMovieDetailsFromKodi(self.curVideo['id'], ['imdbnumber', 'title', 'year', 'file', 'lastplayed', 'playcount'])) elif 'video_ids' in self.curVideo: self.curVideoInfo = {'ids': self.curVideo['video_ids']} elif 'title' in self.curVideo and 'year' in self.curVideo: self.curVideoInfo = {'title': self.curVideo['title'], 'year': self.curVideo['year']} elif utilities.isEpisode(self.curVideo['type']): if 'id' in self.curVideo: episodeDetailsKodi = kodiUtilities.getEpisodeDetailsFromKodi(self.curVideo['id'], ['showtitle', 'season', 'episode', 'tvshowid', 'uniqueid', 'file', 'playcount']) tvdb = episodeDetailsKodi['imdbnumber'] title, year = utilities.regex_year(episodeDetailsKodi['showtitle']) if not year: self.traktShowSummary = {'title': episodeDetailsKodi['showtitle'], 'year': episodeDetailsKodi['year']} else: self.traktShowSummary = {'title': title, 'year': year} if tvdb: self.traktShowSummary['ids'] = {'tvdb': tvdb} self.curVideoInfo = kodiUtilities.kodiRpcToTraktMediaObject('episode', episodeDetailsKodi) if not self.curVideoInfo: # getEpisodeDetailsFromKodi was empty logger.debug("Episode details from Kodi was empty, ID (%d) seems invalid, aborting further scrobbling of this episode." % self.curVideo['id']) self.curVideo = None self.isPlaying = False self.watchedTime = 0 return elif 'video_ids' in self.curVideo and 'season' in self.curVideo and 'episode' in self.curVideo: self.curVideoInfo = {'season': self.curVideo['season'], 'number': self.curVideo['episode']} self.traktShowSummary = {'ids': self.curVideo['video_ids']} elif 'title' in self.curVideo and 'season' in self.curVideo and 'episode' in self.curVideo: self.curVideoInfo = {'title': self.curVideo['title'], 'season': self.curVideo['season'], 'number': self.curVideo['episode']} title, year = utilities.regex_year(self.curVideo['showtitle']) if not year: self.traktShowSummary = {'title': self.curVideo['showtitle']} else: self.traktShowSummary = {'title': title, 'year': year} if 'year' in self.curVideo: self.traktShowSummary['year'] = self.curVideo['year'] if 'multi_episode_count' in self.curVideo and self.curVideo['multi_episode_count'] > 1: self.isMultiPartEpisode = True self.isPlaying = True self.isPaused = False result = {} if kodiUtilities.getSettingAsBool('scrobble_movie') or kodiUtilities.getSettingAsBool('scrobble_episode'): result = self.__scrobble('start') elif kodiUtilities.getSettingAsBool('rate_movie') and utilities.isMovie(self.curVideo['type']) and 'ids' in self.curVideoInfo: best_id = utilities.best_id(self.curVideoInfo['ids']) result = {'movie': self.traktapi.getMovieSummary(best_id).to_dict()} elif kodiUtilities.getSettingAsBool('rate_episode') and utilities.isEpisode(self.curVideo['type']) and 'ids' in self.traktShowSummary: best_id = utilities.best_id(self.traktShowSummary['ids']) result = {'show': self.traktapi.getShowSummary(best_id).to_dict(), 'episode': self.traktapi.getEpisodeSummary(best_id, self.curVideoInfo['season'], self.curVideoInfo['number']).to_dict()} result['episode']['season'] = self.curVideoInfo['season'] if 'id' in self.curVideo: if utilities.isMovie(self.curVideo['type']): result['movie']['movieid'] = self.curVideo['id'] elif utilities.isEpisode(self.curVideo['type']): result['episode']['episodeid'] = self.curVideo['id'] self.__preFetchUserRatings(result)
def __syncPlaybackCheck(self, media_type): if media_type == 'movies': return getSettingAsBool('trakt_movie_playback') else: return getSettingAsBool('trakt_episode_playback')
def __syncCollectionCheck(self, media_type): if media_type == 'movies': return getSettingAsBool('add_movies_to_trakt') or getSettingAsBool('clean_trakt_movies') else: return getSettingAsBool('add_episodes_to_trakt') or getSettingAsBool('clean_trakt_episodes')
def __syncRatingsCheck(self): return getSettingAsBool('trakt_sync_ratings')
def playbackStarted(self, data): logger.debug("playbackStarted(data: %s)" % data) if not data: return self.curVideo = data self.curVideoInfo = None self.videosToRate = [] if not kodiUtilities.getSettingAsBool( 'scrobble_fallback' ) and 'id' not in self.curVideo and 'video_ids' not in self.curVideo: logger.debug('Aborting scrobble to avoid fallback: %s' % (self.curVideo)) return if 'type' in self.curVideo: logger.debug("Watching: %s" % self.curVideo['type']) if not xbmc.Player().isPlayingVideo(): logger.debug("Suddenly stopped watching item") return xbmc.sleep( 1000) # Wait for possible silent seek (caused by resuming) try: self.watchedTime = xbmc.Player().getTime() self.videoDuration = xbmc.Player().getTotalTime() except Exception as e: logger.debug("Suddenly stopped watching item: %s" % e.message) self.curVideo = None return if self.videoDuration == 0: if utilities.isMovie(self.curVideo['type']): self.videoDuration = 90 elif utilities.isEpisode(self.curVideo['type']): self.videoDuration = 30 else: self.videoDuration = 1 self.playlistLength = len(xbmc.PlayList(xbmc.PLAYLIST_VIDEO)) self.playlistIndex = xbmc.PlayList( xbmc.PLAYLIST_VIDEO).getposition() if self.playlistLength == 0: logger.debug( "Warning: Cant find playlist length, assuming that this item is by itself" ) self.playlistLength = 1 self.isMultiPartEpisode = False if utilities.isMovie(self.curVideo['type']): if 'id' in self.curVideo: self.curVideoInfo = kodiUtilities.kodiRpcToTraktMediaObject( 'movie', kodiUtilities.getMovieDetailsFromKodi( self.curVideo['id'], [ 'imdbnumber', 'title', 'year', 'file', 'lastplayed', 'playcount' ])) elif 'video_ids' in self.curVideo: self.curVideoInfo = {'ids': self.curVideo['video_ids']} elif 'title' in self.curVideo and 'year' in self.curVideo: self.curVideoInfo = { 'title': self.curVideo['title'], 'year': self.curVideo['year'] } else: logger.debug("Couldn't set curVideoInfo for movie type") logger.debug("Movie type, curVideoInfo: %s" % self.curVideoInfo) elif utilities.isEpisode(self.curVideo['type']): if 'id' in self.curVideo: episodeDetailsKodi = kodiUtilities.getEpisodeDetailsFromKodi( self.curVideo['id'], [ 'showtitle', 'season', 'episode', 'tvshowid', 'uniqueid', 'file', 'playcount' ]) tvdb = episodeDetailsKodi['imdbnumber'] title, year = utilities.regex_year( episodeDetailsKodi['showtitle']) if not year: self.traktShowSummary = { 'title': episodeDetailsKodi['showtitle'], 'year': episodeDetailsKodi['year'] } else: self.traktShowSummary = {'title': title, 'year': year} if tvdb: self.traktShowSummary['ids'] = {'tvdb': tvdb} self.curVideoInfo = kodiUtilities.kodiRpcToTraktMediaObject( 'episode', episodeDetailsKodi) if not self.curVideoInfo: # getEpisodeDetailsFromKodi was empty logger.debug( "Episode details from Kodi was empty, ID (%d) seems invalid, aborting further scrobbling of this episode." % self.curVideo['id']) self.curVideo = None self.isPlaying = False self.watchedTime = 0 return elif 'video_ids' in self.curVideo and 'season' in self.curVideo and 'episode' in self.curVideo: self.curVideoInfo = { 'season': self.curVideo['season'], 'number': self.curVideo['episode'] } self.traktShowSummary = {'ids': self.curVideo['video_ids']} elif 'title' in self.curVideo and 'season' in self.curVideo and 'episode' in self.curVideo: self.curVideoInfo = { 'title': self.curVideo['title'], 'season': self.curVideo['season'], 'number': self.curVideo['episode'] } title, year = utilities.regex_year( self.curVideo['showtitle']) if not year: self.traktShowSummary = { 'title': self.curVideo['showtitle'] } else: self.traktShowSummary = {'title': title, 'year': year} if 'year' in self.curVideo: self.traktShowSummary['year'] = self.curVideo['year'] else: logger.debug( "Couldn't set curVideoInfo/traktShowSummary for episode type" ) if 'multi_episode_count' in self.curVideo and self.curVideo[ 'multi_episode_count'] > 1: self.isMultiPartEpisode = True logger.debug("Episode type, curVideoInfo: %s" % self.curVideoInfo) logger.debug("Episode type, traktShowSummary: %s" % self.traktShowSummary) self.isPlaying = True self.isPaused = False result = {} if kodiUtilities.getSettingAsBool( 'scrobble_movie') or kodiUtilities.getSettingAsBool( 'scrobble_episode'): result = self.__scrobble('start') elif kodiUtilities.getSettingAsBool( 'rate_movie') and utilities.isMovie( self.curVideo['type']) and 'ids' in self.curVideoInfo: best_id = utilities.best_id(self.curVideoInfo['ids']) result = { 'movie': self.traktapi.getMovieSummary(best_id).to_dict() } elif kodiUtilities.getSettingAsBool( 'rate_episode') and utilities.isEpisode( self.curVideo['type'] ) and 'ids' in self.traktShowSummary: best_id = utilities.best_id(self.traktShowSummary['ids']) result = { 'show': self.traktapi.getShowSummary(best_id).to_dict(), 'episode': self.traktapi.getEpisodeSummary( best_id, self.curVideoInfo['season'], self.curVideoInfo['number']).to_dict() } result['episode']['season'] = self.curVideoInfo['season'] if 'id' in self.curVideo: if utilities.isMovie(self.curVideo['type']): result['movie']['movieid'] = self.curVideo['id'] elif utilities.isEpisode(self.curVideo['type']): result['episode']['episodeid'] = self.curVideo['id'] self.__preFetchUserRatings(result)
def rateMedia(media_type, itemsToRate, unrate=False, rating=None): """Launches the rating dialog""" for summary_info in itemsToRate: if not utilities.isValidMediaType(media_type): logger.debug("Not a valid media type") return elif 'user' not in summary_info: logger.debug("No user data") return s = utilities.getFormattedItemName(media_type, summary_info) logger.debug("Summary Info %s" % summary_info) if unrate: rating = None if summary_info['user']['ratings']['rating'] > 0: rating = 0 if not rating is None: logger.debug("'%s' is being unrated." % s) __rateOnTrakt(rating, media_type, summary_info, unrate=True) else: logger.debug("'%s' has not been rated, so not unrating." % s) return rerate = kodiUtilities.getSettingAsBool('rate_rerate') if rating is not None: if summary_info['user']['ratings']['rating'] == 0: logger.debug("Rating for '%s' is being set to '%d' manually." % (s, rating)) __rateOnTrakt(rating, media_type, summary_info) else: if rerate: if not summary_info['user']['ratings']['rating'] == rating: logger.debug( "Rating for '%s' is being set to '%d' manually." % (s, rating)) __rateOnTrakt(rating, media_type, summary_info) else: kodiUtilities.notification( kodiUtilities.getString(32043), s) logger.debug("'%s' already has a rating of '%d'." % (s, rating)) else: kodiUtilities.notification(kodiUtilities.getString(32041), s) logger.debug("'%s' is already rated." % s) return if summary_info['user']['ratings'] and summary_info['user']['ratings'][ 'rating']: if not rerate: logger.debug("'%s' has already been rated." % s) kodiUtilities.notification(kodiUtilities.getString(32041), s) return else: logger.debug("'%s' is being re-rated." % s) xbmc.executebuiltin('Dialog.Close(all, true)') gui = RatingDialog("script-trakt-RatingDialog.xml", __addon__.getAddonInfo('path'), media_type=media_type, media=summary_info, rerate=rerate) gui.doModal() if gui.rating: rating = gui.rating if rerate: rating = gui.rating if summary_info['user']['ratings'] and summary_info['user'][ 'ratings']['rating'] > 0 and rating == summary_info[ 'user']['ratings']['rating']: rating = 0 if rating == 0 or rating == "unrate": __rateOnTrakt(rating, gui.media_type, gui.media, unrate=True) else: __rateOnTrakt(rating, gui.media_type, gui.media) else: logger.debug("Rating dialog was closed with no rating.") del gui #Reset rating and unrate for multi part episodes unrate = False rating = None
def __syncMovieRatings(self, traktMovies, kodiMovies, fromPercent, toPercent): if kodiUtilities.getSettingAsBool( 'trakt_sync_ratings' ) and traktMovies and not self.sync.IsCanceled(): updateKodiTraktMovies = copy.deepcopy(traktMovies) updateKodiKodiMovies = copy.deepcopy(kodiMovies) traktMoviesToUpdate = utilities.compareMovies( updateKodiKodiMovies, updateKodiTraktMovies, rating=True) if len(traktMoviesToUpdate) == 0: self.sync.UpdateProgress(toPercent, line1='', line2=kodiUtilities.getString(32179)) logger.debug( "[Movies Sync] Trakt movie ratings are up to date.") else: logger.debug( "[Movies Sync] %i movie(s) ratings will be updated on Trakt" % len(traktMoviesToUpdate)) self.sync.UpdateProgress(fromPercent, line1='', line2=kodiUtilities.getString(32180) % len(traktMoviesToUpdate)) moviesRatings = {'movies': traktMoviesToUpdate} self.sync.traktapi.addRating(moviesRatings) kodiMoviesToUpdate = utilities.compareMovies(updateKodiTraktMovies, updateKodiKodiMovies, restrict=True, rating=True) if len(kodiMoviesToUpdate) == 0: self.sync.UpdateProgress(toPercent, line1='', line2=kodiUtilities.getString(32169)) logger.debug( "[Movies Sync] Kodi movie ratings are up to date.") else: logger.debug( "[Movies Sync] %i movie(s) ratings will be updated in Kodi" % len(kodiMoviesToUpdate)) self.sync.UpdateProgress(fromPercent, line1='', line2=kodiUtilities.getString(32170) % len(kodiMoviesToUpdate)) # split movie list into chunks of 50 chunksize = 50 chunked_movies = utilities.chunks([{ "jsonrpc": "2.0", "id": i, "method": "VideoLibrary.SetMovieDetails", "params": { "movieid": kodiMoviesToUpdate[i]['movieid'], "userrating": kodiMoviesToUpdate[i]['rating'] } } for i in range(len(kodiMoviesToUpdate))], chunksize) i = 0 x = float(len(kodiMoviesToUpdate)) for chunk in chunked_movies: if self.sync.IsCanceled(): return i += 1 y = ((i / x) * (toPercent - fromPercent)) + fromPercent self.sync.UpdateProgress( int(y), line2=kodiUtilities.getString(32171) % ((i) * chunksize if (i) * chunksize < x else x, x)) kodiUtilities.kodiJsonRequest(chunk) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32172) % len(kodiMoviesToUpdate))
def __addMovieProgressToKodi(self, traktMovies, kodiMovies, fromPercent, toPercent): if kodiUtilities.getSettingAsBool( 'trakt_movie_playback' ) and traktMovies and not self.sync.IsCanceled(): updateKodiTraktMovies = copy.deepcopy(traktMovies) updateKodiKodiMovies = copy.deepcopy(kodiMovies) kodiMoviesToUpdate = utilities.compareMovies( updateKodiTraktMovies['movies'], updateKodiKodiMovies, restrict=True, playback=True) if len(kodiMoviesToUpdate) == 0: self.sync.UpdateProgress(toPercent, line1='', line2=kodiUtilities.getString(32125)) logger.debug( "[Movies Sync] Kodi movie playbacks are up to date.") return logger.debug( "[Movies Sync] %i movie(s) playbacks will be updated in Kodi" % len(kodiMoviesToUpdate)) self.sync.UpdateProgress(fromPercent, line1='', line2=kodiUtilities.getString(32126) % len(kodiMoviesToUpdate)) # need to calculate the progress in int from progress in percent from Trakt # split movie list into chunks of 50 chunksize = 50 chunked_movies = utilities.chunks([{ "jsonrpc": "2.0", "id": i, "method": "VideoLibrary.SetMovieDetails", "params": { "movieid": kodiMoviesToUpdate[i]['movieid'], "resume": { "position": kodiMoviesToUpdate[i]['runtime'] / 100.0 * kodiMoviesToUpdate[i]['progress'], "total": kodiMoviesToUpdate[i]['runtime'] } } } for i in range(len(kodiMoviesToUpdate))], chunksize) i = 0 x = float(len(kodiMoviesToUpdate)) for chunk in chunked_movies: if self.sync.IsCanceled(): return i += 1 y = ((i / x) * (toPercent - fromPercent)) + fromPercent self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString(32127) % ((i) * chunksize if (i) * chunksize < x else x, x)) kodiUtilities.kodiJsonRequest(chunk) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32128) % len(kodiMoviesToUpdate))
def __addMoviesToKodiWatched(self, traktMovies, kodiMovies, fromPercent, toPercent): if kodiUtilities.getSettingAsBool( 'kodi_movie_playcount') and not self.sync.IsCanceled(): updateKodiTraktMovies = copy.deepcopy(traktMovies) updateKodiKodiMovies = copy.deepcopy(kodiMovies) kodiMoviesToUpdate = utilities.compareMovies(updateKodiTraktMovies, updateKodiKodiMovies, watched=True, restrict=True) if len(kodiMoviesToUpdate) == 0: self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32088)) logger.debug( "[Movies Sync] Kodi movie playcount is up to date.") return titles = ", ".join( ["%s" % (m['title']) for m in kodiMoviesToUpdate]) logger.debug( "[Movies Sync] %i movie(s) playcount will be updated in Kodi" % len(kodiMoviesToUpdate)) logger.debug("[Movies Sync] Movies to add: %s" % titles) self.sync.UpdateProgress(fromPercent, line2=kodiUtilities.getString(32065) % len(kodiMoviesToUpdate)) # split movie list into chunks of 50 chunksize = 50 chunked_movies = utilities.chunks([{ "jsonrpc": "2.0", "method": "VideoLibrary.SetMovieDetails", "params": { "movieid": kodiMoviesToUpdate[i]['movieid'], "playcount": kodiMoviesToUpdate[i]['plays'], "lastplayed": utilities.convertUtcToDateTime( kodiMoviesToUpdate[i]['last_watched_at']) }, "id": i } for i in range(len(kodiMoviesToUpdate))], chunksize) i = 0 x = float(len(kodiMoviesToUpdate)) for chunk in chunked_movies: if self.sync.IsCanceled(): return i += 1 y = ((i / x) * (toPercent - fromPercent)) + fromPercent self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString(32089) % ((i) * chunksize if (i) * chunksize < x else x, x)) kodiUtilities.kodiJsonRequest(chunk) self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32090) % len(kodiMoviesToUpdate))
def __syncWatchedCheck(self, media_type): if media_type == 'movies': return getSettingAsBool('trakt_movie_playcount') or getSettingAsBool('kodi_movie_playcount') else: return getSettingAsBool('trakt_episode_playcount') or getSettingAsBool('kodi_episode_playcount')
def __scrobble(self, status): if not self.curVideoInfo: return logger.debug("scrobble()") scrobbleMovieOption = kodiUtilities.getSettingAsBool('scrobble_movie') scrobbleEpisodeOption = kodiUtilities.getSettingAsBool( 'scrobble_episode') watchedPercent = self.__calculateWatchedPercent() if utilities.isMovie(self.curVideo['type']) and scrobbleMovieOption: response = self.traktapi.scrobbleMovie(self.curVideoInfo, watchedPercent, status) if response is not None: self.__scrobbleNotification(response) logger.debug("Scrobble response: %s" % str(response)) return response else: logger.debug("Failed to scrobble movie: %s | %s | %s" % (self.curVideoInfo, watchedPercent, status)) elif utilities.isEpisode( self.curVideo['type']) and scrobbleEpisodeOption: if self.isMultiPartEpisode: logger.debug("Multi-part episode, scrobbling part %d of %d." % (self.curMPEpisode + 1, self.curVideo['multi_episode_count'])) adjustedDuration = int(self.videoDuration / self.curVideo['multi_episode_count']) watchedPercent = ((self.watchedTime - (adjustedDuration * self.curMPEpisode)) / adjustedDuration) * 100 logger.debug("scrobble sending show object: %s" % str(self.traktShowSummary)) logger.debug("scrobble sending episode object: %s" % str(self.curVideoInfo)) response = self.traktapi.scrobbleEpisode(self.traktShowSummary, self.curVideoInfo, watchedPercent, status) if (kodiUtilities.getSettingAsBool('scrobble_secondary_title')): logger.debug( '[traktPlayer] Setting is enabled to try secondary show title, if necessary.' ) # If there is an empty response, the reason might be that the title we have isn't the actual show title, # but rather an alternative title. To handle this case, call the Trakt search function. if response is None: logger.debug("Searching for show title: %s" % self.traktShowSummary['title']) # This text query API is basically the same as searching on the website. Works with alternative # titles, unlike the scrobble function. newResp = self.traktapi.getTextQuery( self.traktShowSummary['title'], "show", None) if not newResp: logger.debug( "Empty Response from getTextQuery, giving up") else: logger.debug("Got Response from getTextQuery: %s" % str(newResp)) # We got something back. Have to assume the first show found is the right one; if there's more than # one, there's no way to know which to use. Pull the primary title from the response (and the year, # just because it's there). showObj = { 'title': newResp[0].title, 'year': newResp[0].year } logger.debug( "scrobble sending getTextQuery first show object: %s" % str(showObj)) # Now we can attempt the scrobble again, using the primary title this time. response = self.traktapi.scrobbleEpisode( showObj, self.curVideoInfo, watchedPercent, status) if response is not None: self.__scrobbleNotification(response) logger.debug("Scrobble response: %s" % str(response)) return response else: logger.debug("Failed to scrobble episode: %s | %s | %s | %s" % (self.traktShowSummary, self.curVideoInfo, watchedPercent, status))
def onPlayBackStarted(self): xbmc.sleep(1000) self.type = None self.id = None # take the user start scrobble offset into account scrobbleStartOffset = kodiUtilities.getSettingAsInt( 'scrobble_start_offset') * 60 if scrobbleStartOffset > 0: waitFor = 10 waitedFor = 0 # check each 10 seconds if we can abort or proceed while not xbmc.abortRequested and scrobbleStartOffset > waitedFor: waitedFor += waitFor time.sleep(waitFor) if not self.isPlayingVideo(): logger.debug( '[traktPlayer] Playback stopped before reaching the scrobble offset' ) return # only do anything if we're playing a video if self.isPlayingVideo(): # get item data from json rpc logger.debug( "[traktPlayer] onPlayBackStarted() - Doing Player.GetItem kodiJsonRequest" ) result = kodiUtilities.kodiJsonRequest({ 'jsonrpc': '2.0', 'method': 'Player.GetItem', 'params': { 'playerid': 1 }, 'id': 1 }) if result: logger.debug("[traktPlayer] onPlayBackStarted() - %s" % result) # check for exclusion _filename = None try: _filename = self.getPlayingFile() except: logger.debug( "[traktPlayer] onPlayBackStarted() - Exception trying to get playing filename, player suddenly stopped." ) return if kodiUtilities.checkExclusion(_filename): logger.debug( "[traktPlayer] onPlayBackStarted() - '%s' is in exclusion settings, ignoring." % _filename) return self.type = result['item']['type'] data = {'action': 'started'} if (kodiUtilities.getSettingAsBool('scrobble_mythtv_pvr')): logger.debug( '[traktPlayer] Setting is enabled to try scrobbling mythtv pvr recording, if necessary.' ) # check type of item if 'id' not in result['item']: # do a deeper check to see if we have enough data to perform scrobbles logger.debug( "[traktPlayer] onPlayBackStarted() - Started playing a non-library file, checking available data." ) season = xbmc.getInfoLabel('VideoPlayer.Season') episode = xbmc.getInfoLabel('VideoPlayer.Episode') showtitle = xbmc.getInfoLabel('VideoPlayer.TVShowTitle') year = xbmc.getInfoLabel('VideoPlayer.Year') video_ids = xbmcgui.Window(10000).getProperty( 'script.trakt.ids') if video_ids: data['video_ids'] = json.loads(video_ids) logger.debug( "[traktPlayer] info - ids: %s, showtitle: %s, Year: %s, Season: %s, Episode: %s" % (video_ids, showtitle, year, season, episode)) if season and episode and (showtitle or video_ids): # we have season, episode and either a show title or video_ids, can scrobble this as an episode self.type = 'episode' data['type'] = 'episode' data['season'] = int(season) data['episode'] = int(episode) data['showtitle'] = showtitle data['title'] = xbmc.getInfoLabel('VideoPlayer.Title') if year.isdigit(): data['year'] = int(year) logger.debug( "[traktPlayer] onPlayBackStarted() - Playing a non-library 'episode' - %s - S%02dE%02d - %s." % (data['showtitle'], data['season'], data['episode'], data['title'])) elif (year or video_ids) and not season and not showtitle: # we have a year or video_id and no season/showtitle info, enough for a movie self.type = 'movie' data['type'] = 'movie' if year.isdigit(): data['year'] = int(year) data['title'] = xbmc.getInfoLabel('VideoPlayer.Title') logger.debug( "[traktPlayer] onPlayBackStarted() - Playing a non-library 'movie' - %s (%s)." % (data['title'], data.get('year', 'NaN'))) elif showtitle: title, season, episode = utilities.regex_tvshow( showtitle) data['type'] = 'episode' data['season'] = season data['episode'] = episode data['title'] = data['showtitle'] = title logger.debug( "[traktPlayer] onPlayBackStarted() - Title: %s, showtitle: %s, season: %d, episode: %d" % (title, showtitle, season, episode)) else: logger.debug( "[traktPlayer] onPlayBackStarted() - Non-library file, not enough data for scrobbling, skipping." ) return elif self.type == 'episode' or self.type == 'movie': # get library id self.id = result['item']['id'] data['id'] = self.id data['type'] = self.type if self.type == 'episode': logger.debug( "[traktPlayer] onPlayBackStarted() - Doing multi-part episode check." ) result = kodiUtilities.kodiJsonRequest({ 'jsonrpc': '2.0', 'method': 'VideoLibrary.GetEpisodeDetails', 'params': { 'episodeid': self.id, 'properties': ['tvshowid', 'season', 'episode', 'file'] }, 'id': 1 }) if result: logger.debug( "[traktPlayer] onPlayBackStarted() - %s" % result) tvshowid = int( result['episodedetails']['tvshowid']) season = int(result['episodedetails']['season']) currentfile = result['episodedetails']['file'] result = kodiUtilities.kodiJsonRequest({ 'jsonrpc': '2.0', 'method': 'VideoLibrary.GetEpisodes', 'params': { 'tvshowid': tvshowid, 'season': season, 'properties': ['episode', 'file'], 'sort': { 'method': 'episode' } }, 'id': 1 }) if result: logger.debug( "[traktPlayer] onPlayBackStarted() - %s" % result) # make sure episodes array exists in results if 'episodes' in result: multi = [] for i in range(result['limits']['start'], result['limits']['total']): if currentfile == result['episodes'][ i]['file']: multi.append(result['episodes'][i] ['episodeid']) if len(multi) > 1: data['multi_episode_data'] = multi data['multi_episode_count'] = len( multi) logger.debug( "[traktPlayer] onPlayBackStarted() - This episode is part of a multi-part episode." ) else: logger.debug( "[traktPlayer] onPlayBackStarted() - This is a single episode." ) elif (kodiUtilities.getSettingAsBool('scrobble_mythtv_pvr') and self.type == 'unknown' and result['item']['label']): # If we have label/id but no show type, then this might be a PVR recording. # DEBUG INFO: This code is useful when trying to figure out what info is available. Many of the fields # that you'd expect (TVShowTitle, episode, season, etc) are always blank. In Kodi v15, we got the show # and episode name in the VideoPlayer label. In v16, that's gone, but the Player.Filename infolabel # is populated with several interesting things. If these things change in future versions, uncommenting # this code will hopefully provide some useful info in the debug log. #logger.debug("[traktPlayer] onPlayBackStarted() - TEMP Checking all videoplayer infolabels.") #for il in ['VideoPlayer.Time','VideoPlayer.TimeRemaining','VideoPlayer.TimeSpeed','VideoPlayer.Duration','VideoPlayer.Title','VideoPlayer.TVShowTitle','VideoPlayer.Season','VideoPlayer.Episode','VideoPlayer.Genre','VideoPlayer.Director','VideoPlayer.Country','VideoPlayer.Year','VideoPlayer.Rating','VideoPlayer.UserRating','VideoPlayer.Votes','VideoPlayer.RatingAndVotes','VideoPlayer.mpaa','VideoPlayer.IMDBNumber','VideoPlayer.EpisodeName','VideoPlayer.PlaylistPosition','VideoPlayer.PlaylistLength','VideoPlayer.Cast','VideoPlayer.CastAndRole','VideoPlayer.Album','VideoPlayer.Artist','VideoPlayer.Studio','VideoPlayer.Writer','VideoPlayer.Tagline','VideoPlayer.PlotOutline','VideoPlayer.Plot','VideoPlayer.LastPlayed','VideoPlayer.PlayCount','VideoPlayer.VideoCodec','VideoPlayer.VideoResolution','VideoPlayer.VideoAspect','VideoPlayer.AudioCodec','VideoPlayer.AudioChannels','VideoPlayer.AudioLanguage','VideoPlayer.SubtitlesLanguage','VideoPlayer.StereoscopicMode','VideoPlayer.EndTime','VideoPlayer.NextTitle','VideoPlayer.NextGenre','VideoPlayer.NextPlot','VideoPlayer.NextPlotOutline','VideoPlayer.NextStartTime','VideoPlayer.NextEndTime','VideoPlayer.NextDuration','VideoPlayer.ChannelName','VideoPlayer.ChannelNumber','VideoPlayer.SubChannelNumber','VideoPlayer.ChannelNumberLabel','VideoPlayer.ChannelGroup','VideoPlayer.ParentalRating','Player.FinishTime','Player.FinishTime(format)','Player.Chapter','Player.ChapterCount','Player.Time','Player.Time(format)','Player.TimeRemaining','Player.TimeRemaining(format)','Player.Duration','Player.Duration(format)','Player.SeekTime','Player.SeekOffset','Player.SeekOffset(format)','Player.SeekStepSize','Player.ProgressCache','Player.Folderpath','Player.Filenameandpath','Player.StartTime','Player.StartTime(format)','Player.Title','Player.Filename']: # logger.debug("[traktPlayer] TEMP %s : %s" % (il, xbmc.getInfoLabel(il))) #for k,v in result.iteritems(): # logger.debug("[traktPlayer] onPlayBackStarted() - result - %s : %s" % (k,v)) #for k,v in result['item'].iteritems(): # logger.debug("[traktPlayer] onPlayBackStarted() - result.item - %s : %s" % (k,v)) # As of Kodi v16 with the MythTV PVR addon, the only way I could find to get the TV show and episode # info is from the Player.Filename infolabel. It shows up like this: # ShowName [sXXeYY ](year) EpisodeName, channel, PVRFileName # The season and episode info may or may not be present. For example: # Elementary s04e10 (2016) Alma Matters, TV (WWMT-HD), 20160129_030000.pvr # DC's Legends of Tomorrow (2016) Pilot, Part 2, TV (CW W MI), 20160129_010000.pvr foundLabel = xbmc.getInfoLabel('Player.Filename') logger.debug( "[traktPlayer] onPlayBackStarted() - Found unknown video type with label: %s. Might be a PVR episode, searching Trakt for it." % foundLabel) splitLabel = foundLabel.rsplit(", ", 2) logger.debug( "[traktPlayer] onPlayBackStarted() - Post-split of label: %s " % splitLabel) if len(splitLabel) != 3: logger.debug( "[traktPlayer] onPlayBackStarted() - Label doesn't have the ShowName sXXeYY (year) EpisodeName, channel, PVRFileName format that was expected. Giving up." ) return foundShowAndEpInfo = splitLabel[0] logger.debug( "[traktPlayer] onPlayBackStarted() - show plus episode info: %s" % foundShowAndEpInfo) splitShowAndEpInfo = re.split( ' (s\d\de\d\d)? ?\((\d\d\d\d)\) ', foundShowAndEpInfo, 1) logger.debug( "[traktPlayer] onPlayBackStarted() - Post-split of show plus episode info: %s " % splitShowAndEpInfo) if len(splitShowAndEpInfo) != 4: logger.debug( "[traktPlayer] onPlayBackStarted() - Show plus episode info doesn't have the ShowName sXXeYY (year) EpisodeName format that was expected. Giving up." ) return foundShowName = splitShowAndEpInfo[0] logger.debug( "[traktPlayer] onPlayBackStarted() - using show name: %s" % foundShowName) foundEpisodeName = splitShowAndEpInfo[3] logger.debug( "[traktPlayer] onPlayBackStarted() - using episode name: %s" % foundEpisodeName) foundEpisodeYear = splitShowAndEpInfo[2] logger.debug( "[traktPlayer] onPlayBackStarted() - using episode year: %s" % foundEpisodeYear) epYear = None try: epYear = int(foundEpisodeYear) except ValueError: epYear = None logger.debug( "[traktPlayer] onPlayBackStarted() - verified episode year: %d" % epYear) # All right, now we have the show name, episode name, and (maybe) episode year. All good, but useless for # scrobbling since Trakt only understands IDs, not names. data['video_ids'] = None data['season'] = None data['episode'] = None data['episodeTitle'] = None # First thing to try, a text query to the Trakt DB looking for this episode. Note # that we can't search for show and episode together, because the Trakt function gets confused and returns nothing. newResp = globals.traktapi.getTextQuery( foundEpisodeName, "episode", epYear) if not newResp: logger.debug( "[traktPlayer] onPlayBackStarted() - Empty Response from getTextQuery, giving up" ) else: logger.debug( "[traktPlayer] onPlayBackStarted() - Got Response from getTextQuery: %s" % str(newResp)) # We got something back. See if one of the returned values is for the show we're looking for. Often it's # not, but since there's no way to tell the search which show we want, this is all we can do. rightResp = None for thisResp in newResp: compareShowName = thisResp.show.title logger.debug( "[traktPlayer] onPlayBackStarted() - comparing show name: %s" % compareShowName) if thisResp.show.title == foundShowName: logger.debug( "[traktPlayer] onPlayBackStarted() - found the right show, using this response" ) rightResp = thisResp break if rightResp is None: logger.debug( "[traktPlayer] onPlayBackStarted() - Failed to find matching episode/show via text search." ) else: # OK, now we have a episode object to work with. self.type = 'episode' data['type'] = 'episode' # You'd think we could just use the episode key that Trakt just returned to us, but the scrobbler # function (see scrobber.py) only understands the show key plus season/episode values. showKeys = {} for eachKey in rightResp.show.keys: showKeys[eachKey[0]] = eachKey[1] data['video_ids'] = showKeys # For some reason, the Trakt search call returns the season and episode as an array in the pk field. # You'd think individual episode and season fields would be better, but whatever. data['season'] = rightResp.pk[0] data['episode'] = rightResp.pk[1] # At this point if we haven't found the episode data yet, the episode-title-text-search method # didn't work. if (not data['season']): # This text query API is basically the same as searching on the website. Works with alternative # titles, unlike the scrobble function. Though we can't use the episode year since that would only # match the show if we're dealing with season 1. logger.debug( "[traktPlayer] onPlayBackStarted() - Searching for show title via getTextQuery: %s" % foundShowName) newResp = globals.traktapi.getTextQuery( foundShowName, "show", None) if not newResp: logger.debug( "[traktPlayer] onPlayBackStarted() - Empty Show Response from getTextQuery, falling back on episode text query" ) else: logger.debug( "[traktPlayer] onPlayBackStarted() - Got Show Response from getTextQuery: %s" % str(newResp)) # We got something back. Have to assume the first show found is the right one; if there's more than # one, there's no way to know which to use. Pull the ids from the show data, and store 'em for scrobbling. showKeys = {} for eachKey in newResp[0].keys: showKeys[eachKey[0]] = eachKey[1] data['video_ids'] = showKeys # Now to find the episode. There's no search function to look for an episode within a show, but # we can get all the episodes and look for the title. while (not data['season']): logger.debug( "[traktPlayer] onPlayBackStarted() - Querying for all seasons/episodes of this show" ) epQueryResp = globals.traktapi.getShowWithAllEpisodesList( data['video_ids']['trakt']) if not epQueryResp: # Nothing returned. Giving up. logger.debug( "[traktPlayer] onPlayBackStarted() - No response received" ) break else: # Got the list back. Go through each season. logger.debug( "[traktPlayer] onPlayBackStarted() - Got response with seasons: %s" % str(epQueryResp)) for eachSeason in epQueryResp: # For each season, check each episode. logger.debug( "[traktPlayer] onPlayBackStarted() - Processing season: %s" % str(eachSeason)) for eachEpisodeNumber in eachSeason.episodes: thisEpTitle = None # Get the title. The try block is here in case the title doesn't exist for some entries. try: thisEpTitle = eachSeason.episodes[ eachEpisodeNumber].title except: thisEpTitle = None logger.debug( "[traktPlayer] onPlayBackStarted() - Checking episode number %d with title %s" % (eachEpisodeNumber, thisEpTitle)) if (foundEpisodeName == thisEpTitle ): # Found it! Save the data. The scrobbler wants season and episode number. Which for some # reason is stored as a pair in the first item in the keys array. data[ 'season'] = eachSeason.episodes[ eachEpisodeNumber].keys[ 0][0] data[ 'episode'] = eachSeason.episodes[ eachEpisodeNumber].keys[ 0][1] # Title too, just for the heck of it. Though it's not actually used. data[ 'episodeTitle'] = thisEpTitle break # If we already found our data, no need to go through the rest of the seasons. if (data['season']): break # Now we've done all we can. if (data['season']): # OK, that's everything. Data should be all set for scrobbling. logger.debug( "[traktPlayer] onPlayBackStarted() - Playing a non-library 'episode' : show trakt key %s, season: %d, episode: %d" % (data['video_ids'], data['season'], data['episode'])) else: # Still no data? Too bad, have to give up. logger.debug( "[traktPlayer] onPlayBackStarted() - Did our best, but couldn't get info for this show and episode. Skipping." ) return else: logger.debug( "[traktPlayer] onPlayBackStarted() - Video type '%s' unrecognized, skipping." % self.type) return pl = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) plSize = len(pl) if plSize > 1: pos = pl.getposition() if not self.plIndex is None: logger.debug( "[traktPlayer] onPlayBackStarted() - User manually skipped to next (or previous) video, forcing playback ended event." ) self.onPlayBackEnded() self.plIndex = pos logger.debug( "[traktPlayer] onPlayBackStarted() - Playlist contains %d item(s), and is currently on item %d" % (plSize, (pos + 1))) self._playing = True # send dispatch self.action(data)
def onPlayBackStarted(self): xbmc.sleep(1000) self.type = None self.id = None # take the user start scrobble offset into account scrobbleStartOffset = kodiUtilities.getSettingAsInt('scrobble_start_offset')*60 if scrobbleStartOffset > 0: waitFor = 10 waitedFor = 0 # check each 10 seconds if we can abort or proceed while not xbmc.abortRequested and scrobbleStartOffset > waitedFor: waitedFor += waitFor time.sleep(waitFor) if not self.isPlayingVideo(): logger.debug('[traktPlayer] Playback stopped before reaching the scrobble offset') return # only do anything if we're playing a video if self.isPlayingVideo(): # get item data from json rpc logger.debug("[traktPlayer] onPlayBackStarted() - Doing Player.GetItem kodiJsonRequest") result = kodiUtilities.kodiJsonRequest({'jsonrpc': '2.0', 'method': 'Player.GetItem', 'params': {'playerid': 1}, 'id': 1}) if result: logger.debug("[traktPlayer] onPlayBackStarted() - %s" % result) # check for exclusion _filename = None try: _filename = self.getPlayingFile() except: logger.debug("[traktPlayer] onPlayBackStarted() - Exception trying to get playing filename, player suddenly stopped.") return if kodiUtilities.checkExclusion(_filename): logger.debug("[traktPlayer] onPlayBackStarted() - '%s' is in exclusion settings, ignoring." % _filename) return self.type = result['item']['type'] data = {'action': 'started'} if (kodiUtilities.getSettingAsBool('scrobble_mythtv_pvr')): logger.debug('[traktPlayer] Setting is enabled to try scrobbling mythtv pvr recording, if necessary.') # check type of item if 'id' not in result['item']: # do a deeper check to see if we have enough data to perform scrobbles logger.debug("[traktPlayer] onPlayBackStarted() - Started playing a non-library file, checking available data.") season = xbmc.getInfoLabel('VideoPlayer.Season') episode = xbmc.getInfoLabel('VideoPlayer.Episode') showtitle = xbmc.getInfoLabel('VideoPlayer.TVShowTitle') year = xbmc.getInfoLabel('VideoPlayer.Year') video_ids = xbmcgui.Window(10000).getProperty('script.trakt.ids') if video_ids: data['video_ids'] = json.loads(video_ids) logger.debug("[traktPlayer] info - ids: %s, showtitle: %s, Year: %s, Season: %s, Episode: %s" % (video_ids, showtitle, year, season, episode)) if season and episode and (showtitle or video_ids): # we have season, episode and either a show title or video_ids, can scrobble this as an episode self.type = 'episode' data['type'] = 'episode' data['season'] = int(season) data['episode'] = int(episode) data['showtitle'] = showtitle data['title'] = xbmc.getInfoLabel('VideoPlayer.Title') if year.isdigit(): data['year'] = int(year) logger.debug("[traktPlayer] onPlayBackStarted() - Playing a non-library 'episode' - %s - S%02dE%02d - %s." % (data['showtitle'], data['season'], data['episode'], data['title'])) elif (year or video_ids) and not season and not showtitle: # we have a year or video_id and no season/showtitle info, enough for a movie self.type = 'movie' data['type'] = 'movie' if year.isdigit(): data['year'] = int(year) data['title'] = xbmc.getInfoLabel('VideoPlayer.Title') logger.debug("[traktPlayer] onPlayBackStarted() - Playing a non-library 'movie' - %s (%s)." % (data['title'], data.get('year', 'NaN'))) elif showtitle: title, season, episode = utilities.regex_tvshow(showtitle) data['type'] = 'episode' data['season'] = season data['episode'] = episode data['title'] = data['showtitle'] = title logger.debug("[traktPlayer] onPlayBackStarted() - Title: %s, showtitle: %s, season: %d, episode: %d" % (title, showtitle, season, episode)) else: logger.debug("[traktPlayer] onPlayBackStarted() - Non-library file, not enough data for scrobbling, skipping.") return elif self.type == 'episode' or self.type == 'movie': # get library id self.id = result['item']['id'] data['id'] = self.id data['type'] = self.type if self.type == 'episode': logger.debug("[traktPlayer] onPlayBackStarted() - Doing multi-part episode check.") result = kodiUtilities.kodiJsonRequest({'jsonrpc': '2.0', 'method': 'VideoLibrary.GetEpisodeDetails', 'params': {'episodeid': self.id, 'properties': ['tvshowid', 'season', 'episode', 'file']}, 'id': 1}) if result: logger.debug("[traktPlayer] onPlayBackStarted() - %s" % result) tvshowid = int(result['episodedetails']['tvshowid']) season = int(result['episodedetails']['season']) currentfile = result['episodedetails']['file'] result = kodiUtilities.kodiJsonRequest({'jsonrpc': '2.0', 'method': 'VideoLibrary.GetEpisodes', 'params': {'tvshowid': tvshowid, 'season': season, 'properties': ['episode', 'file'], 'sort': {'method': 'episode'}}, 'id': 1}) if result: logger.debug("[traktPlayer] onPlayBackStarted() - %s" % result) # make sure episodes array exists in results if 'episodes' in result: multi = [] for i in range(result['limits']['start'], result['limits']['total']): if currentfile == result['episodes'][i]['file']: multi.append(result['episodes'][i]['episodeid']) if len(multi) > 1: data['multi_episode_data'] = multi data['multi_episode_count'] = len(multi) logger.debug("[traktPlayer] onPlayBackStarted() - This episode is part of a multi-part episode.") else: logger.debug("[traktPlayer] onPlayBackStarted() - This is a single episode.") elif (kodiUtilities.getSettingAsBool('scrobble_mythtv_pvr') and self.type == 'unknown' and result['item']['label']): # If we have label/id but no show type, then this might be a PVR recording. # DEBUG INFO: This code is useful when trying to figure out what info is available. Many of the fields # that you'd expect (TVShowTitle, episode, season, etc) are always blank. In Kodi v15, we got the show # and episode name in the VideoPlayer label. In v16, that's gone, but the Player.Filename infolabel # is populated with several interesting things. If these things change in future versions, uncommenting # this code will hopefully provide some useful info in the debug log. #logger.debug("[traktPlayer] onPlayBackStarted() - TEMP Checking all videoplayer infolabels.") #for il in ['VideoPlayer.Time','VideoPlayer.TimeRemaining','VideoPlayer.TimeSpeed','VideoPlayer.Duration','VideoPlayer.Title','VideoPlayer.TVShowTitle','VideoPlayer.Season','VideoPlayer.Episode','VideoPlayer.Genre','VideoPlayer.Director','VideoPlayer.Country','VideoPlayer.Year','VideoPlayer.Rating','VideoPlayer.UserRating','VideoPlayer.Votes','VideoPlayer.RatingAndVotes','VideoPlayer.mpaa','VideoPlayer.IMDBNumber','VideoPlayer.EpisodeName','VideoPlayer.PlaylistPosition','VideoPlayer.PlaylistLength','VideoPlayer.Cast','VideoPlayer.CastAndRole','VideoPlayer.Album','VideoPlayer.Artist','VideoPlayer.Studio','VideoPlayer.Writer','VideoPlayer.Tagline','VideoPlayer.PlotOutline','VideoPlayer.Plot','VideoPlayer.LastPlayed','VideoPlayer.PlayCount','VideoPlayer.VideoCodec','VideoPlayer.VideoResolution','VideoPlayer.VideoAspect','VideoPlayer.AudioCodec','VideoPlayer.AudioChannels','VideoPlayer.AudioLanguage','VideoPlayer.SubtitlesLanguage','VideoPlayer.StereoscopicMode','VideoPlayer.EndTime','VideoPlayer.NextTitle','VideoPlayer.NextGenre','VideoPlayer.NextPlot','VideoPlayer.NextPlotOutline','VideoPlayer.NextStartTime','VideoPlayer.NextEndTime','VideoPlayer.NextDuration','VideoPlayer.ChannelName','VideoPlayer.ChannelNumber','VideoPlayer.SubChannelNumber','VideoPlayer.ChannelNumberLabel','VideoPlayer.ChannelGroup','VideoPlayer.ParentalRating','Player.FinishTime','Player.FinishTime(format)','Player.Chapter','Player.ChapterCount','Player.Time','Player.Time(format)','Player.TimeRemaining','Player.TimeRemaining(format)','Player.Duration','Player.Duration(format)','Player.SeekTime','Player.SeekOffset','Player.SeekOffset(format)','Player.SeekStepSize','Player.ProgressCache','Player.Folderpath','Player.Filenameandpath','Player.StartTime','Player.StartTime(format)','Player.Title','Player.Filename']: # logger.debug("[traktPlayer] TEMP %s : %s" % (il, xbmc.getInfoLabel(il))) #for k,v in result.iteritems(): # logger.debug("[traktPlayer] onPlayBackStarted() - result - %s : %s" % (k,v)) #for k,v in result['item'].iteritems(): # logger.debug("[traktPlayer] onPlayBackStarted() - result.item - %s : %s" % (k,v)) # As of Kodi v16 with the MythTV PVR addon, the only way I could find to get the TV show and episode # info is from the Player.Filename infolabel. It shows up like this: # ShowName [sXXeYY ](year) EpisodeName, channel, PVRFileName # The season and episode info may or may not be present. For example: # Elementary s04e10 (2016) Alma Matters, TV (WWMT-HD), 20160129_030000.pvr # DC's Legends of Tomorrow (2016) Pilot, Part 2, TV (CW W MI), 20160129_010000.pvr foundLabel = xbmc.getInfoLabel('Player.Filename') logger.debug("[traktPlayer] onPlayBackStarted() - Found unknown video type with label: %s. Might be a PVR episode, searching Trakt for it." % foundLabel) splitLabel = foundLabel.rsplit(", ", 2) logger.debug("[traktPlayer] onPlayBackStarted() - Post-split of label: %s " % splitLabel) if len(splitLabel) != 3: logger.debug("[traktPlayer] onPlayBackStarted() - Label doesn't have the ShowName sXXeYY (year) EpisodeName, channel, PVRFileName format that was expected. Giving up.") return foundShowAndEpInfo = splitLabel[0] logger.debug("[traktPlayer] onPlayBackStarted() - show plus episode info: %s" % foundShowAndEpInfo) splitShowAndEpInfo = re.split(' (s\d\de\d\d)? ?\((\d\d\d\d)\) ',foundShowAndEpInfo, 1) logger.debug("[traktPlayer] onPlayBackStarted() - Post-split of show plus episode info: %s " % splitShowAndEpInfo) if len(splitShowAndEpInfo) != 4: logger.debug("[traktPlayer] onPlayBackStarted() - Show plus episode info doesn't have the ShowName sXXeYY (year) EpisodeName format that was expected. Giving up.") return foundShowName = splitShowAndEpInfo[0] logger.debug("[traktPlayer] onPlayBackStarted() - using show name: %s" % foundShowName) foundEpisodeName = splitShowAndEpInfo[3] logger.debug("[traktPlayer] onPlayBackStarted() - using episode name: %s" % foundEpisodeName) foundEpisodeYear = splitShowAndEpInfo[2] logger.debug("[traktPlayer] onPlayBackStarted() - using episode year: %s" % foundEpisodeYear) epYear = None try: epYear = int(foundEpisodeYear) except ValueError: epYear = None logger.debug("[traktPlayer] onPlayBackStarted() - verified episode year: %d" % epYear) # All right, now we have the show name, episode name, and (maybe) episode year. All good, but useless for # scrobbling since Trakt only understands IDs, not names. data['video_ids'] = None data['season'] = None data['episode'] = None data['episodeTitle'] = None # First thing to try, a text query to the Trakt DB looking for this episode. Note # that we can't search for show and episode together, because the Trakt function gets confused and returns nothing. newResp = globals.traktapi.getTextQuery(foundEpisodeName, "episode", epYear) if not newResp: logger.debug("[traktPlayer] onPlayBackStarted() - Empty Response from getTextQuery, giving up") else: logger.debug("[traktPlayer] onPlayBackStarted() - Got Response from getTextQuery: %s" % str(newResp)) # We got something back. See if one of the returned values is for the show we're looking for. Often it's # not, but since there's no way to tell the search which show we want, this is all we can do. rightResp = None for thisResp in newResp: compareShowName = thisResp.show.title logger.debug("[traktPlayer] onPlayBackStarted() - comparing show name: %s" % compareShowName) if thisResp.show.title == foundShowName: logger.debug("[traktPlayer] onPlayBackStarted() - found the right show, using this response") rightResp = thisResp break if rightResp is None: logger.debug("[traktPlayer] onPlayBackStarted() - Failed to find matching episode/show via text search.") else: # OK, now we have a episode object to work with. self.type = 'episode' data['type'] = 'episode' # You'd think we could just use the episode key that Trakt just returned to us, but the scrobbler # function (see scrobber.py) only understands the show key plus season/episode values. showKeys = { } for eachKey in rightResp.show.keys: showKeys[eachKey[0]] = eachKey[1] data['video_ids'] = showKeys # For some reason, the Trakt search call returns the season and episode as an array in the pk field. # You'd think individual episode and season fields would be better, but whatever. data['season'] = rightResp.pk[0]; data['episode'] = rightResp.pk[1]; # At this point if we haven't found the episode data yet, the episode-title-text-search method # didn't work. if (not data['season']): # This text query API is basically the same as searching on the website. Works with alternative # titles, unlike the scrobble function. Though we can't use the episode year since that would only # match the show if we're dealing with season 1. logger.debug("[traktPlayer] onPlayBackStarted() - Searching for show title via getTextQuery: %s" % foundShowName) newResp = globals.traktapi.getTextQuery(foundShowName, "show", None) if not newResp: logger.debug("[traktPlayer] onPlayBackStarted() - Empty Show Response from getTextQuery, falling back on episode text query") else: logger.debug("[traktPlayer] onPlayBackStarted() - Got Show Response from getTextQuery: %s" % str(newResp)) # We got something back. Have to assume the first show found is the right one; if there's more than # one, there's no way to know which to use. Pull the ids from the show data, and store 'em for scrobbling. showKeys = { } for eachKey in newResp[0].keys: showKeys[eachKey[0]] = eachKey[1] data['video_ids'] = showKeys # Now to find the episode. There's no search function to look for an episode within a show, but # we can get all the episodes and look for the title. while (not data['season']): logger.debug("[traktPlayer] onPlayBackStarted() - Querying for all seasons/episodes of this show") epQueryResp = globals.traktapi.getShowWithAllEpisodesList(data['video_ids']['trakt']) if not epQueryResp: # Nothing returned. Giving up. logger.debug("[traktPlayer] onPlayBackStarted() - No response received") break; else: # Got the list back. Go through each season. logger.debug("[traktPlayer] onPlayBackStarted() - Got response with seasons: %s" % str(epQueryResp)) for eachSeason in epQueryResp: # For each season, check each episode. logger.debug("[traktPlayer] onPlayBackStarted() - Processing season: %s" % str(eachSeason)) for eachEpisodeNumber in eachSeason.episodes: thisEpTitle = None # Get the title. The try block is here in case the title doesn't exist for some entries. try: thisEpTitle = eachSeason.episodes[eachEpisodeNumber].title except: thisEpTitle = None logger.debug("[traktPlayer] onPlayBackStarted() - Checking episode number %d with title %s" % (eachEpisodeNumber, thisEpTitle)) if (foundEpisodeName == thisEpTitle): # Found it! Save the data. The scrobbler wants season and episode number. Which for some # reason is stored as a pair in the first item in the keys array. data['season'] = eachSeason.episodes[eachEpisodeNumber].keys[0][0] data['episode'] = eachSeason.episodes[eachEpisodeNumber].keys[0][1] # Title too, just for the heck of it. Though it's not actually used. data['episodeTitle'] = thisEpTitle break # If we already found our data, no need to go through the rest of the seasons. if (data['season']): break; # Now we've done all we can. if (data['season']): # OK, that's everything. Data should be all set for scrobbling. logger.debug("[traktPlayer] onPlayBackStarted() - Playing a non-library 'episode' : show trakt key %s, season: %d, episode: %d" % (data['video_ids'], data['season'], data['episode'])) else: # Still no data? Too bad, have to give up. logger.debug("[traktPlayer] onPlayBackStarted() - Did our best, but couldn't get info for this show and episode. Skipping.") return; else: logger.debug("[traktPlayer] onPlayBackStarted() - Video type '%s' unrecognized, skipping." % self.type) return pl = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) plSize = len(pl) if plSize > 1: pos = pl.getposition() if not self.plIndex is None: logger.debug("[traktPlayer] onPlayBackStarted() - User manually skipped to next (or previous) video, forcing playback ended event.") self.onPlayBackEnded() self.plIndex = pos logger.debug("[traktPlayer] onPlayBackStarted() - Playlist contains %d item(s), and is currently on item %d" % (plSize, (pos + 1))) self._playing = True # send dispatch self.action(data)
def rateMedia(media_type, itemsToRate, unrate=False, rating=None): """Launches the rating dialog""" for summary_info in itemsToRate: if not utilities.isValidMediaType(media_type): logger.debug("Not a valid media type") return elif 'user' not in summary_info: logger.debug("No user data") return s = utilities.getFormattedItemName(media_type, summary_info) logger.debug("Summary Info %s" % summary_info) if unrate: rating = None if summary_info['user']['ratings']['rating'] > 0: rating = 0 if not rating is None: logger.debug("'%s' is being unrated." % s) __rateOnTrakt(rating, media_type, summary_info, unrate=True) else: logger.debug("'%s' has not been rated, so not unrating." % s) return rerate = kodiUtilities.getSettingAsBool('rate_rerate') if rating is not None: if summary_info['user']['ratings']['rating'] == 0: logger.debug("Rating for '%s' is being set to '%d' manually." % (s, rating)) __rateOnTrakt(rating, media_type, summary_info) else: if rerate: if not summary_info['user']['ratings']['rating'] == rating: logger.debug("Rating for '%s' is being set to '%d' manually." % (s, rating)) __rateOnTrakt(rating, media_type, summary_info) else: kodiUtilities.notification(kodiUtilities.getString(32043), s) logger.debug("'%s' already has a rating of '%d'." % (s, rating)) else: kodiUtilities.notification(kodiUtilities.getString(32041), s) logger.debug("'%s' is already rated." % s) return if summary_info['user']['ratings'] and summary_info['user']['ratings']['rating']: if not rerate: logger.debug("'%s' has already been rated." % s) kodiUtilities.notification(kodiUtilities.getString(32041), s) return else: logger.debug("'%s' is being re-rated." % s) xbmc.executebuiltin('Dialog.Close(all, true)') gui = RatingDialog( "script-trakt-RatingDialog.xml", __addon__.getAddonInfo('path'), media_type=media_type, media=summary_info, rerate=rerate ) gui.doModal() if gui.rating: rating = gui.rating if rerate: rating = gui.rating if summary_info['user']['ratings'] and summary_info['user']['ratings']['rating'] > 0 and rating == summary_info['user']['ratings']['rating']: rating = 0 if rating == 0 or rating == "unrate": __rateOnTrakt(rating, gui.media_type, gui.media, unrate=True) else: __rateOnTrakt(rating, gui.media_type, gui.media) else: logger.debug("Rating dialog was closed with no rating.") del gui #Reset rating and unrate for multi part episodes unrate=False rating=None