def Main(): args = __getArguments() data = {} if args['action'] == 'sync': data = {'action': 'manualSync', 'silent': False} if 'silent' in args: data['silent'] = (args['silent'].lower() == 'true') data['library'] = "all" if 'library' in args and args['library'] in ['episodes', 'movies']: data['library'] = args['library'] elif args['action'] in ['rate', 'unrate']: data = {'action': args['action']} media_type = None if 'media_type' in args and 'dbid' in args: media_type = args['media_type'] try: data['dbid'] = int(args['dbid']) except ValueError: utils.Debug( "Manual %s triggered for library item, but DBID is invalid." % args['action']) return elif 'media_type' in args and 'remoteid' in args: media_type = args['media_type'] data['remoteid'] = args['remoteid'] if 'season' in args: if not 'episode' in args: utils.Debug( "Manual %s triggered for non-library episode, but missing episode number." % args['action']) return try: data['season'] = int(args['season']) data['episode'] = int(args['episode']) except ValueError: utils.Debug( "Error parsing season or episode for manual %s" % args['action']) return else: media_type = __getMediaType() if not utils.isValidMediaType(media_type): utils.Debug("Error, not in video library.") return data['dbid'] = int(xbmc.getInfoLabel('ListItem.DBID')) if media_type is None: utils.Debug( "Manual %s triggered on an unsupported content container." % args['action']) elif utils.isValidMediaType(media_type): data['media_type'] = media_type if 'dbid' in data: utils.Debug("Manual %s of library '%s' with an ID of '%s'." % (args['action'], media_type, data['dbid'])) if utils.isMovie(media_type): result = utils.getMovieDetailsFromKodi( data['dbid'], ['imdbnumber', 'title', 'year']) if not result: utils.Debug( "No data was returned from Kodi, aborting manual %s." % args['action']) return data['imdbnumber'] = result['imdbnumber'] elif utils.isShow(media_type): result = utils.getShowDetailsFromKodi( data['dbid'], ['imdbnumber', 'tag']) if not result: utils.Debug( "No data was returned from Kodi, aborting manual %s." % args['action']) return data['imdbnumber'] = result['imdbnumber'] data['tag'] = result['tag'] elif utils.isEpisode(media_type): result = utils.getEpisodeDetailsFromKodi( data['dbid'], ['showtitle', 'season', 'episode', 'imdbnumber']) if not result: utils.Debug( "No data was returned from Kodi, aborting manual %s." % args['action']) return data['imdbnumber'] = result['imdbnumber'] data['season'] = result['season'] data['episode'] = result['episode'] else: if 'season' in data: utils.Debug( "Manual %s of non-library '%s' S%02dE%02d, with an ID of '%s'." % (args['action'], media_type, data['season'], data['episode'], data['remoteid'])) data['imdbnumber'] = data['remoteid'] else: utils.Debug( "Manual %s of non-library '%s' with an ID of '%s'." % (args['action'], media_type, data['remoteid'])) data['imdbnumber'] = data['remoteid'] if args['action'] == 'rate' and 'rating' in args: if args['rating'] in [ '1', '2', '3', '4', '5', '6', '7', '8', '9', '10' ]: data['rating'] = int(args['rating']) data = {'action': 'manualRating', 'ratingData': data} else: utils.Debug("Manual %s of '%s' is unsupported." % (args['action'], media_type)) elif args['action'] == 'togglewatched': media_type = __getMediaType() if media_type in ['movie', 'show', 'season', 'episode']: data = {'media_type': media_type} if utils.isMovie(media_type): dbid = int(xbmc.getInfoLabel('ListItem.DBID')) result = utils.getMovieDetailsFromKodi( dbid, ['imdbnumber', 'title', 'year', 'playcount']) if result: if result['playcount'] == 0: data['id'] = result['imdbnumber'] else: utils.Debug("Movie alread marked as watched in Kodi.") else: utils.Debug("Error getting movie details from Kodi.") return elif utils.isEpisode(media_type): dbid = int(xbmc.getInfoLabel('ListItem.DBID')) result = utils.getEpisodeDetailsFromKodi( dbid, [ 'showtitle', 'season', 'episode', 'tvshowid', 'playcount' ]) if result: if result['playcount'] == 0: data['id'] = result['tvdb_id'] data['season'] = result['season'] data['episode'] = result['episode'] else: utils.Debug( "Episode already marked as watched in Kodi.") else: utils.Debug("Error getting episode details from Kodi.") return elif utils.isSeason(media_type): showID = None showTitle = xbmc.getInfoLabel('ListItem.TVShowTitle') result = utils.kodiJsonRequest({ 'jsonrpc': '2.0', 'method': 'VideoLibrary.GetTVShows', 'params': { 'properties': ['title', 'imdbnumber', 'year'] }, 'id': 0 }) if result and 'tvshows' in result: for show in result['tvshows']: if show['title'] == showTitle: showID = show['tvshowid'] data['id'] = show['imdbnumber'] break else: utils.Debug("Error getting TV shows from Kodi.") return season = xbmc.getInfoLabel('ListItem.Season') if season == "": season = 0 else: season = int(season) result = utils.kodiJsonRequest({ 'jsonrpc': '2.0', 'method': 'VideoLibrary.GetEpisodes', 'params': { 'tvshowid': showID, 'season': season, 'properties': ['season', 'episode', 'playcount'] }, 'id': 0 }) if result and 'episodes' in result: episodes = [] for episode in result['episodes']: if episode['playcount'] == 0: episodes.append(episode['episode']) if len(episodes) == 0: utils.Debug( "'%s - Season %d' is already marked as watched." % (showTitle, season)) return data['season'] = season data['episodes'] = episodes else: utils.Debug( "Error getting episodes from '%s' for Season %d" % (showTitle, season)) return elif utils.isShow(media_type): dbid = int(xbmc.getInfoLabel('ListItem.DBID')) result = utils.getShowDetailsFromKodi(dbid, ['year', 'imdbnumber']) if not result: utils.Debug("Error getting show details from Kodi.") return showTitle = result['label'] data['id'] = result['imdbnumber'] result = utils.kodiJsonRequest({ 'jsonrpc': '2.0', 'method': 'VideoLibrary.GetEpisodes', 'params': { 'tvshowid': dbid, 'properties': ['season', 'episode', 'playcount'] }, 'id': 0 }) if result and 'episodes' in result: i = 0 s = {} for e in result['episodes']: season = str(e['season']) if not season in s: s[season] = [] if e['playcount'] == 0: s[season].append(e['episode']) i += 1 if i == 0: utils.Debug("'%s' is already marked as watched." % showTitle) return data['seasons'] = dict( (k, v) for k, v in s.iteritems() if v) else: utils.Debug( "Error getting episode details for '%s' from Kodi." % showTitle) return if len(data) > 1: utils.Debug( "Marking '%s' with the following data '%s' as watched on trakt.tv" % (media_type, str(data))) data['action'] = 'markWatched' # execute toggle watched action xbmc.executebuiltin("Action(ToggleWatched)") q = sqliteQueue.SqliteQueue() if 'action' in data: utils.Debug("Queuing for dispatch: %s" % data) q.append(data)
class traktService: scrobbler = None updateTagsThread = None syncThread = None dispatchQueue = sqliteQueue.SqliteQueue() def __init__(self): threading.Thread.name = 'trakt' def _dispatchQueue(self, data): logger.debug("Queuing for dispatch: %s" % data) self.dispatchQueue.append(data) def _dispatch(self, data): 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 == 'databaseUpdated': if utilities.getSettingAsBool('sync_on_update'): logger.debug("Performing sync after library update.") self.doSync() elif action == 'databaseCleaned': if utilities.getSettingAsBool('sync_on_update') and (utilities.getSettingAsBool('clean_trakt_movies') or utilities.getSettingAsBool('clean_trakt_episodes')): logger.debug("Performing sync after library clean.") self.doSync() elif action == 'settingsChanged': logger.debug("Settings changed, reloading.") globals.traktapi.updateSettings() elif action == 'markWatched': del data['action'] self.doMarkWatched(data) elif action == 'manualRating': ratingData = data['ratingData'] self.doManualRating(ratingData) 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': utilities.showSettings() elif action == 'scanStarted': pass else: logger.debug("Unknown dispatch action, '%s'." % action) def run(self): startup_delay = utilities.getSettingAsInt('startup_delay') if startup_delay: logger.debug("Delaying startup by %d seconds." % startup_delay) xbmc.sleep(startup_delay * 1000) logger.debug("Service thread starting.") # purge queue before doing anything self.dispatchQueue.purge() # setup event driven classes self.Player = traktPlayer(action = self._dispatchQueue) self.Monitor = traktMonitor(action = self._dispatchQueue) # init traktapi class globals.traktapi = traktAPI() # init sync thread self.syncThread = syncThread() # init scrobbler class self.scrobbler = Scrobbler(globals.traktapi) # start loop for events while not xbmc.abortRequested: while len(self.dispatchQueue) and (not xbmc.abortRequested): data = self.dispatchQueue.get() logger.debug("Queued dispatch: %s" % data) self._dispatch(data) if xbmc.Player().isPlayingVideo(): self.scrobbler.update() xbmc.sleep(500) # we are shutting down logger.debug("Beginning shut down.") # delete player/monitor del self.Player del self.Monitor # check if sync thread is running, if so, join it. if self.syncThread.isAlive(): self.syncThread.join() def doManualRating(self, data): action = data['action'] media_type = data['media_type'] summaryInfo = None if not utilities.isValidMediaType(media_type): logger.debug("doManualRating(): Invalid media type '%s' passed for manual %s." % (media_type, action)) return if not data['action'] in ['rate', 'unrate']: logger.debug("doManualRating(): Unknown action passed.") return if 'dbid' in data: logger.debug("Getting data for manual %s of library '%s' with ID of '%s'" % (action, media_type, data['dbid'])) elif 'remoteitd' in data: if 'season' in data: logger.debug("Getting data for manual %s of non-library '%s' S%02dE%02d, with ID of '%s'." % (action, media_type, data['season'], data['episode'], data['remoteid'])) else: logger.debug("Getting data for manual %s of non-library '%s' with ID of '%s'" % (action, media_type, data['remoteid'])) if utilities.isEpisode(media_type): summaryInfo = globals.traktapi.getEpisodeSummary(data['trakt'], data['season'], data['episode']) elif utilities.isShow(media_type): summaryInfo = globals.traktapi.getShowSummary(data['imdbnumber']) elif utilities.isMovie(media_type): summaryInfo = globals.traktapi.getMovieSummary(data['imdbnumber']) if not summaryInfo is None: if utilities.isMovie(media_type) or utilities.isShow(media_type): summaryInfo['xbmc_id'] = data['dbid'] if action == 'rate': if not 'rating' in data: rateMedia(media_type, summaryInfo) else: rateMedia(media_type, summaryInfo, rating=data['rating']) else: logger.debug("doManualRating(): Summary info was empty, possible problem retrieving data from trakt.tv") def doMarkWatched(self, data): media_type = data['media_type'] markedNotification = utilities.getSettingAsBool('show_marked_notification') if utilities.isMovie(media_type): summaryInfo = globals.traktapi.getMovieSummary(data['id']) if summaryInfo: if not summaryInfo['watched']: s = utilities.getFormattedItemName(media_type, summaryInfo) logger.debug("doMarkWatched(): '%s' is not watched on trakt, marking it as watched." % s) movie = {'imdb_id': data['id'], 'title': summaryInfo['title'], 'year': summaryInfo['year'], 'plays': 1, 'last_played': int(time())} params = {'movies': [movie]} logger.debug("doMarkWatched(): %s" % str(params)) result = globals.traktapi.updateSeenMovie(params) if result: if markedNotification: utilities.notification(utilities.getString(32113), s) else: utilities.notification(utilities.getString(32114), s) elif utilities.isEpisode(media_type): summaryInfo = globals.traktapi.getEpisodeSummary(data['id'], data['season'], data['episode']) if summaryInfo: if not summaryInfo['episode']['watched']: s = utilities.getFormattedItemName(media_type, summaryInfo) logger.debug("doMarkWathced(): '%s' is not watched on trakt, marking it as watched." % s) params = {'imdb_id': summaryInfo['ids']['imdb_id'], 'tvdb_id': summaryInfo['ids']['tvdb_id'], 'title': summaryInfo['title'], 'year': summaryInfo['year'], 'episodes': [{'season': data['season'], 'episode': data['episode']}]} logger.debug("doMarkWatched(): %s" % str(params)) result = globals.traktapi.updateSeenEpisode(params) if result: if markedNotification: utilities.notification(utilities.getString(32113), s) else: utilities.notification(utilities.getString(32114), s) elif utilities.isSeason(media_type): showInfo = globals.traktapi.getShowSummary(data['id']) if not showInfo: return summaryInfo = globals.traktapi.getSeasonInfo(data['id'], data['season']) if summaryInfo: showInfo['season'] = data['season'] s = utilities.getFormattedItemName(media_type, showInfo) params = {'imdb_id': summaryInfo['ids']['imdb'], 'tvdb_id': summaryInfo['ids']['tvdb'], 'title': showInfo['title'], 'year': showInfo['year'], 'episodes': []} for ep in summaryInfo: if ep['episode'] in data['episodes']: if not ep['watched']: params['episodes'].append({'season': ep['season'], 'episode': ep['episode']}) logger.debug("doMarkWatched(): '%s - Season %d' has %d episode(s) that are going to be marked as watched." % (showInfo['title'], data['season'], len(params['episodes']))) if len(params['episodes']) > 0: logger.debug("doMarkWatched(): %s" % str(params)) result = globals.traktapi.updateSeenEpisode(params) if result: if markedNotification: utilities.notification(utilities.getString(32113), utilities.getString(32115) % (len(params['episodes']), s)) else: utilities.notification(utilities.getString(32114), utilities.getString(32115) % (len(params['episodes']), s)) elif utilities.isShow(media_type): summaryInfo = globals.traktapi.getShowSummary(data['id'], extended=True) if summaryInfo: s = utilities.getFormattedItemName(media_type, summaryInfo) params = {'imdb_id': summaryInfo['ids']['imdb'], 'tvdb_id': summaryInfo['ids']['tvdb'], 'title': summaryInfo['title'], 'year': summaryInfo['year'], 'episodes': []} for season in summaryInfo['seasons']: for ep in season['episodes']: if str(season['season']) in data['seasons']: if ep['episode'] in data['seasons'][str(season['season'])]: if not ep['watched']: params['episodes'].append({'season': ep['season'], 'episode': ep['episode']}) logger.debug("doMarkWatched(): '%s' has %d episode(s) that are going to be marked as watched." % (summaryInfo['title'], len(params['episodes']))) if len(params['episodes']) > 0: logger.debug("doMarkWatched(): %s" % str(params)) result = globals.traktapi.updateSeenEpisode(params) if result: if markedNotification: utilities.notification(utilities.getString(32113), utilities.getString(32115) % (len(params['episodes']), s)) else: utilities.notification(utilities.getString(32114), utilities.getString(32115) % (len(params['episodes']), s)) def doSync(self, manual=False, silent=False, library="all"): self.syncThread = syncThread(manual, silent, library) self.syncThread.start()