def run(self): #while xbmc is running self._scrobbler = Scrobbler() self._scrobbler.start() while not (self._abortRequested or xbmc.abortRequested): try: #try to connect, catch errors and retry every 5 seconds telnet = telnetlib.Telnet(self.TELNET_ADDRESS, self.TELNET_PORT) #if connection succeeds while not (self._abortRequested or xbmc.abortRequested): try: #read notification data data = self._readNotification(telnet) Debug("[Notification Service] message: " + str(data)) self._forward(data) except EOFError: #if we end up here, it means the connection was lost or reset, # so we empty out the buffer, and exit this loop, which retries # the connection in the outer loop self._notificationBuffer = "" break except: time.sleep(5) continue telnet.close() self._scrobbler.abortRequested = True Debug("Notification service stopping")
def run(self): startup_delay = utilities.getSettingAsInt('startup_delay') if startup_delay: utilities.Debug("Delaying startup by %d seconds." % startup_delay) xbmc.sleep(startup_delay * 1000) utilities.Debug("Service thread starting.") # 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) # init tagging class self.tagger = Tagger(globals.traktapi) # purge queue self.dispatchQueue.purge() # start loop for events while (not xbmc.abortRequested): while len(self.dispatchQueue) and (not xbmc.abortRequested): data = self.dispatchQueue.get() utilities.Debug("Queued dispatch: %s" % data) self._dispatch(data) if xbmc.Player().isPlayingVideo(): self.scrobbler.update() xbmc.sleep(500) # we are shutting down utilities.Debug("Beginning shut down.") # check if watcher is set and active, if so, cancel it. if self.watcher: if self.watcher.isAlive(): self.watcher.cancel() # delete player/monitor del self.Player del self.Monitor # check update tags thread. if self.updateTagsThread and self.updateTagsThread.isAlive(): self.updateTagsThread.join() # check if sync thread is running, if so, join it. if self.syncThread.isAlive(): self.syncThread.join()
def main(): register_exception_handler() assert get_access_token() scrobble_queue = Queue() scrobbler = Scrobbler(scrobble_queue) scrobbler.start() for Mon in get_monitors(): mon = Mon(scrobble_queue) mon.start()
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 self.Monitor.abortRequested(): if not utilities.getSetting('authorization'): last_reminder = utilities.getSettingAsInt('last_reminder') now = int(time.time()) if last_reminder >= 0 and last_reminder < now - (24 * 60 * 60): gui_utils.get_pin() while len(self.dispatchQueue) and ( not self.Monitor.abortRequested()): data = self.dispatchQueue.get() logger.debug("Queued dispatch: %s" % data) self._dispatch(data) if xbmc.Player().isPlayingVideo(): self.scrobbler.transitionCheck() if self.Monitor.waitForAbort(1): # Abort was requested while waiting. We should exit break # 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 run(self): #while xbmc is running scrobbler = Scrobbler() scrobbler.start() while (not (self.abortRequested or xbmc.abortRequested)): time.sleep(1) try: tn = telnetlib.Telnet('localhost', 9090, 10) except IOError as (errno, strerror): #connection failed, try again soon Debug("[Notification Service] Telnet too soon? ("+str(errno)+") "+strerror) time.sleep(1) continue Debug("[Notification Service] Waiting~"); bCount = 0 while (not (self.abortRequested or xbmc.abortRequested)): try: if bCount == 0: notification = "" inString = False [index, match, raw] = tn.expect(["(\\\\)|(\\\")|[{\"}]"], 0.2) #note, pre-compiled regex might be faster here notification += raw if index == -1: # Timeout continue if index == 0: # Found escaped quote match = match.group(0) if match == "\"": inString = not inString continue if match == "{": bCount += 1 if match == "}": bCount -= 1 if bCount > 0: continue if bCount < 0: bCount = 0 except EOFError: break #go out to the other loop to restart the connection Debug("[Notification Service] message: " + str(notification)) # Parse recieved notification data = json.loads(notification) # Forward notification to functions if 'method' in data and 'params' in data and 'sender' in data['params'] and data['params']['sender'] == 'xbmc': if data['method'] == 'Player.OnStop': scrobbler.playbackEnded() elif data['method'] == 'Player.OnPlay': if 'data' in data['params'] and 'item' in data['params']['data'] and 'type' in data['params']['data']['item']: scrobbler.playbackStarted(data['params']['data']) elif data['method'] == 'Player.OnPause': scrobbler.playbackPaused() elif data['method'] == 'System.OnQuit': self.abortRequested = True
class NotificationService: _scrobbler = None def __init__(self): self.run() def _dispatch(self, data): Debug("[Notification] Dispatch: %s" % data) xbmc.sleep(500) action = data["action"] if action == "started": p = {"item": {"type": data["type"], "id": data["id"]}} self._scrobbler.playbackStarted(p) elif action == "ended" or action == "stopped": self._scrobbler.playbackEnded() elif action == "paused": self._scrobbler.playbackPaused() elif action == "resumed": self._scrobbler.playbackResumed() elif action == "databaseUpdated": if do_sync('movies'): movies = SyncMovies(show_progress=False) movies.Run() if do_sync('episodes'): episodes = SyncEpisodes(show_progress=False) episodes.Run() elif action == "scanStarted": Debug("[Notification] Dispatch: scanStarted") else: Debug("[Notification] '%s' unknown dispatch action!" % action) def run(self): Debug("[Notification] Starting") # setup event driven classes self.Player = traktPlayer(action = self._dispatch) self.Monitor = traktMonitor(action = self._dispatch) # initalize scrobbler class self._scrobbler = Scrobbler() self._scrobbler.start() # start loop for events while (not xbmc.abortRequested): xbmc.sleep(500) # we aborted if xbmc.abortRequested: Debug("[Notification] abortRequested received, shutting down.") # join scrobbler, to wait for termination Debug("[Notification] Joining scrobbler thread to wait for exit.") self._scrobbler.join()
def run(self): startup_delay = kodiUtilities.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) AddonSignals.registerSlot('service.nextup.notification', 'NEXTUPWATCHEDSIGNAL', self.callback) # start loop for events while not self.Monitor.abortRequested(): while len(self.dispatchQueue) and ( not self.Monitor.abortRequested()): data = self.dispatchQueue.get() logger.debug("Queued dispatch: %s" % data) self._dispatch(data) if xbmc.Player().isPlayingVideo(): self.scrobbler.transitionCheck() if self.Monitor.waitForAbort(1): # Abort was requested while waiting. We should exit break # 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 login_lastfm(self): '''Last.fm登陆''' if self.lastfm and self.last_fm_username and self.last_fm_password: self.scrobbler = Scrobbler(self.last_fm_username, self.last_fm_password) r, err = self.scrobbler.handshake() if r: logger.info("Last.fm login succeeds!") print '\033[31m♥\033[0m Last.fm logged in: %s' % self.last_fm_username else: logger.error("Last.fm login fails: " + err) self.lastfm = False else: self.lastfm = False
def run(self): Debug("[Notification] Starting") # setup event driven classes self.Player = traktPlayer(action = self._dispatch) self.Monitor = traktMonitor(action = self._dispatch) # init traktapi class globals.traktapi = traktAPI() # initalize scrobbler class self._scrobbler = Scrobbler(globals.traktapi) # start loop for events while (not xbmc.abortRequested): xbmc.sleep(500) # we aborted if xbmc.abortRequested: Debug("[Notification] abortRequested received, shutting down.") # delete player/monitor del self.Player del self.Monitor # join scrobbler, to wait for termination Debug("[Notification] Joining scrobbler thread to wait for exit.") self._scrobbler.join()
def __init__(self): self._load_config() self.douban = Douban(self.email, self.password, self.user_id, self.expire, self.token, self.user_name) self.player = Player() self.current_channel = 0 self.current_song = None self.current_play_list = None self.get_channels() self.palette = [('selected', 'bold', 'default'), ('title', 'yellow', 'default')] self.selected_button = None self.main_loop = None self.song_change_alarm = None if self.scrobbling: self.scrobbler = Scrobbler(self.last_fm_username, self.last_fm_password) r = self.scrobbler.handshake() if r: print("Last.FM logged in.") else: print("Last.FM login failed") if self.douban_account: r, err = self.douban.do_login() if r: print("Douban logged in.") self._save_cache() else: print("Douban login failed: " + err)
class main(): ## The Constructor # @param self Object pointer # @param pyjama Reference to the pyjama object def __init__(self, pyjama): self.pyjama = pyjama self.Events = self.pyjama.Events self.Events.connect_event("nowplaying", self.ev_nowplaying) self.Events.connect_event('player-status', self.ev_player_status) self.pyjama.preferences.register_plugin("LastFM", self.create_preferences, self.save_preferences) # login to last.fm self.scrobbler = None if self.get_session(): logging.debug('last.fm plugin loaded') else: logging.debug('last.fm plugin loaded but scrobbling isn\'t available') #raise self.last_scrobbled = None # def get_session(self): # thr = Thread(target = self.get_session_do, args = ()) # thr.start() @threaded def get_session(self): if self.pyjama.settings.get_value('LASTFM','SCROBBLING'): login=str(self.pyjama.settings.get_value('LASTFM','LOGIN')) password=str(self.pyjama.settings.get_value('LASTFM','PASS')) try: # pyjama has own last.fm clien id 'pyj' self.scrobbler=Scrobbler(client=('pyj',VERSION)) self.scrobbler.login(login, password) except Exception, e: logging.error(e) self.scrobbler=None return False else: #self.logged=True return True else:
def __init__(self): self.email = None self.password = None self.user_name = None self.user_id = None self.expire = None self.token = None self.cookies = None self.last_fm_username = None self.last_fm_password = None self.scrobbling = True self.douban_account = True self.channels = None # Set up config try: arg = sys.argv[1] self._do_config() except IndexError: self._load_config() # Init API tools self.douban = Douban( self.email, self.password, self.user_id, self.expire, self.token, self.user_name, self.cookies) self.player = Player() self.current_channel = 0 self.current_song = None self.current_play_list = None # Init terminal ui self.palette = [('selected', 'bold', 'default'), ('title', 'yellow', 'default')] self.selected_button = None self.main_loop = None self.song_change_alarm = None # Try to login if self.last_fm_username is None or self.last_fm_username == "": self.scrobbling = False if (self.email is None or self.email == "") and self.cookies == None: self.douban_account = False if self.scrobbling: self.scrobbler = Scrobbler( self.last_fm_username, self.last_fm_password) r, err = self.scrobbler.handshake() if r: print("Last.FM 已登陆") else: print("Last.FM 登录失败: " + err) if self.douban_account: r, err = self.douban.do_login() if r: print("Douban 已登陆") else: print("Douban 登录失败: " + err) self.get_channels() self._save_cache()
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.transitionCheck() 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 run(self): startup_delay = utilities.getSettingAsInt('startup_delay') if startup_delay: utilities.Debug("Delaying startup by %d seconds." % startup_delay) xbmc.sleep(startup_delay * 1000) utilities.Debug("Service thread starting.") # 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) # init tagging class self.tagger = Tagger(globals.traktapi) # purge queue self.dispatchQueue.purge() # start loop for events while (not xbmc.abortRequested): while len(self.dispatchQueue) and (not xbmc.abortRequested): data = self.dispatchQueue.get() utilities.Debug("Queued dispatch: %s" % data) self._dispatch(data) if xbmc.Player().isPlayingVideo(): self.scrobbler.update() xbmc.sleep(500) # we are shutting down utilities.Debug("Beginning shut down.") # check if watcher is set and active, if so, cancel it. if self.watcher: if self.watcher.isAlive(): self.watcher.cancel() # delete player/monitor del self.Player del self.Monitor # check update tags thread. if self.updateTagsThread and self.updateTagsThread.isAlive(): self.updateTagsThread.join() # check if sync thread is running, if so, join it. if self.syncThread.isAlive(): self.syncThread.join()
def run(self): startup_delay = utilities.getSettingAsInt('startup_delay') if startup_delay: utilities.Debug("Delaying startup by %d seconds." % startup_delay) xbmc.sleep(startup_delay * 1000) utilities.Debug("Service thread starting.") # 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 not self.dispatchQueue.empty() and (not xbmc.abortRequested): data = self.dispatchQueue.get() utilities.Debug("Queued dispatch: %s" % data) self._dispatch(data) # check if we were tasked to do a manual sync if utilities.getPropertyAsBool('traktManualSync'): if not self.syncThread.isAlive(): utilities.Debug("Performing a manual sync.") self.doSync(manual=True) else: utilities.Debug("There already is a sync in progress.") utilities.clearProperty('traktManualSync') if xbmc.Player().isPlayingVideo(): self.scrobbler.update() xbmc.sleep(500) # we are shutting down utilities.Debug("Beginning shut down.") # check if watcher is set and active, if so, cancel it. if self.watcher: if self.watcher.isAlive(): self.watcher.cancel() # 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 run(self): startup_delay = kodiUtilities.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) AddonSignals.registerSlot('service.nextup.notification', 'NEXTUPWATCHEDSIGNAL', self.callback) # start loop for events while not self.Monitor.abortRequested(): if not kodiUtilities.getSetting('authorization'): last_reminder = kodiUtilities.getSettingAsInt('last_reminder') now = int(time.time()) if last_reminder >= 0 and last_reminder < now - (24 * 60 * 60): gui_utils.get_pin() while len(self.dispatchQueue) and (not self.Monitor.abortRequested()): data = self.dispatchQueue.get() logger.debug("Queued dispatch: %s" % data) self._dispatch(data) if xbmc.Player().isPlayingVideo(): self.scrobbler.transitionCheck() if self.Monitor.waitForAbort(1): # Abort was requested while waiting. We should exit break # 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 setup_scrobbler(api_key, shared_secret, username, password, max_retries, max_retry_delay): """Returns Last.fm scrobbler. Keyword argument(s): api_key -- Last.fm API key shared_secret -- Last.fm API shared secret username -- Last.fm username password -- Last.fm password max_retries -- Maximum number of retries max_delay -- Maximum delay between retries """ scrobbler = Scrobbler(api_key, shared_secret, max_retries, max_retry_delay) result = scrobbler.authenticate(username, password) if not result: log.error("Last.fm authentication failed") sys.exit(errno.EACCES) return scrobbler
class App: def __init__(self): # Version version = "0.1.0" with open("version.txt") as f: version = f.read() os.environ["BUTLER_VERSION"] = version # Config config = {} with open("config.json") as f: raw_config = f.read() config = json.loads(raw_config) config_i = config["interface"] # Initialize self.sampler = Sampler( interface_name=config_i["name"], channels=config_i["channels"], sample_format=config_i["sample_format"], sample_rate=config_i["sample_rate"], ) self.fp = Fingerprinter(FINGERPRINT_API_KEY) self.fm = Scrobbler(LASTFM_API_KEY, LASTFM_SECRET, LASTFM_SESSION_KEY) def run(self): while (True): logger.info(f'Recording and fingerprinting sample') self.sampler.record() self.sampler.save(SAMPLE_FILENAME) res = self.fp.run(SAMPLE_FILENAME) if (res["status"] != "success"): logger.error(f'Failed to fingerprint sample') elif (res["result"] == None): logger.info('No track detected by fingerprinter') else: self.fm.run(res["result"]) sleep(SLEEP_SEC)
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: if not utilities.getSetting('authorization'): last_reminder = utilities.getSettingAsInt('last_reminder') now = int(time.time()) if last_reminder >= 0 and last_reminder < now - (24 * 60 * 60): gui_utils.get_pin() 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.transitionCheck() 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 __init__(self): # Version version = "0.1.0" with open("version.txt") as f: version = f.read() os.environ["BUTLER_VERSION"] = version # Config config = {} with open("config.json") as f: raw_config = f.read() config = json.loads(raw_config) config_i = config["interface"] # Initialize self.sampler = Sampler( interface_name=config_i["name"], channels=config_i["channels"], sample_format=config_i["sample_format"], sample_rate=config_i["sample_rate"], ) self.fp = Fingerprinter(FINGERPRINT_API_KEY) self.fm = Scrobbler(LASTFM_API_KEY, LASTFM_SECRET, LASTFM_SESSION_KEY)
def login_lastfm(self): '''Last.fm登陆''' if self.lastfm and self.last_fm_username and self.last_fm_password: self.scrobbler = Scrobbler( self.last_fm_username, self.last_fm_password) r, err = self.scrobbler.handshake() if r: logger.info("Last.fm login succeeds!") print '\033[31m♥\033[0m Last.fm logged in: %s' % self.last_fm_username else: logger.error("Last.fm login fails: " + err) self.lastfm = False else: self.lastfm = False
def lastfm_login(self): '''Last.fm登陆''' # username & password self.last_fm_username = \ self.login_data['last_fm_username'] if 'last_fm_username' in self.login_data\ else None self.last_fm_password = \ self.login_data['last_fm_password'] if 'last_fm_password' in self.login_data\ else None if len(sys.argv) > 1 and sys.argv[1] == 'last.fm': from hashlib import md5 username = raw_input('Last.fm username: '******'Last.fm password :'******'r') as f: data = pickle.load(f) with open(config.PATH_TOKEN, 'w') as f: data['last_fm_username'] = username data['last_fm_password'] = self.last_fm_password pickle.dump(data, f) # login if self.lastfm and self.last_fm_username and self.last_fm_password: self.scrobbler = Scrobbler( self.last_fm_username, self.last_fm_password) r, err = self.scrobbler.handshake() if r: logger.info("Last.fm login succeeds!") print '\033[31m♥\033[0m Last.fm logged in: %s' % self.last_fm_username else: logger.error("Last.fm login fails: " + err) self.lastfm = False else: self.lastfm = False
def get_session(self): if self.pyjama.settings.get_value("LIBREFM", "SCROBBLING"): login = str(self.pyjama.settings.get_value("LIBREFM", "LOGIN")) password = str(self.pyjama.settings.get_value("LIBREFM", "PASS")) try: # pyjama has own libre.fm clien id 'pyj' self.scrobbler = Scrobbler(client=("pyj", VERSION)) self.scrobbler.login(login, password) except Exception, e: logging.error(e) self.scrobbler = None return False else: # self.logged=True return True
def get_session(self): if self.pyjama.settings.get_value('LASTFM','SCROBBLING'): login=str(self.pyjama.settings.get_value('LASTFM','LOGIN')) password=str(self.pyjama.settings.get_value('LASTFM','PASS')) try: # pyjama has own last.fm clien id 'pyj' self.scrobbler=Scrobbler(client=('pyj',VERSION)) self.scrobbler.login(login, password) except Exception, e: logging.error(e) self.scrobbler=None return False else: #self.logged=True return True
def run(self): self._scrobbler = Scrobbler() self._scrobbler.start() telnet = telnetlib.Telnet(self.TELNET_ADDRESS, self.TELNET_PORT) while not (self.abort_requested or xbmc.abortRequested): try: data = self._read_notification(telnet) except EOFError: telnet = telnetlib.Telnet(self.TELNET_ADDRESS, self.TELNET_PORT) self._notification_buffer = "" continue Debug("[Notification Service] message: " + str(data)) self._forward(data) telnet.close() self._scrobbler.abortRequested = True Debug("Notification service stopping")
class Doubanfm(object): def __init__(self): self.login_data = {} self.lastfm = True # lastfm 登陆 def init_login(self): print ''' ──╔╗─────╔╗────────╔═╗ ──║║─────║║────────║╔╝ ╔═╝╠══╦╗╔╣╚═╦══╦═╗╔╝╚╦╗╔╗ ║╔╗║╔╗║║║║╔╗║╔╗║╔╗╬╗╔╣╚╝║ ║╚╝║╚╝║╚╝║╚╝║╔╗║║║╠╣║║║║║ ╚══╩══╩══╩══╩╝╚╩╝╚╩╩╝╚╩╩╝ ''' self.douban_login() # 登陆 self.login_lastfm() # 登陆 last.fm print '\033[31m♥\033[0m Get channels ', # self.get_channels() # 获取频道列表 print '[\033[32m OK \033[0m]' # self.get_channellines() # 重构列表用以显示 print '\033[31m♥\033[0m Check PRO ', # self.is_pro() print '[\033[32m OK \033[0m]' def win_login(self): '''登陆界面''' email = raw_input('Email:') password = getpass.getpass('Password:'******'''Last.fm登陆''' if self.lastfm and self.last_fm_username and self.last_fm_password: self.scrobbler = Scrobbler( self.last_fm_username, self.last_fm_password) r, err = self.scrobbler.handshake() if r: logger.info("Last.fm login succeeds!") print '\033[31m♥\033[0m Last.fm logged in: %s' % self.last_fm_username else: logger.error("Last.fm login fails: " + err) self.lastfm = False else: self.lastfm = False def last_fm_account_required(fun): '''装饰器,用于需要登录Last.fm后才能使用的接口''' @wraps(fun) def wrapper(self, *args, **kwds): if not self.lastfm: return return fun(self, *args, **kwds) return wrapper @last_fm_account_required def submit_current_song(self): '''提交播放过的曲目''' # Submit the track if total playback time of the track > 30s if self.playingsong['length'] > 30: self.scrobbler.submit( self.playingsong['artist'], self.playingsong['title'], self.playingsong['albumtitle'], self.playingsong['length'] ) @last_fm_account_required def scrobble_now_playing(self): '''提交当前正在播放曲目''' self.scrobbler.now_playing( self.playingsong['artist'], self.playingsong['title'], self.playingsong['albumtitle'], self.playingsong['length'] ) def douban_login(self): '''登陆douban.fm获取token''' path_token = os.path.expanduser('~/.douban_token.txt') if os.path.exists(path_token): # 已登陆 logger.info("Found existing Douban.fm token.") with open(path_token, 'r') as f: self.login_data = pickle.load(f) self.token = self.login_data['token'] self.user_name = self.login_data['user_name'] self.user_id = self.login_data['user_id'] self.expire = self.login_data['expire'] self.default_volume = int(self.login_data['volume'])\ if 'volume' in self.login_data else 50 self.default_channel = int(self.login_data['channel'])\ if 'channel' in self.login_data else 1 # 存储的default_channel是行数而不是真正发送数据的channel_id # 这里需要进行转化一下 self.set_channel(self.default_channel) print '\033[31m♥\033[0m Get local token - Username: \033[33m%s\033[0m' % self.user_name else: # 未登陆 logger.info('First time logging in Douban.fm.') while True: self.email, self.password = self.win_login() login_data = { 'app_name': 'radio_desktop_win', 'version': '100', 'email': self.email, 'password': self.password } s = requests.post('http://www.douban.com/j/app/login', login_data) dic = eval(s.text) if dic['r'] == 1: logger.debug(dic['err']) continue else: self.token = dic['token'] self.user_name = dic['user_name'] self.user_id = dic['user_id'] self.expire = dic['expire'] self.default_volume = 50 self.default_channel = 1 self.login_data = { 'app_name': 'radio_desktop_win', 'version': '100', 'user_id': self.user_id, 'expire': self.expire, 'token': self.token, 'user_name': self.user_name, 'volume': '50', 'channel': '0' } logger.info('Logged in username: '******'w') as f: pickle.dump(self.login_data, f) logger.debug('Write data to ' + path_token) break self.last_fm_username = \ self.login_data['last_fm_username'] if 'last_fm_username' in self.login_data\ else None self.last_fm_password = \ self.login_data['last_fm_password'] if 'last_fm_password' in self.login_data\ else None # last.fm登陆 try: if sys.argv[1] == 'last.fm': from hashlib import md5 username = raw_input('last.fm username:'******'last.fm password:'******'r') as f: data = pickle.load(f) with open(path_token, 'w') as f: data['last_fm_username'] = username data['last_fm_password'] = self.last_fm_password pickle.dump(data, f) except IndexError: pass # 配置文件 path_config = os.path.expanduser('~/.doubanfm_config') if not os.path.exists(path_config): print '\033[31m♥\033[0m Get default config [\033[32m OK \033[0m]' config = '''[key] UP = k DOWN = j TOP = g BOTTOM = G OPENURL = w RATE = r NEXT = n BYE = b QUIT = q PAUSE = p LOOP = l MUTE = m LRC = o ''' # 这个很丑,怎么办 with open(path_config, 'w') as F: F.write(config) else: print '\033[31m♥\033[0m Get local config [\033[32m OK \033[0m]' @property def channels(self): '''获取channel,存入self.channels''' # 红心兆赫需要手动添加 channels = [{ 'name': '红心兆赫', 'channel_id': -3 }] r = requests.get('http://www.douban.com/j/app/radio/channels') channels += eval(r.text)['channels'] # 格式化频道列表,以便display lines = [] for channel in channels: lines.append(channel['name']) return lines def requests_url(self, ptype, **data): '''这里包装了一个函数,发送post_data''' post_data = self.login_data.copy() post_data['type'] = ptype for x in data: post_data[x] = data[x] url = 'http://www.douban.com/j/app/radio/people?' + urllib.urlencode(post_data) try: s = requests.get(url) except requests.exceptions.RequestException: logger.error("Error communicating with Douban.fm API.") return s.text def set_channel(self, channel): '''把行数转化成channel_id''' self.default_channel = channel channel = -3 if channel == 0 else channel - 1 self.login_data['channel'] = channel def get_playlist(self, channel): '''获取播放列表,返回一个list''' if self.default_channel != channel: self.set_channel(channel) s = self.requests_url('n') return eval(s)['song'] def skip_song(self, playingsong): '''下一首,返回一个list''' s = self.requests_url('s', sid=playingsong['sid']) return eval(s)['song'] def bye(self, playingsong): '''不再播放,返回一个list''' s = self.requests_url('b', sid=playingsong['sid']) return eval(s)['song'] def rate_music(self, playingsong): '''标记喜欢歌曲''' self.requests_url('r', sid=playingsong['sid']) # self.playlist = eval(s)['song'] def unrate_music(self, playingsong): '''取消标记喜欢歌曲''' self.requests_url('u', sid=playingsong['sid']) # self.playlist = eval(s)['song'] def submit_music(self, playingsong): '''歌曲结束标记''' self.requests_url('e', sid=playingsong['sid']) def get_pic(self, playingsong, tempfile_path): '''获取专辑封面''' url = playingsong['picture'].replace('\\', '') for i in range(3): try: urllib.urlretrieve(url, tempfile_path) logger.debug('Get cover art success!') return True except (IOError, urllib.ContentTooShortError): pass logger.error('Get cover art failed!') return False def get_lrc(self, playingsong): '''获取歌词''' try: url = "http://api.douban.com/v2/fm/lyric" postdata = { 'sid': playingsong['sid'], 'ssid': playingsong['ssid'], } s = requests.session() response = s.post(url, data=postdata) lyric = eval(response.text) logger.debug(response.text) lrc_dic = lrc2dic.lrc2dict(lyric['lyric']) # 原歌词用的unicode,为了兼容 for key, value in lrc_dic.iteritems(): lrc_dic[key] = value.decode('utf-8') if lrc_dic: logger.debug('Get lyric success!') return lrc_dic except requests.exceptions.RequestException: logger.error('Get lyric failed!') return 0
class Doubanfm(object): def __init__(self): self.login_data = {} self.lastfm = True # lastfm 登陆 def init_login(self): print ''' ──╔╗─────╔╗────────╔═╗ ──║║─────║║────────║╔╝ ╔═╝╠══╦╗╔╣╚═╦══╦═╗╔╝╚╦╗╔╗ ║╔╗║╔╗║║║║╔╗║╔╗║╔╗╬╗╔╣╚╝║ ║╚╝║╚╝║╚╝║╚╝║╔╗║║║╠╣║║║║║ ╚══╩══╩══╩══╩╝╚╩╝╚╩╩╝╚╩╩╝ ''' self.douban_login() # 登陆 self.login_lastfm() # 登陆 last.fm print '\033[31m♥\033[0m Get channels ', # self.get_channels() # 获取频道列表 print '[\033[32m OK \033[0m]' # self.get_channellines() # 重构列表用以显示 print '\033[31m♥\033[0m Check PRO ', # self.is_pro() print '[\033[32m OK \033[0m]' def win_login(self): '''登陆界面''' email = raw_input('Email:') password = getpass.getpass('Password:'******'''Last.fm登陆''' if self.lastfm and self.last_fm_username and self.last_fm_password: self.scrobbler = Scrobbler(self.last_fm_username, self.last_fm_password) r, err = self.scrobbler.handshake() if r: logger.info("Last.fm login succeeds!") print '\033[31m♥\033[0m Last.fm logged in: %s' % self.last_fm_username else: logger.error("Last.fm login fails: " + err) self.lastfm = False else: self.lastfm = False def last_fm_account_required(fun): '''装饰器,用于需要登录Last.fm后才能使用的接口''' @wraps(fun) def wrapper(self, *args, **kwds): if not self.lastfm: return return fun(self, *args, **kwds) return wrapper @last_fm_account_required def submit_current_song(self): '''提交播放过的曲目''' # Submit the track if total playback time of the track > 30s if self.playingsong['length'] > 30: self.scrobbler.submit(self.playingsong['artist'], self.playingsong['title'], self.playingsong['albumtitle'], self.playingsong['length']) @last_fm_account_required def scrobble_now_playing(self): '''提交当前正在播放曲目''' self.scrobbler.now_playing(self.playingsong['artist'], self.playingsong['title'], self.playingsong['albumtitle'], self.playingsong['length']) def douban_login(self): '''登陆douban.fm获取token''' path_token = os.path.expanduser('~/.douban_token.txt') if os.path.exists(path_token): # 已登陆 logger.info("Found existing Douban.fm token.") with open(path_token, 'r') as f: self.login_data = pickle.load(f) self.token = self.login_data['token'] self.user_name = self.login_data['user_name'] self.user_id = self.login_data['user_id'] self.expire = self.login_data['expire'] self.default_volume = int(self.login_data['volume'])\ if 'volume' in self.login_data else 50 self.default_channel = int(self.login_data['channel'])\ if 'channel' in self.login_data else 1 # 存储的default_channel是行数而不是真正发送数据的channel_id # 这里需要进行转化一下 self.set_channel(self.default_channel) print '\033[31m♥\033[0m Get local token - Username: \033[33m%s\033[0m' % self.user_name else: # 未登陆 logger.info('First time logging in Douban.fm.') while True: self.email, self.password = self.win_login() login_data = { 'app_name': 'radio_desktop_win', 'version': '100', 'email': self.email, 'password': self.password } s = requests.post('http://www.douban.com/j/app/login', login_data) dic = eval(s.text) if dic['r'] == 1: logger.debug(dic['err']) continue else: self.token = dic['token'] self.user_name = dic['user_name'] self.user_id = dic['user_id'] self.expire = dic['expire'] self.default_volume = 50 self.default_channel = 1 self.login_data = { 'app_name': 'radio_desktop_win', 'version': '100', 'user_id': self.user_id, 'expire': self.expire, 'token': self.token, 'user_name': self.user_name, 'volume': '50', 'channel': '0' } logger.info('Logged in username: '******'w') as f: pickle.dump(self.login_data, f) logger.debug('Write data to ' + path_token) break self.last_fm_username = \ self.login_data['last_fm_username'] if 'last_fm_username' in self.login_data\ else None self.last_fm_password = \ self.login_data['last_fm_password'] if 'last_fm_password' in self.login_data\ else None # last.fm登陆 try: if sys.argv[1] == 'last.fm': from hashlib import md5 username = raw_input('last.fm username:'******'last.fm password:'******'r') as f: data = pickle.load(f) with open(path_token, 'w') as f: data['last_fm_username'] = username data['last_fm_password'] = self.last_fm_password pickle.dump(data, f) except IndexError: pass # 配置文件 path_config = os.path.expanduser('~/.doubanfm_config') if not os.path.exists(path_config): print '\033[31m♥\033[0m Get default config [\033[32m OK \033[0m]' config = '''[key] UP = k DOWN = j TOP = g BOTTOM = G OPENURL = w RATE = r NEXT = n BYE = b QUIT = q PAUSE = p LOOP = l MUTE = m LRC = o ''' # 这个很丑,怎么办 with open(path_config, 'w') as F: F.write(config) else: print '\033[31m♥\033[0m Get local config [\033[32m OK \033[0m]' @property def channels(self): '''获取channel,存入self.channels''' # 红心兆赫需要手动添加 channels = [{'name': '红心兆赫', 'channel_id': -3}] r = requests.get('http://www.douban.com/j/app/radio/channels') channels += eval(r.text)['channels'] # 格式化频道列表,以便display lines = [] for channel in channels: lines.append(channel['name']) return lines def requests_url(self, ptype, **data): '''这里包装了一个函数,发送post_data''' post_data = self.login_data.copy() post_data['type'] = ptype for x in data: post_data[x] = data[x] url = 'http://www.douban.com/j/app/radio/people?' + urllib.urlencode( post_data) try: s = requests.get(url) except requests.exceptions.RequestException: logger.error("Error communicating with Douban.fm API.") return s.text def set_channel(self, channel): '''把行数转化成channel_id''' self.default_channel = channel channel = -3 if channel == 0 else channel - 1 self.login_data['channel'] = channel def get_playlist(self, channel): '''获取播放列表,返回一个list''' if self.default_channel != channel: self.set_channel(channel) s = self.requests_url('n') return eval(s)['song'] def skip_song(self, playingsong): '''下一首,返回一个list''' s = self.requests_url('s', sid=playingsong['sid']) return eval(s)['song'] def bye(self, playingsong): '''不再播放,返回一个list''' s = self.requests_url('b', sid=playingsong['sid']) return eval(s)['song'] def rate_music(self, playingsong): '''标记喜欢歌曲''' self.requests_url('r', sid=playingsong['sid']) # self.playlist = eval(s)['song'] def unrate_music(self, playingsong): '''取消标记喜欢歌曲''' self.requests_url('u', sid=playingsong['sid']) # self.playlist = eval(s)['song'] def submit_music(self, playingsong): '''歌曲结束标记''' self.requests_url('e', sid=playingsong['sid']) def get_pic(self, playingsong, tempfile_path): '''获取专辑封面''' url = playingsong['picture'].replace('\\', '') for i in range(3): try: urllib.urlretrieve(url, tempfile_path) logger.debug('Get cover art success!') return True except (IOError, urllib.ContentTooShortError): pass logger.error('Get cover art failed!') return False def get_lrc(self, playingsong): '''获取歌词''' try: url = "http://api.douban.com/v2/fm/lyric" postdata = { 'sid': playingsong['sid'], 'ssid': playingsong['ssid'], } s = requests.session() response = s.post(url, data=postdata) lyric = eval(response.text) logger.debug(response.text) lrc_dic = lrc2dic.lrc2dict(lyric['lyric']) # 原歌词用的unicode,为了兼容 for key, value in lrc_dic.iteritems(): lrc_dic[key] = value.decode('utf-8') if lrc_dic: logger.debug('Get lyric success!') return lrc_dic except requests.exceptions.RequestException: logger.error('Get lyric failed!') return 0
class NotificationService(threading.Thread): """ Receives XBMC notifications and passes them off as needed """ TELNET_ADDRESS = 'localhost' TELNET_PORT = 9090 abort_requested = False _scrobbler = None _notification_buffer = "" def _forward(self, notification): """ Fowards the notification recieved to a function on the scrobbler """ if not ('method' in notification and 'params' in notification and 'sender' in notification['params'] and notification['params']['sender'] == 'xbmc'): return if notification['method'] == 'Player.OnStop': self._scrobbler.playback_ended() elif notification['method'] == 'Player.OnPlay': if 'data' in notification['params'] and 'item' in notification['params']['data'] and 'id' in notification['params']['data']['item'] and 'type' in notification['params']['data']['item']: self._scrobbler.playback_started(notification['params']['data']) elif notification['method'] == 'Player.OnPause': self._scrobbler.playback_paused() elif notification['method'] == 'System.OnQuit': self.abort_requested = True def _read_notification(self, telnet): """ Read a notification from the telnet connection, blocks until the data is available, or else raises an EOFError if the connection is lost """ while True: try: addbuffer = telnet.read_some() except socket.timeout: continue if addbuffer == "": raise EOFError self._notification_buffer += addbuffer try: data, offset = json.JSONDecoder().raw_decode(self._notification_buffer) self._notification_buffer = self._notification_buffer[offset:] except ValueError: continue return data def run(self): self._scrobbler = Scrobbler() self._scrobbler.start() telnet = telnetlib.Telnet(self.TELNET_ADDRESS, self.TELNET_PORT) while not (self.abort_requested or xbmc.abortRequested): try: data = self._read_notification(telnet) except EOFError: telnet = telnetlib.Telnet(self.TELNET_ADDRESS, self.TELNET_PORT) self._notification_buffer = "" continue Debug("[Notification Service] message: " + str(data)) self._forward(data) telnet.close() self._scrobbler.abortRequested = True Debug("Notification service stopping")
def run(self): #while xbmc is running scrobbler = Scrobbler() scrobbler.start() while (not (self.abortRequested or xbmc.abortRequested)): time.sleep(1) try: tn = telnetlib.Telnet('localhost', 9090, 10) except IOError as (errno, strerror): #connection failed, try again soon Debug("[Notification Service] Telnet too soon? (" + str(errno) + ") " + strerror) time.sleep(1) continue Debug("[Notification Service] Waiting~") bCount = 0 while (not (self.abortRequested or xbmc.abortRequested)): try: if bCount == 0: notification = "" inString = False [index, match, raw] = tn.expect( ["(\\\\)|(\\\")|[{\"}]"], 0.2) #note, pre-compiled regex might be faster here notification += raw if index == -1: # Timeout continue if index == 0: # Found escaped quote match = match.group(0) if match == "\"": inString = not inString continue if match == "{": bCount += 1 if match == "}": bCount -= 1 if bCount > 0: continue if bCount < 0: bCount = 0 except EOFError: break #go out to the other loop to restart the connection Debug("[Notification Service] message: " + str(notification)) # Parse recieved notification data = json.loads(notification) # Forward notification to functions if 'method' in data and 'params' in data and 'sender' in data[ 'params'] and data['params']['sender'] == 'xbmc': if data['method'] == 'Player.OnStop': scrobbler.playbackEnded() elif data['method'] == 'Player.OnPlay': if 'data' in data['params'] and 'item' in data[ 'params']['data'] and 'id' in data['params'][ 'data']['item'] and 'type' in data[ 'params']['data']['item']: scrobbler.playbackStarted(data['params']['data']) elif data['method'] == 'Player.OnPause': scrobbler.playbackPaused() elif data['method'] == 'VideoLibrary.OnUpdate': if 'data' in data['params'] and 'playcount' in data[ 'params']['data']: instantSyncPlayCount(data) elif data['method'] == 'System.OnQuit': self.abortRequested = True
headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:27.0) Gecko/20100101 Firefox/27.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3', 'Accept-Encoding': 'gzip, deflate', 'DNT': '1', 'Connection': 'keep-alive' } # 虾米个人首页地址 xiami_url = 'http://www.xiami.com/space/charts-recent/u/41714420/' # 用户名和MD5的密码 scrobbler = Scrobbler('skyline75489', '989f9717dce59b6c2f40a6ee940fa95c') def get_tracks(): r = requests.get(xiami_url, headers=headers) soup = BeautifulSoup(r.content) minutes = 5 track_times = soup.findAll('td', class_='track_time') track_times = [ re.search(u'\d+', track_time.text).group() for track_time in track_times if re.search(u'分钟前', track_time.text) ] second_html = soup.find('td', class_='track_time') if second_html: second_exist = re.search(u'秒前|刚刚', second_html.text) else:
class Doubanfm: def __init__(self): self.email = None self.password = None self.user_name = None self.user_id = None self.expire = None self.token = None self.cookies = None self.last_fm_username = None self.last_fm_password = None self.scrobbling = True self.douban_account = True self.channels = None # Set up config try: arg = sys.argv[1] self._do_config() except IndexError: self._load_config() # Init API tools self.douban = Douban( self.email, self.password, self.user_id, self.expire, self.token, self.user_name, self.cookies) self.player = Player() self.current_channel = 0 self.current_song = None self.current_play_list = None # Init terminal ui self.palette = [('selected', 'bold', 'default'), ('title', 'yellow', 'default')] self.selected_button = None self.main_loop = None self.song_change_alarm = None # Try to login if self.last_fm_username is None or self.last_fm_username == "": self.scrobbling = False if (self.email is None or self.email == "") and self.cookies == None: self.douban_account = False if self.scrobbling: self.scrobbler = Scrobbler( self.last_fm_username, self.last_fm_password) r, err = self.scrobbler.handshake() if r: print("Last.FM 已登陆") else: print("Last.FM 登录失败: " + err) if self.douban_account: r, err = self.douban.do_login() if r: print("Douban 已登陆") else: print("Douban 登录失败: " + err) self.get_channels() self._save_cache() def _do_config(self): self.email = input('豆瓣账户 (Email地址): ') or None self.password = getpass('豆瓣密码: ') or None self.last_fm_username = input('Last.fm 用户名: ') or None password = getpass('Last.fm 密码: ') or None self.last_fm_password = md5(password.encode('utf-8')).hexdigest() def _load_config(self): try: f = open('channels.json', 'r') self.channels = deque(json.load(f)) logger.debug("Load channel file.") except FileNotFoundError: logger.debug("Channels file not found.") try: f = open('cache.json', 'r') cache = json.load(f) try: self.user_name = cache['user_name'] self.user_id = cache['user_id'] self.expire = cache['expire'] self.token = cache['token'] self.cookies = cache['cookies'] except (KeyError, ValueError): self.douban_account = False try: self.last_fm_username = cache['last_fm_username'] self.last_fm_password = cache['last_fm_password'] except (KeyError, ValueError): self.scrobbling = False except FileNotFoundError: logger.debug("Cache file not found.") def _save_cache(self): f = None try: f = open('cache.json', 'w') f2 = open('channels.json', 'w') json.dump({ 'user_name': self.douban.user_name, 'user_id': self.douban.user_id, 'expire': self.douban.expire, 'token': self.douban.token, 'cookies': self.douban.cookies, 'last_fm_username': self.last_fm_username, 'last_fm_password': self.last_fm_password }, f) json.dump(list(self.channels), f2) except IOError: raise Exception("Unable to write cache file") def get_channels(self): if self.channels is None: self.channels = deque(self.douban.get_channels()) def _choose_channel(self, channel): self.current_channel = channel self.current_play_list = deque( self.douban.get_new_play_list(self.current_channel)) def _play_track(self): _song = self.current_play_list.popleft() self.current_song = Song(_song) logger.debug('Playing Track') logger.debug('Artist: ' + self.current_song.artist) logger.debug('Title: ' + self.current_song.song_title) logger.debug('Album: ' + self.current_song.album_title) logger.debug('Length: ' + self.current_song.length_in_str) logger.debug('Sid: ' + self.current_song.sid) logger.debug( '{0} tracks remaining in the playlist'.format(len(self.current_play_list))) self.song_change_alarm = self.main_loop.set_alarm_in(self.current_song.length_in_sec, self.next_song, None) self.selected_button.set_text(self.selected_button.text[0:11].strip()) heart = u'\N{WHITE HEART SUIT}' if self.current_song.like: heart = u'\N{BLACK HEART SUIT}' if not self.douban_account: heart = ' ' self.selected_button.set_text(self.selected_button.text + ' ' + heart + ' ' + self.current_song.artist + ' - ' + self.current_song.song_title) if self.scrobbling: self.scrobbler.now_playing(self.current_song.artist, self.current_song.song_title, self.current_song.album_title, self.current_song.length_in_sec) self.player.stop() self.player.play(self.current_song) # Currently playing the second last song in queue if len(self.current_play_list) == 1: # Extend the playing list playing_list = self.douban.get_playing_list( self.current_song.sid, self.current_channel) logger.debug('Got {0} more tracks'.format(len(playing_list))) self.current_play_list.extend(deque(playing_list)) def next_song(self, loop, user_data): # Scrobble the track if scrobbling is enabled # and total playback time of the track > 30s if self.scrobbling and self.current_song.length_in_sec > 30: self.scrobbler.submit(self.current_song.artist, self.current_song.song_title, self.current_song.album_title, self.current_song.length_in_sec) if self.douban_account: r, err = self.douban.end_song( self.current_song.sid, self.current_channel) if r: logger.debug('End song OK') else: logger.error(err) if self.song_change_alarm: self.main_loop.remove_alarm(self.song_change_alarm) self._play_track() def skip_current_song(self): if self.douban_account: r, err = self.douban.skip_song( self.current_song.sid, self.current_channel) if r: logger.debug('Skip song OK') else: logger.error(err) if self.song_change_alarm: self.main_loop.remove_alarm(self.song_change_alarm) self._play_track() def rate_current_song(self): if not self.douban_account: return r, err = self.douban.rate_song( self.current_song.sid, self.current_channel) if r: self.current_song.like = True self.selected_button.set_text(self.selected_button.text.replace( u'\N{WHITE HEART SUIT}', u'\N{BLACK HEART SUIT}')) logger.debug('Rate song OK') else: logger.error(err) def unrate_current_song(self): if not self.douban_account: return r, err = self.douban.unrate_song( self.current_song.sid, self.current_channel) if r: self.current_song.like = False self.selected_button.set_text(self.selected_button.text.replace( u'\N{BLACK HEART SUIT}', u'\N{WHITE HEART SUIT}')) logger.debug('Unrate song OK') else: logger.error(err) def trash_current_song(self): if not self.douban_account: return r, err = self.douban.bye_song( self.current_song.sid, self.current_channel) if r: # play next song if self.song_change_alarm: self.main_loop.remove_alarm(self.song_change_alarm) self._play_track() logger.debug('Trash song OK') else: logger.error(err) def quit(self): logger.debug('Quit') self.player.stop() def start(self): title = urwid.AttrMap(urwid.Text('豆瓣FM'), 'title') divider = urwid.Divider() pile = urwid.Padding( urwid.Pile([divider, title, divider]), left=4, right=4) box = urwid.Padding(self.ChannelListBox(), left=2, right=4) frame = urwid.Frame(box, header=pile, footer=divider) self.main_loop = urwid.MainLoop( frame, self.palette, handle_mouse=False) self.main_loop.run() def ChannelListBox(self): body = [] for c in self.channels: _channel = ChannelButton(c['name']) urwid.connect_signal( _channel, 'click', self.channel_chosen, c['channel_id']) body.append(urwid.AttrMap(_channel, None, focus_map="channel")) return MyListBox(urwid.SimpleFocusListWalker(body), self) def channel_chosen(self, button, choice): # Choose the channel which is playing right now # ignore this if self.selected_button == button: return # Choose a different channel if self.player.is_playing: self.player.stop() self._choose_channel(choice) if self.selected_button != None and button != self.selected_button: self.selected_button.set_text( self.selected_button.text[0:11].strip()) self.selected_button = button if self.song_change_alarm: self.main_loop.remove_alarm(self.song_change_alarm) self._play_track()
class traktService: scrobbler = None tagger = None updateTagsThread = None watcher = None syncThread = None dispatchQueue = queue.SqliteQueue() _interval = 10 * 60 # how often to send watching call def __init__(self): threading.Thread.name = 'trakt' def _dispatchQueue(self, data): utilities.Debug("Queuing for dispatch: %s" % data) self.dispatchQueue.append(data) def _dispatch(self, data): utilities.Debug("Dispatch: %s" % data) action = data['action'] if action == 'started': del data['action'] self.scrobbler.playbackStarted(data) self.watcher = threading.Timer(self._interval, self.doWatching) self.watcher.name = "trakt-watching" self.watcher.start() elif action == 'ended' or action == 'stopped': self.scrobbler.playbackEnded() if self.watcher: if self.watcher.isAlive(): self.watcher.cancel() self.watcher = None elif action == 'paused': self.scrobbler.playbackPaused() elif action == 'resumed': self.scrobbler.playbackResumed() elif action == 'seek' or action == 'seekchapter': self.scrobbler.playbackSeek() elif action == 'databaseUpdated': self.doSync() elif action == 'scanStarted': pass elif action == 'settingsChanged': utilities.Debug("Settings changed, reloading.") globals.traktapi.updateSettings() self.tagger.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(): utilities.Debug("Performing a manual sync.") self.doSync(manual=True) else: utilities.Debug("There already is a sync in progress.") elif action == 'updatetags': if self.updateTagsThread and self.updateTagsThread.isAlive(): utilities.Debug("Currently updating tags already.") else: self.updateTagsThread = threading.Thread( target=self.tagger.updateTagsFromTrakt, name="trakt-updatetags") self.updateTagsThread.start() elif action == 'managelists': self.tagger.manageLists() elif action == 'itemlists': del data['action'] self.tagger.itemLists(data) elif action == 'addtolist': del data['action'] list = data['list'] del data['list'] self.tagger.manualAddToList(list, data) elif action == 'removefromlist': del data['action'] list = data['list'] del data['list'] self.tagger.manualRemoveFromList(list, data) else: utilities.Debug("Unknown dispatch action, '%s'." % action) def run(self): startup_delay = utilities.getSettingAsInt('startup_delay') if startup_delay: utilities.Debug("Delaying startup by %d seconds." % startup_delay) xbmc.sleep(startup_delay * 1000) utilities.Debug("Service thread starting.") # 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) # init tagging class self.tagger = Tagger(globals.traktapi) # purge queue self.dispatchQueue.purge() # start loop for events while (not xbmc.abortRequested): while len(self.dispatchQueue) and (not xbmc.abortRequested): data = self.dispatchQueue.get() utilities.Debug("Queued dispatch: %s" % data) self._dispatch(data) if xbmc.Player().isPlayingVideo(): self.scrobbler.update() xbmc.sleep(500) # we are shutting down utilities.Debug("Beginning shut down.") # check if watcher is set and active, if so, cancel it. if self.watcher: if self.watcher.isAlive(): self.watcher.cancel() # delete player/monitor del self.Player del self.Monitor # check update tags thread. if self.updateTagsThread and self.updateTagsThread.isAlive(): self.updateTagsThread.join() # check if sync thread is running, if so, join it. if self.syncThread.isAlive(): self.syncThread.join() def doWatching(self): # check if we're still playing a video if not xbmc.Player().isPlayingVideo(): self.watcher = None return # call watching method self.scrobbler.watching() # start a new timer thread self.watcher = threading.Timer(self._interval, self.doWatching) self.watcher.name = "trakt-watching" self.watcher.start() def doManualRating(self, data): action = data['action'] media_type = data['media_type'] summaryInfo = None if not utilities.isValidMediaType(media_type): utilities.Debug( "doManualRating(): Invalid media type '%s' passed for manual %s." % (media_type, action)) return if not data['action'] in ['rate', 'unrate']: utilities.Debug("doManualRating(): Unknown action passed.") return if 'dbid' in data: utilities.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: utilities.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: utilities.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['tvdb_id'], 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']) elif action == 'unrate': rateMedia(media_type, summaryInfo, unrate=True) else: utilities.Debug( "doManualRating(): Summary info was empty, possible problem retrieving data from trakt.tv" ) def doMarkWatched(self, data): media_type = data['media_type'] simulate = utilities.getSettingAsBool('simulate_sync') 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) utilities.Debug( "doMarkWatched(): '%s' is not watched on trakt, marking it as watched." % s) movie = {} movie['imdb_id'] = data['id'] movie['title'] = summaryInfo['title'] movie['year'] = summaryInfo['year'] movie['plays'] = 1 movie['last_played'] = int(time()) params = {'movies': [movie]} utilities.Debug("doMarkWatched(): %s" % str(params)) if not simulate: result = globals.traktapi.updateSeenMovie(params) if result: if markedNotification: utilities.notification( utilities.getString(1550), s) else: utilities.notification(utilities.getString(1551), s) else: if markedNotification: utilities.notification(utilities.getString(1550), 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) utilities.Debug( "doMarkWathced(): '%s' is not watched on trakt, marking it as watched." % s) params = {} params['imdb_id'] = summaryInfo['show']['imdb_id'] params['tvdb_id'] = summaryInfo['show']['tvdb_id'] params['title'] = summaryInfo['show']['title'] params['year'] = summaryInfo['show']['year'] params['episodes'] = [{ 'season': data['season'], 'episode': data['episode'] }] utilities.Debug("doMarkWatched(): %s" % str(params)) if not simulate: result = globals.traktapi.updateSeenEpisode(params) if result: if markedNotification: utilities.notification( utilities.getString(1550), s) else: utilities.notification(utilities.getString(1551), s) else: if markedNotification: utilities.notification(utilities.getString(1550), 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 = {} params['imdb_id'] = showInfo['imdb_id'] params['tvdb_id'] = showInfo['tvdb_id'] params['title'] = showInfo['title'] params['year'] = showInfo['year'] params['episodes'] = [] for ep in summaryInfo: if ep['episode'] in data['episodes']: if not ep['watched']: params['episodes'].append({ 'season': ep['season'], 'episode': ep['episode'] }) utilities.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: utilities.Debug("doMarkWatched(): %s" % str(params)) if not simulate: result = globals.traktapi.updateSeenEpisode(params) if result: if markedNotification: utilities.notification( utilities.getString(1550), utilities.getString(1552) % (len(params['episodes']), s)) else: utilities.notification( utilities.getString(1551), utilities.getString(1552) % (len(params['episodes']), s)) else: if markedNotification: utilities.notification( utilities.getString(1550), utilities.getString(1552) % (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 = {} params['imdb_id'] = summaryInfo['imdb_id'] params['tvdb_id'] = summaryInfo['tvdb_id'] params['title'] = summaryInfo['title'] params['year'] = summaryInfo['year'] params['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'] }) utilities.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: utilities.Debug("doMarkWatched(): %s" % str(params)) if not simulate: result = globals.traktapi.updateSeenEpisode(params) if result: if markedNotification: utilities.notification( utilities.getString(1550), utilities.getString(1552) % (len(params['episodes']), s)) else: utilities.notification( utilities.getString(1551), utilities.getString(1552) % (len(params['episodes']), s)) else: if markedNotification: utilities.notification( utilities.getString(1550), utilities.getString(1552) % (len(params['episodes']), s)) def doSync(self, manual=False): self.syncThread = syncThread(manual) self.syncThread.start()
class NotificationService(threading.Thread): """ Receives XBMC notifications and passes them off as needed """ TELNET_ADDRESS = 'localhost' TELNET_PORT = 9090 _abortRequested = False _scrobbler = None _notificationBuffer = "" def _forward(self, notification): """ Fowards the notification recieved to a function on the scrobbler """ if not ('method' in notification and 'params' in notification and 'sender' in notification['params'] and notification['params']['sender'] == 'xbmc'): return if notification['method'] == 'Player.OnStop': self._scrobbler.playbackEnded() elif notification['method'] == 'Player.OnPlay': if 'data' in notification['params'] and 'item' in notification[ 'params']['data'] and 'type' in notification['params'][ 'data']['item']: self._scrobbler.playbackStarted(notification['params']['data']) elif notification['method'] == 'Player.OnPause': self._scrobbler.playbackPaused() elif notification['method'] == 'VideoLibrary.OnScanFinished': if do_sync('movies'): movies = SyncMovies(show_progress=False) movies.Run() if do_sync('episodes'): episodes = SyncEpisodes(show_progress=False) episodes.Run() elif notification['method'] == 'System.OnQuit': self._abortRequested = True def _readNotification(self, telnet): """ Read a notification from the telnet connection, blocks until the data is available, or else raises an EOFError if the connection is lost """ while True: try: addbuffer = telnet.read_some() except socket.timeout: continue if addbuffer == "": raise EOFError self._notificationBuffer += addbuffer try: data, offset = json.JSONDecoder().raw_decode( self._notificationBuffer) self._notificationBuffer = self._notificationBuffer[offset:] except ValueError: continue return data def run(self): #while xbmc is running self._scrobbler = Scrobbler() self._scrobbler.start() while not (self._abortRequested or xbmc.abortRequested): try: #try to connect, catch errors and retry every 5 seconds telnet = telnetlib.Telnet(self.TELNET_ADDRESS, self.TELNET_PORT) #if connection succeeds while not (self._abortRequested or xbmc.abortRequested): try: #read notification data data = self._readNotification(telnet) Debug("[Notification Service] message: " + str(data)) self._forward(data) except EOFError: #if we end up here, it means the connection was lost or reset, # so we empty out the buffer, and exit this loop, which retries # the connection in the outer loop self._notificationBuffer = "" break except: time.sleep(5) continue telnet.close() self._scrobbler.abortRequested = True Debug("Notification service stopping")
class traktService: scrobbler = None watcher = None syncThread = None dispatchQueue = Queue.Queue() _interval = 10 * 60 # how often to send watching call def __init__(self): threading.Thread.name = 'trakt' def _dispatchQueue(self, data): utilities.Debug("Queuing for dispatch: %s" % data) self.dispatchQueue.put(data) def _dispatch(self, data): utilities.Debug("Dispatch: %s" % data) action = data['action'] if action == 'started': del data['action'] self.scrobbler.playbackStarted(data) self.watcher = threading.Timer(self._interval, self.doWatching) self.watcher.name = "trakt-watching" self.watcher.start() elif action == 'ended' or action == 'stopped': self.scrobbler.playbackEnded() if self.watcher: if self.watcher.isAlive(): self.watcher.cancel() self.watcher = None elif action == 'paused': self.scrobbler.playbackPaused() elif action == 'resumed': self.scrobbler.playbackResumed() elif action == 'seek' or action == 'seekchapter': self.scrobbler.playbackSeek() elif action == 'databaseUpdated': self.doSync() elif action == 'scanStarted': pass elif action == 'settingsChanged': utilities.Debug("Settings changed, reloading.") globals.traktapi.updateSettings() else: utilities.Debug("Unknown dispatch action, '%s'." % action) def run(self): startup_delay = utilities.getSettingAsInt('startup_delay') if startup_delay: utilities.Debug("Delaying startup by %d seconds." % startup_delay) xbmc.sleep(startup_delay * 1000) utilities.Debug("Service thread starting.") # 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 not self.dispatchQueue.empty() and (not xbmc.abortRequested): data = self.dispatchQueue.get() utilities.Debug("Queued dispatch: %s" % data) self._dispatch(data) # check if we were tasked to do a manual sync if utilities.getPropertyAsBool('traktManualSync'): if not self.syncThread.isAlive(): utilities.Debug("Performing a manual sync.") self.doSync(manual=True) else: utilities.Debug("There already is a sync in progress.") utilities.clearProperty('traktManualSync') if xbmc.Player().isPlayingVideo(): self.scrobbler.update() xbmc.sleep(500) # we are shutting down utilities.Debug("Beginning shut down.") # check if watcher is set and active, if so, cancel it. if self.watcher: if self.watcher.isAlive(): self.watcher.cancel() # 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 doWatching(self): # check if we're still playing a video if not xbmc.Player().isPlayingVideo(): self.watcher = None return # call watching method self.scrobbler.watching() # start a new timer thread self.watcher = threading.Timer(self._interval, self.doWatching) self.watcher.name = "trakt-watching" self.watcher.start() def doSync(self, manual=False): self.syncThread = syncThread(manual) self.syncThread.start()
class traktService: scrobbler = None tagger = None updateTagsThread = None watcher = None syncThread = None dispatchQueue = queue.SqliteQueue() _interval = 10 * 60 # how often to send watching call def __init__(self): threading.Thread.name = 'trakt' def _dispatchQueue(self, data): utilities.Debug("Queuing for dispatch: %s" % data) self.dispatchQueue.append(data) def _dispatch(self, data): utilities.Debug("Dispatch: %s" % data) action = data['action'] if action == 'started': del data['action'] self.scrobbler.playbackStarted(data) self.watcher = threading.Timer(self._interval, self.doWatching) self.watcher.name = "trakt-watching" self.watcher.start() elif action == 'ended' or action == 'stopped': self.scrobbler.playbackEnded() if self.watcher: if self.watcher.isAlive(): self.watcher.cancel() self.watcher = None elif action == 'paused': self.scrobbler.playbackPaused() elif action == 'resumed': self.scrobbler.playbackResumed() elif action == 'seek' or action == 'seekchapter': self.scrobbler.playbackSeek() elif action == 'databaseUpdated': self.doSync() elif action == 'scanStarted': pass elif action == 'settingsChanged': utilities.Debug("Settings changed, reloading.") globals.traktapi.updateSettings() self.tagger.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(): utilities.Debug("Performing a manual sync.") self.doSync(manual=True, silent=data['silent']) else: utilities.Debug("There already is a sync in progress.") elif action == 'updatetags': if self.updateTagsThread and self.updateTagsThread.isAlive(): utilities.Debug("Currently updating tags already.") else: self.updateTagsThread = threading.Thread(target=self.tagger.updateTagsFromTrakt, name="trakt-updatetags") self.updateTagsThread.start() elif action == 'managelists': self.tagger.manageLists() elif action == 'itemlists': del data['action'] self.tagger.itemLists(data) elif action == 'addtolist': del data['action'] list = data['list'] del data['list'] self.tagger.manualAddToList(list, data) elif action == 'removefromlist': del data['action'] list = data['list'] del data['list'] self.tagger.manualRemoveFromList(list, data) elif action == 'loadsettings': force = False if 'force' in data: force = data['force'] globals.traktapi.getAccountSettings(force) else: utilities.Debug("Unknown dispatch action, '%s'." % action) def run(self): startup_delay = utilities.getSettingAsInt('startup_delay') if startup_delay: utilities.Debug("Delaying startup by %d seconds." % startup_delay) xbmc.sleep(startup_delay * 1000) utilities.Debug("Service thread starting.") # purge queue before doing anything self.dispatchQueue.purge() # queue a loadsettings action self.dispatchQueue.append({'action': 'loadsettings'}) # 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) # init tagging class self.tagger = Tagger(globals.traktapi) # start loop for events while (not xbmc.abortRequested): while len(self.dispatchQueue) and (not xbmc.abortRequested): data = self.dispatchQueue.get() utilities.Debug("Queued dispatch: %s" % data) self._dispatch(data) if xbmc.Player().isPlayingVideo(): self.scrobbler.update() xbmc.sleep(500) # we are shutting down utilities.Debug("Beginning shut down.") # check if watcher is set and active, if so, cancel it. if self.watcher: if self.watcher.isAlive(): self.watcher.cancel() # delete player/monitor del self.Player del self.Monitor # check update tags thread. if self.updateTagsThread and self.updateTagsThread.isAlive(): self.updateTagsThread.join() # check if sync thread is running, if so, join it. if self.syncThread.isAlive(): self.syncThread.join() def doWatching(self): # check if we're still playing a video if not xbmc.Player().isPlayingVideo(): self.watcher = None return # call watching method self.scrobbler.watching() # start a new timer thread self.watcher = threading.Timer(self._interval, self.doWatching) self.watcher.name = "trakt-watching" self.watcher.start() def doManualRating(self, data): action = data['action'] media_type = data['media_type'] summaryInfo = None if not utilities.isValidMediaType(media_type): utilities.Debug("doManualRating(): Invalid media type '%s' passed for manual %s." % (media_type, action)) return if not data['action'] in ['rate', 'unrate']: utilities.Debug("doManualRating(): Unknown action passed.") return if 'dbid' in data: utilities.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: utilities.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: utilities.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['tvdb_id'], 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']) elif action == 'unrate': rateMedia(media_type, summaryInfo, unrate=True) else: utilities.Debug("doManualRating(): Summary info was empty, possible problem retrieving data from trakt.tv") def doMarkWatched(self, data): media_type = data['media_type'] simulate = utilities.getSettingAsBool('simulate_sync') 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) utilities.Debug("doMarkWatched(): '%s' is not watched on trakt, marking it as watched." % s) movie = {} movie['imdb_id'] = data['id'] movie['title'] = summaryInfo['title'] movie['year'] = summaryInfo['year'] movie['plays'] = 1 movie['last_played'] = int(time()) params = {'movies': [movie]} utilities.Debug("doMarkWatched(): %s" % str(params)) if not simulate: result = globals.traktapi.updateSeenMovie(params) if result: if markedNotification: utilities.notification(utilities.getString(1550), s) else: utilities.notification(utilities.getString(1551), s) else: if markedNotification: utilities.notification(utilities.getString(1550), 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) utilities.Debug("doMarkWathced(): '%s' is not watched on trakt, marking it as watched." % s) params = {} params['imdb_id'] = summaryInfo['show']['imdb_id'] params['tvdb_id'] = summaryInfo['show']['tvdb_id'] params['title'] = summaryInfo['show']['title'] params['year'] = summaryInfo['show']['year'] params['episodes'] = [{'season': data['season'], 'episode': data['episode']}] utilities.Debug("doMarkWatched(): %s" % str(params)) if not simulate: result = globals.traktapi.updateSeenEpisode(params) if result: if markedNotification: utilities.notification(utilities.getString(1550), s) else: utilities.notification(utilities.getString(1551), s) else: if markedNotification: utilities.notification(utilities.getString(1550), 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 = {} params['imdb_id'] = showInfo['imdb_id'] params['tvdb_id'] = showInfo['tvdb_id'] params['title'] = showInfo['title'] params['year'] = showInfo['year'] params['episodes'] = [] for ep in summaryInfo: if ep['episode'] in data['episodes']: if not ep['watched']: params['episodes'].append({'season': ep['season'], 'episode': ep['episode']}) utilities.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: utilities.Debug("doMarkWatched(): %s" % str(params)) if not simulate: result = globals.traktapi.updateSeenEpisode(params) if result: if markedNotification: utilities.notification(utilities.getString(1550), utilities.getString(1552) % (len(params['episodes']), s)) else: utilities.notification(utilities.getString(1551), utilities.getString(1552) % (len(params['episodes']), s)) else: if markedNotification: utilities.notification(utilities.getString(1550), utilities.getString(1552) % (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 = {} params['imdb_id'] = summaryInfo['imdb_id'] params['tvdb_id'] = summaryInfo['tvdb_id'] params['title'] = summaryInfo['title'] params['year'] = summaryInfo['year'] params['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']}) utilities.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: utilities.Debug("doMarkWatched(): %s" % str(params)) if not simulate: result = globals.traktapi.updateSeenEpisode(params) if result: if markedNotification: utilities.notification(utilities.getString(1550), utilities.getString(1552) % (len(params['episodes']), s)) else: utilities.notification(utilities.getString(1551), utilities.getString(1552) % (len(params['episodes']), s)) else: if markedNotification: utilities.notification(utilities.getString(1550), utilities.getString(1552) % (len(params['episodes']), s)) def doSync(self, manual=False, silent=False): self.syncThread = syncThread(manual, silent) self.syncThread.start()
class Doubanfm: def __init__(self): self._load_config() self.douban = Douban(self.email, self.password, self.user_id, self.expire, self.token, self.user_name) self.player = Player() self.current_channel = 0 self.current_song = None self.current_play_list = None self.get_channels() self.palette = [('selected', 'bold', 'default'), ('title', 'yellow', 'default')] self.selected_button = None self.main_loop = None self.song_change_alarm = None if self.scrobbling: self.scrobbler = Scrobbler(self.last_fm_username, self.last_fm_password) r = self.scrobbler.handshake() if r: print("Last.FM logged in.") else: print("Last.FM login failed") if self.douban_account: r, err = self.douban.do_login() if r: print("Douban logged in.") self._save_cache() else: print("Douban login failed: " + err) def _load_config(self): self.email = None self.password = None self.user_id = None self.expire = None self.token = None self.user_name = None self.lasf_fm_username = None self.last_fm_password = None self.scrobbling = True self.douban_account = True self.channels = None config = None token = None try: f = open('config.json', 'r') config = json.load(f) self.email = config['email'] self.password = config['password'] except (KeyError,ValueError): self.douban_account = False print("Douban account not found. Personal FM disabled.") try: if config == None: raise ValueError self.last_fm_username = config['last_fm_username'] self.last_fm_password = config['last_fm_password'] except (KeyError,ValueError): self.scrobbling = False print("Last.fm account not found. Scrobbling disabled.") try: f = open('channels.json', 'r') self.channels = json.load(f) print("Load channel file.") except FileNotFoundError: print("Channels file not found.") def _save_cache(self): f = None try: f = open('cache.json', 'w') f2 = open('channels.json', 'w') json.dump({ 'user_name': self.douban.user_name, 'user_id': self.douban.user_id, 'expire': self.douban.expire, 'token': self.douban.token }, f) json.dump(self.channels, f2) except IOError: raise Exception("Unable to write cache file") def get_channels(self): if self.channels is None: self.channels = self.douban.get_channels() return self.channels def _choose_channel(self, channel): self.current_channel = channel self.current_play_list = deque(self.douban.get_new_play_list(self.current_channel)) def _play_track(self): _song = self.current_play_list.popleft() self.current_song = Song(_song) self.song_change_alarm = self.main_loop.set_alarm_in(self.current_song.length_in_sec, self.next_song, None); self.selected_button.set_text(self.selected_button.text[0:7].strip()) heart = u'\N{WHITE HEART SUIT}'; if self.current_song.like: heart = u'\N{BLACK HEART SUIT}' self.selected_button.set_text(self.selected_button.text + ' ' + heart + ' ' + self.current_song.artist + ' - ' + self.current_song.title) if self.scrobbling: self.scrobbler.now_playing(self.current_song.artist, self.current_song.title, self.current_song.album_title, self.current_song.length_in_sec) self.player.stop() self.player.play(self.current_song) # Currently playing the second last song in queue if len(self.current_play_list) == 1: playing_list = self.douban.get_playing_list(self.current_song.sid, self.current_channel) self.current_play_list.extend(deque(playing_list)) def next_song(self, loop, user_data): # Scrobble the track if scrobbling is enabled # and total playback time of the track > 30s if self.scrobbling and self.current_song.length_in_sec > 30: self.scrobbler.submit(self.current_song.artist, self.current_song.title, self.current_song.album_title, self.current_song.length_in_sec) self.douban.end_song(self.current_song.sid, self.current_channel) if self.song_change_alarm: self.main_loop.remove_alarm(self.song_change_alarm) self._play_track() def skip_current_song(self): self.douban.skip_song(self.current_song.sid, self.current_channel) if self.song_change_alarm: self.main_loop.remove_alarm(self.song_change_alarm) self._play_track() def rate_current_song(self): self.douban.rate_song(self.current_song.sid, self.current_channel) self.selected_button.set_text(self.selected_button.text.replace(u'\N{WHITE HEART SUIT}', u'\N{BLACK HEART SUIT}')) def unrate_current_song(self): self.douban.unrate_song(self.current_song.sid, self.current_channel) self.selected_button.set_text(self.selected_button.text.replace(u'\N{BLACK HEART SUIT}', u'\N{WHITE HEART SUIT}')) def quit(self): self.player.stop() def start(self): title = urwid.AttrMap(urwid.Text('豆瓣FM'), 'title') divider = urwid.Divider() pile = urwid.Padding(urwid.Pile([divider, title, divider]), left=4, right=4) box = urwid.Padding(self.ChannelListBox(), left=2, right=4) frame = urwid.Frame(box, header=pile, footer=divider) self.main_loop = urwid.MainLoop(frame, self.palette, handle_mouse=False) self.main_loop.run() def ChannelListBox(self): body = [] for c in self.channels: _channel = ChannelButton(c['name']) urwid.connect_signal(_channel, 'click', self.channel_chosen, c['channel_id']) body.append(urwid.AttrMap(_channel, None, focus_map="channel")) return MyListBox(urwid.SimpleFocusListWalker(body), self) def channel_chosen(self, button, choice): # Choose the channel which is playing right now # ignore this if self.selected_button == button: return # Choose a different channel if self.player.is_playing: self.player.stop() self._choose_channel(choice) if self.selected_button != None and button != self.selected_button: self.selected_button.set_text(self.selected_button.text[0:7].strip()) self.selected_button = button self._play_track()
class traktService: scrobbler = None watcher = None syncThread = None dispatchQueue = Queue.Queue() _interval = 10 * 60 # how often to send watching call def __init__(self): threading.Thread.name = 'trakt' def _dispatchQueue(self, data): utilities.Debug("Queuing for dispatch: %s" % data) self.dispatchQueue.put(data) def _dispatch(self, data): utilities.Debug("Dispatch: %s" % data) action = data['action'] if action == 'started': del data['action'] self.scrobbler.playbackStarted(data) self.watcher = threading.Timer(self._interval, self.doWatching) self.watcher.name = "trakt-watching" self.watcher.start() elif action == 'ended' or action == 'stopped': self.scrobbler.playbackEnded() if self.watcher: if self.watcher.isAlive(): self.watcher.cancel() self.watcher = None elif action == 'paused': self.scrobbler.playbackPaused() elif action == 'resumed': self.scrobbler.playbackResumed() elif action == 'seek' or action == 'seekchapter': self.scrobbler.playbackSeek() elif action == 'databaseUpdated': self.doSync() elif action == 'scanStarted': pass elif action == 'settingsChanged': utilities.Debug("Settings changed, reloading.") globals.traktapi.updateSettings() else: utilities.Debug("Unknown dispatch action, '%s'." % action) def run(self): startup_delay = utilities.getSettingAsInt('startup_delay') if startup_delay: utilities.Debug("Delaying startup by %d seconds." % startup_delay) xbmc.sleep(startup_delay * 1000) utilities.Debug("Service thread starting.") # clear any left over properties utilities.clearProperty('traktManualSync') utilities.clearProperty('traktManualRateData') utilities.clearProperty('traktManualRate') # 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 not self.dispatchQueue.empty() and (not xbmc.abortRequested): data = self.dispatchQueue.get() utilities.Debug("Queued dispatch: %s" % data) self._dispatch(data) # check if we were tasked to do a manual sync if utilities.getPropertyAsBool('traktManualSync'): if not self.syncThread.isAlive(): utilities.Debug("Performing a manual sync.") self.doSync(manual=True) else: utilities.Debug("There already is a sync in progress.") utilities.clearProperty('traktManualSync') # check if we were tasked to do a manual rating if utilities.getPropertyAsBool('traktManualRate'): self.doManualRating() utilities.clearProperty('traktManualRateData') utilities.clearProperty('traktManualRate') if xbmc.Player().isPlayingVideo(): self.scrobbler.update() xbmc.sleep(500) # we are shutting down utilities.Debug("Beginning shut down.") # check if watcher is set and active, if so, cancel it. if self.watcher: if self.watcher.isAlive(): self.watcher.cancel() # 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 doWatching(self): # check if we're still playing a video if not xbmc.Player().isPlayingVideo(): self.watcher = None return # call watching method self.scrobbler.watching() # start a new timer thread self.watcher = threading.Timer(self._interval, self.doWatching) self.watcher.name = "trakt-watching" self.watcher.start() def doManualRating(self): data = json.loads(utilities.getProperty('traktManualRateData')) action = data['action'] media_type = data['media_type'] summaryInfo = None if not utilities.isValidMediaType(media_type): utilities.Debug("doManualRating(): Invalid media type '%s' passed for manual %s." % (media_type, action)) return if not data['action'] in ['rate', 'unrate']: utilities.Debug("doManualRating(): Unknown action passed.") return if 'dbid' in data: utilities.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: utilities.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: utilities.Debug("Getting data for manual %s of non-library '%s' with ID of '%s'" % (action, media_type, data['remoteid'])) if utilities.isEpisode(media_type): result = {} if 'dbid' in data: result = utilities.getEpisodeDetailsFromXbmc(data['dbid'], ['showtitle', 'season', 'episode', 'tvshowid']) if not result: utilities.Debug("doManualRating(): No data was returned from XBMC, aborting manual %s." % action) return else: result['tvdb_id'] = data['remoteid'] result['season'] = data['season'] result['episode'] = data['episode'] summaryInfo = globals.traktapi.getEpisodeSummary(result['tvdb_id'], result['season'], result['episode']) elif utilities.isShow(media_type): result = {} if 'dbid' in data: result = utilities.getShowDetailsFromXBMC(data['dbid'], ['imdbnumber']) if not result: utilities.Debug("doManualRating(): No data was returned from XBMC, aborting manual %s." % action) return else: result['imdbnumber'] = data['remoteid'] summaryInfo = globals.traktapi.getShowSummary(result['imdbnumber']) elif utilities.isMovie(media_type): result = {} if 'dbid' in data: result = utilities.getMovieDetailsFromXbmc(data['dbid'], ['imdbnumber', 'title', 'year']) if not result: utilities.Debug("doManualRating(): No data was returned from XBMC, aborting manual %s." % action) return else: result['imdbnumber'] = data['remoteid'] summaryInfo = globals.traktapi.getMovieSummary(result['imdbnumber']) if not summaryInfo is None: if action == 'rate': if not 'rating' in data: rateMedia(media_type, summaryInfo) else: rateMedia(media_type, summaryInfo, rating=data['rating']) elif action == 'unrate': rateMedia(media_type, summaryInfo, unrate=True) else: utilities.Debug("doManualRating(): Summary info was empty, possible problem retrieving data from trakt.tv") def doSync(self, manual=False): self.syncThread = syncThread(manual) self.syncThread.start()
from pylab import * if __name__ == "__main__": limits = { 'NA': (3000, 'orange', 'North America'), 'EU': (10000, 'blue', 'Europe'), 'RU': (30000, 'red', 'Russia'), 'ASIA': (1000, 'green', 'Asia'), 'KR': (500, 'purple', 'Korea'), } rc('font', **{'family':'serif'}) for server in ['EU', 'NA', 'RU', 'ASIA', 'KR']: print server # scrob = Scrobbler(min_points=5000, max_pages=90, max_rank=90000, max_players=90000) scrob = Scrobbler(min_points=5000, max_pages=90, max_rank=90000, max_players=90000) scrob.page_size = 1000 scrob.build_headers(server) try: data = scrob.scrobble() x = [] y = [] for player in scrob.ranking: x.append(scrob.players[player[0]]['real_position']) y.append(player[1]) if scrob.players[player[0]]['real_position'] == limits[server][0]: cutoffx = [limits[server][0], limits[server][0]] cutoffy = [0, player[1]]
class Doubanfm(object): def __init__(self): self.login_data = {} self.lastfm = True # lastfm 登陆 def init_login(self): print LOGO self.douban_login() # 登陆 self.lastfm_login() # 登陆 last.fm print '\033[31m♥\033[0m Get channels ', self.get_channels() # 获取频道列表 print '[\033[32m OK \033[0m]' # 存储的default_channel是行数而不是真正发送数据的channel_id # 这里需要进行转化一下 self.set_channel(self.default_channel) print '\033[31m♥\033[0m Check PRO ', # self.is_pro() print '[\033[32m OK \033[0m]' def win_login(self): '''登陆界面''' email = raw_input('Email: ') password = getpass.getpass('Password: '******'''Last.fm登陆''' # username & password self.last_fm_username = \ self.login_data['last_fm_username'] if 'last_fm_username' in self.login_data\ else None self.last_fm_password = \ self.login_data['last_fm_password'] if 'last_fm_password' in self.login_data\ else None if len(sys.argv) > 1 and sys.argv[1] == 'last.fm': from hashlib import md5 username = raw_input('Last.fm username: '******'Last.fm password :'******'r') as f: data = pickle.load(f) with open(config.PATH_TOKEN, 'w') as f: data['last_fm_username'] = username data['last_fm_password'] = self.last_fm_password pickle.dump(data, f) # login if self.lastfm and self.last_fm_username and self.last_fm_password: self.scrobbler = Scrobbler( self.last_fm_username, self.last_fm_password) r, err = self.scrobbler.handshake() if r: logger.info("Last.fm login succeeds!") print '\033[31m♥\033[0m Last.fm logged in: %s' % self.last_fm_username else: logger.error("Last.fm login fails: " + err) self.lastfm = False else: self.lastfm = False def __last_fm_account_required(func): '''装饰器,用于需要登录Last.fm后才能使用的接口''' @wraps(func) def wrapper(self, *args, **kwds): if not self.lastfm: return # Disable pylint callable check due to pylint's incompability # with using a class method as decorator. # Pylint will consider func as "self" return func(self, *args, **kwds) # pylint: disable=not-callable return wrapper @__last_fm_account_required def submit_current_song(self): '''提交播放过的曲目''' # Submit the track if total playback time of the track > 30s if self.playingsong['length'] > 30: self.scrobbler.submit( self.playingsong['artist'], self.playingsong['title'], self.playingsong['albumtitle'], self.playingsong['length'] ) @__last_fm_account_required def scrobble_now_playing(self): '''提交当前正在播放曲目''' self.scrobbler.now_playing( self.playingsong['artist'], self.playingsong['title'], self.playingsong['albumtitle'], self.playingsong['length'] ) def douban_login(self): '''登陆douban.fm获取token''' if os.path.exists(config.PATH_TOKEN): # 已登陆 logger.info("Found existing Douban.fm token.") with open(config.PATH_TOKEN, 'r') as f: self.login_data = pickle.load(f) self.token = self.login_data['token'] self.user_name = self.login_data['user_name'] self.user_id = self.login_data['user_id'] self.expire = self.login_data['expire'] self.default_volume = int(self.login_data['volume'])\ if 'volume' in self.login_data else 50 # Value stored in login_data in token file is lien number # instead of channel_id! Will do set_channel later. self.default_channel = int(self.login_data['channel'])\ if 'channel' in self.login_data else 0 print '\033[31m♥\033[0m Get local token - Username: \033[33m%s\033[0m' %\ self.user_name else: # 未登陆 logger.info('First time logging in Douban.fm.') while True: self.email, self.password = self.win_login() login_data = { 'app_name': 'radio_desktop_win', 'version': '100', 'email': self.email, 'password': self.password } s = requests.post('http://www.douban.com/j/app/login', login_data) dic = json.loads(s.text, object_hook=_decode_dict) if dic['r'] == 1: logger.debug(dic['err']) continue else: self.token = dic['token'] self.user_name = dic['user_name'] self.user_id = dic['user_id'] self.expire = dic['expire'] self.default_volume = 50 self.default_channel = 1 self.login_data = { 'app_name': 'radio_desktop_win', 'version': '100', 'user_id': self.user_id, 'expire': self.expire, 'token': self.token, 'user_name': self.user_name, 'volume': '50', 'channel': '0' } logger.info('Logged in username: '******'w') as f: pickle.dump(self.login_data, f) logger.debug('Write data to ' + config.PATH_TOKEN) break # set config config.init_config() def get_channels(self): '''获取channel列表,将channel name/id存入self._channel_list''' # 红心兆赫需要手动添加 self._channel_list = [{ 'name': '红心兆赫', 'channel_id': -3 }] r = requests.get('http://www.douban.com/j/app/radio/channels') self._channel_list += json.loads(r.text, object_hook=_decode_dict)['channels'] @property def channels(self): '''返回channel名称列表(一个list,不包括id)''' # 格式化频道列表,以便display lines = [ch['name'] for ch in self._channel_list] return lines def requests_url(self, ptype, **data): '''这里包装了一个函数,发送post_data''' post_data = self.login_data.copy() post_data['type'] = ptype for x in data: post_data[x] = data[x] url = 'http://www.douban.com/j/app/radio/people?' + urllib.urlencode(post_data) try: s = requests.get(url) except requests.exceptions.RequestException: logger.error("Error communicating with Douban.fm API.") return s.text def set_channel(self, line): '''把行数转化成channel_id''' self.default_channel = line self.login_data['channel'] = self._channel_list[line]['channel_id'] def get_playlist(self): '''获取播放列表,返回一个list''' s = self.requests_url('n') return json.loads(s, object_hook=_decode_dict)['song'] def skip_song(self, playingsong): '''下一首,返回一个list''' s = self.requests_url('s', sid=playingsong['sid']) return json.loads(s, object_hook=_decode_dict)['song'] def bye(self, playingsong): '''不再播放,返回一个list''' s = self.requests_url('b', sid=playingsong['sid']) return json.loads(s, object_hook=_decode_dict)['song'] def rate_music(self, playingsong): '''标记喜欢歌曲''' self.requests_url('r', sid=playingsong['sid']) def unrate_music(self, playingsong): '''取消标记喜欢歌曲''' self.requests_url('u', sid=playingsong['sid']) def submit_music(self, playingsong): '''歌曲结束标记''' self.requests_url('e', sid=playingsong['sid']) def get_lrc(self, playingsong): '''获取歌词''' try: url = "http://api.douban.com/v2/fm/lyric" postdata = { 'sid': playingsong['sid'], 'ssid': playingsong['ssid'], } s = requests.session() response = s.post(url, data=postdata) lyric = json.loads(response.text, object_hook=_decode_dict) logger.debug(response.text) lrc_dic = lrc2dic.lrc2dict(lyric['lyric']) # 原歌词用的unicode,为了兼容 for key, value in lrc_dic.iteritems(): lrc_dic[key] = value.decode('utf-8') if lrc_dic: logger.debug('Get lyric success!') return lrc_dic except requests.exceptions.RequestException: logger.error('Get lyric failed!') return {}
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): 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 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 == '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': utilities.showSettings() else: logger.debug("Unknown dispatch action, '%s'." % action) except Exception as ex: message = utilities.createError(ex) logger.fatal(message) 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 self.Monitor.abortRequested(): if not utilities.getSetting('authorization'): last_reminder = utilities.getSettingAsInt('last_reminder') now = int(time.time()) if last_reminder >= 0 and last_reminder < now - (24 * 60 * 60): gui_utils.get_pin() while len(self.dispatchQueue) and ( not self.Monitor.abortRequested()): data = self.dispatchQueue.get() logger.debug("Queued dispatch: %s" % data) self._dispatch(data) if xbmc.Player().isPlayingVideo(): self.scrobbler.transitionCheck() if self.Monitor.waitForAbort(1): # Abort was requested while waiting. We should exit break # 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 logger.debug( "Getting data for manual %s of %s: video_id: |%s| dbid: |%s|" % (action, media_type, data.get('video_id'), data.get('dbid'))) id_type = utilities.parseIdToTraktIds(str(data['video_id']), media_type)[1] if not id_type: logger.debug("doManualRating(): Unrecognized id_type: |%s|-|%s|." % (media_type, data['video_id'])) return ids = globals.traktapi.getIdLookup(data['video_id'], id_type) if not ids: logger.debug("doManualRating(): No Results for: |%s|-|%s|." % (media_type, data['video_id'])) return trakt_id = dict(ids[0].keys)['trakt'] if utilities.isEpisode(media_type): summaryInfo = globals.traktapi.getEpisodeSummary( trakt_id, data['season'], data['episode']) userInfo = globals.traktapi.getEpisodeRatingForUser( trakt_id, data['season'], data['episode'], 'trakt') elif utilities.isSeason(media_type): summaryInfo = globals.traktapi.getShowSummary(trakt_id) userInfo = globals.traktapi.getSeasonRatingForUser( trakt_id, data['season'], 'trakt') elif utilities.isShow(media_type): summaryInfo = globals.traktapi.getShowSummary(trakt_id) userInfo = globals.traktapi.getShowRatingForUser(trakt_id, 'trakt') elif utilities.isMovie(media_type): summaryInfo = globals.traktapi.getMovieSummary(trakt_id) userInfo = globals.traktapi.getMovieRatingForUser( trakt_id, 'trakt') if summaryInfo is not None: summaryInfo = summaryInfo.to_dict() summaryInfo['user'] = {'ratings': userInfo} if utilities.isEpisode(media_type): summaryInfo['season'] = data['season'] summaryInfo['number'] = data['episode'] summaryInfo['episodeid'] = data.get('dbid') elif utilities.isSeason(media_type): summaryInfo['season'] = data['season'] elif utilities.isMovie(media_type): summaryInfo['movieid'] = data.get('dbid') elif utilities.isShow(media_type): summaryInfo['tvshowid'] = data.get('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 doAddToWatchlist(self, data): media_type = data['media_type'] if utilities.isMovie(media_type): summaryInfo = globals.traktapi.getMovieSummary( data['id']).to_dict() if summaryInfo: s = utilities.getFormattedItemName(media_type, summaryInfo) logger.debug( "doAddToWatchlist(): '%s' trying to add to users watchlist." % s) params = {'movies': [summaryInfo]} logger.debug("doAddToWatchlist(): %s" % str(params)) result = globals.traktapi.addToWatchlist(params) if result: utilities.notification(utilities.getString(32165), s) else: utilities.notification(utilities.getString(32166), s) elif utilities.isEpisode(media_type): summaryInfo = { 'shows': [{ 'ids': utilities.parseIdToTraktIds(data['id'], media_type)[0], 'seasons': [{ 'number': data['season'], 'episodes': [{ 'number': data['number'] }] }] }] } logger.debug("doAddToWatchlist(): %s" % str(summaryInfo)) s = utilities.getFormattedItemName(media_type, data) result = globals.traktapi.addToWatchlist(summaryInfo) if result: utilities.notification(utilities.getString(32165), s) else: utilities.notification(utilities.getString(32166), s) elif utilities.isSeason(media_type): summaryInfo = { 'shows': [{ 'ids': utilities.parseIdToTraktIds(data['id'], media_type)[0], 'seasons': [{ 'number': data['season'] }] }] } s = utilities.getFormattedItemName(media_type, data) logger.debug( "doAddToWatchlist(): '%s - Season %d' trying to add to users watchlist." % (data['id'], data['season'])) result = globals.traktapi.addToWatchlist(summaryInfo) if result: utilities.notification(utilities.getString(32165), s) else: utilities.notification(utilities.getString(32166), s) elif utilities.isShow(media_type): summaryInfo = { 'shows': [{ 'ids': utilities.parseIdToTraktIds(data['id'], media_type)[0] }] } s = utilities.getFormattedItemName(media_type, data) logger.debug("doAddToWatchlist(): %s" % str(summaryInfo)) result = globals.traktapi.addToWatchlist(summaryInfo) if result: utilities.notification(utilities.getString(32165), s) else: utilities.notification(utilities.getString(32166), s) def doMarkWatched(self, data): media_type = data['media_type'] if utilities.isMovie(media_type): summaryInfo = globals.traktapi.getMovieSummary( data['id']).to_dict() 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) params = {'movies': [summaryInfo]} logger.debug("doMarkWatched(): %s" % str(params)) result = globals.traktapi.addToHistory(params) if result: utilities.notification(utilities.getString(32113), s) else: utilities.notification(utilities.getString(32114), s) elif utilities.isEpisode(media_type): summaryInfo = { 'shows': [{ 'ids': utilities.parseIdToTraktIds(data['id'], media_type)[0], 'seasons': [{ 'number': data['season'], 'episodes': [{ 'number': data['number'] }] }] }] } logger.debug("doMarkWatched(): %s" % str(summaryInfo)) s = utilities.getFormattedItemName(media_type, data) result = globals.traktapi.addToHistory(summaryInfo) if result: utilities.notification(utilities.getString(32113), s) else: utilities.notification(utilities.getString(32114), s) elif utilities.isSeason(media_type): summaryInfo = { 'shows': [{ 'ids': utilities.parseIdToTraktIds(data['id'], media_type)[0], 'seasons': [{ 'number': data['season'], 'episodes': [] }] }] } s = utilities.getFormattedItemName(media_type, data) for ep in data['episodes']: summaryInfo['shows'][0]['seasons'][0]['episodes'].append( {'number': ep}) logger.debug( "doMarkWatched(): '%s - Season %d' has %d episode(s) that are going to be marked as watched." % (data['id'], data['season'], len(summaryInfo['shows'][0]['seasons'][0]['episodes']))) if len(summaryInfo['shows'][0]['seasons'][0]['episodes']) > 0: logger.debug("doMarkWatched(): %s" % str(summaryInfo)) result = globals.traktapi.addToHistory(summaryInfo) if result: utilities.notification( utilities.getString(32113), utilities.getString(32115) % (result['added']['episodes'], s)) else: utilities.notification(utilities.getString(32114), s) elif utilities.isShow(media_type): summaryInfo = { 'shows': [{ 'ids': utilities.parseIdToTraktIds(data['id'], media_type)[0], 'seasons': [] }] } if summaryInfo: s = utilities.getFormattedItemName(media_type, data) logger.debug('data: %s' % data) for season in data['seasons']: episodeJson = [] for episode in data['seasons'][season]: episodeJson.append({'number': episode}) summaryInfo['shows'][0]['seasons'].append({ 'number': season, 'episodes': episodeJson }) if len(summaryInfo['shows'][0]['seasons'][0]['episodes']) > 0: logger.debug("doMarkWatched(): %s" % str(summaryInfo)) result = globals.traktapi.addToHistory(summaryInfo) if result: utilities.notification( utilities.getString(32113), utilities.getString(32115) % (result['added']['episodes'], s)) else: utilities.notification(utilities.getString(32114), s) def doSync(self, manual=False, silent=False, library="all"): self.syncThread = syncThread(manual, silent, library) self.syncThread.start()
class NotificationService: _scrobbler = None def __init__(self): self.run() def _dispatch(self, data): Debug("[Notification] Dispatch: %s" % data) xbmc.sleep(500) # check if scrobbler thread is still alive if not self._scrobbler.isAlive(): if self.Player._playing and not self._scrobbler.pinging: # make sure pinging is set self._scrobbler.pinging = True Debug("[Notification] Scrobler thread died, restarting.") self._scrobbler.start() action = data["action"] if action == "started": del data["action"] p = {"item": data} self._scrobbler.playbackStarted(p) 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 do_sync('movies'): movies = SyncMovies(show_progress=False, api=globals.traktapi) movies.Run() if do_sync('episodes'): episodes = SyncEpisodes(show_progress=False, api=globals.traktapi) episodes.Run() elif action == "scanStarted": pass elif action == "settingsChanged": Debug("[Notification] Settings changed, reloading.") globals.traktapi.updateSettings() else: Debug("[Notification] '%s' unknown dispatch action!" % action) def run(self): Debug("[Notification] Starting") # setup event driven classes self.Player = traktPlayer(action = self._dispatch) self.Monitor = traktMonitor(action = self._dispatch) # init traktapi class globals.traktapi = traktAPI() # initalize scrobbler class self._scrobbler = Scrobbler(globals.traktapi) # start loop for events while (not xbmc.abortRequested): xbmc.sleep(500) # we aborted if xbmc.abortRequested: Debug("[Notification] abortRequested received, shutting down.") # delete player/monitor del self.Player del self.Monitor # join scrobbler, to wait for termination Debug("[Notification] Joining scrobbler thread to wait for exit.") self._scrobbler.join()
class NotificationService(threading.Thread): """ Receives XBMC notifications and passes them off as needed """ TELNET_ADDRESS = 'localhost' TELNET_PORT = 9090 _abortRequested = False _scrobbler = None _notificationBuffer = "" def _forward(self, notification): """ Fowards the notification recieved to a function on the scrobbler """ if not ('method' in notification and 'params' in notification and 'sender' in notification['params'] and notification['params']['sender'] == 'xbmc'): return if notification['method'] == 'Player.OnStop': self._scrobbler.playbackEnded() elif notification['method'] == 'Player.OnPlay': if 'data' in notification['params'] and 'item' in notification['params']['data'] and 'type' in notification['params']['data']['item']: self._scrobbler.playbackStarted(notification['params']['data']) elif notification['method'] == 'Player.OnPause': self._scrobbler.playbackPaused() elif notification['method'] == 'VideoLibrary.OnScanFinished': if do_sync('movies'): movies = SyncMovies(show_progress=False) movies.Run() if do_sync('episodes'): episodes = SyncEpisodes(show_progress=False) episodes.Run() elif notification['method'] == 'System.OnQuit': self._abortRequested = True def _readNotification(self, telnet): """ Read a notification from the telnet connection, blocks until the data is available, or else raises an EOFError if the connection is lost """ while True: try: addbuffer = telnet.read_some() except socket.timeout: continue if addbuffer == "": raise EOFError self._notificationBuffer += addbuffer try: data, offset = json.JSONDecoder().raw_decode(self._notificationBuffer) self._notificationBuffer = self._notificationBuffer[offset:] except ValueError: continue return data def run(self): #while xbmc is running self._scrobbler = Scrobbler() self._scrobbler.start() while not (self._abortRequested or xbmc.abortRequested): try: #try to connect, catch errors and retry every 5 seconds telnet = telnetlib.Telnet(self.TELNET_ADDRESS, self.TELNET_PORT) #if connection succeeds while not (self._abortRequested or xbmc.abortRequested): try: #read notification data data = self._readNotification(telnet) Debug("[Notification Service] message: " + str(data)) self._forward(data) except EOFError: #if we end up here, it means the connection was lost or reset, # so we empty out the buffer, and exit this loop, which retries # the connection in the outer loop self._notificationBuffer = "" break except: time.sleep(5) continue telnet.close() self._scrobbler.abortRequested = True Debug("Notification service stopping")
def run(self): #while xbmc is running scrobbler = Scrobbler() scrobbler.start() while (not (self.abortRequested or xbmc.abortRequested)): time.sleep(1) try: tn = telnetlib.Telnet('localhost', 9090, 10) except IOError as (errno, strerror): #connection failed, try again soon Debug("[Notification Service] Telnet too soon? ("+str(errno)+") "+strerror) time.sleep(1) continue Debug("[Notification Service] Waiting~"); bCount = 0 while (not (self.abortRequested or xbmc.abortRequested)): try: if bCount == 0: notification = "" inString = False [index, match, raw] = tn.expect(["(\\\\)|(\\\")|[{\"}]"], 0.2) #note, pre-compiled regex might be faster here notification += raw if index == -1: # Timeout continue if index == 0: # Found escaped quote match = match.group(0) if match == "\"": inString = not inString continue if match == "{": bCount += 1 if match == "}": bCount -= 1 if bCount > 0: continue if bCount < 0: bCount = 0 except EOFError: break #go out to the other loop to restart the connection Debug("[Notification Service] message: " + str(notification)) # Parse recieved notification data = json.loads(notification) # Forward notification to functions if 'method' in data and 'params' in data and 'sender' in data['params'] and data['params']['sender'] == 'xbmc': if data['method'] == 'Player.OnStop': Debug("pb ended, %s" % getSync_after_x()) scrobbler.playbackEnded(data=data) # this is using syncIncreasePlayCount already elif data['method'] == 'Player.OnPlay': if 'data' in data['params'] and 'item' in data['params']['data'] and 'id' in data['params']['data']['item'] and 'type' in data['params']['data']['item']: #fixme: look here for current position? scrobbler.playbackStarted(data['params']['data']) elif data['method'] == 'Player.OnPause': scrobbler.playbackPaused() elif data['method'] == 'VideoLibrary.OnUpdate': Debug("OnUpdate %s" % data) if 'data' in data['params'] and 'playcount' in data['params']['data']: noBugging = getNoBugging() # 'playcount' in data indicates that playcount has changed. so we received a seen status on the item if getSync_after_x() and data['params']['data']["playcount"] >= 1: # we've played a file and consider it seen syncIncreasePlayCount() Debug("syncing, playcount: %s" % data['params']['data']["playcount"]) syncAfterX(daemon=noBugging) onlyOnUnwatchMark = getInstantOnlyOnUnwatchMark() Debug("instantUpdateOnWatchMark: %s, %s" % (getInstantUpdateOnWatchMark(), onlyOnUnwatchMark)) if (getInstantUpdateOnWatchMark() and not onlyOnUnwatchMark) or (data['params']['data']["playcount"] == 0 and onlyOnUnwatchMark): instantSyncPlayCount(data) elif data['method'] == 'System.OnQuit': self.abortRequested = True
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): 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 run(self): startup_delay = kodiUtilities.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) AddonSignals.registerSlot('service.nextup.notification', 'NEXTUPWATCHEDSIGNAL', self.callback) # start loop for events while not self.Monitor.abortRequested(): while len(self.dispatchQueue) and (not self.Monitor.abortRequested()): data = self.dispatchQueue.get() logger.debug("Queued dispatch: %s" % data) self._dispatch(data) if xbmc.Player().isPlayingVideo(): self.scrobbler.transitionCheck() if self.Monitor.waitForAbort(1): # Abort was requested while waiting. We should exit break # 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 logger.debug("Getting data for manual %s of %s: video_id: |%s| dbid: |%s|" % (action, media_type, data.get('video_id'), data.get('dbid'))) id_type = utilities.parseIdToTraktIds(str(data['video_id']), media_type)[1] if not id_type: logger.debug("doManualRating(): Unrecognized id_type: |%s|-|%s|." % (media_type, data['video_id'])) return ids = globals.traktapi.getIdLookup(data['video_id'], id_type) if not ids: logger.debug("doManualRating(): No Results for: |%s|-|%s|." % (media_type, data['video_id'])) return trakt_id = dict(ids[0].keys)['trakt'] if utilities.isEpisode(media_type): summaryInfo = globals.traktapi.getEpisodeSummary(trakt_id, data['season'], data['episode']) userInfo = globals.traktapi.getEpisodeRatingForUser(trakt_id, data['season'], data['episode'], 'trakt') elif utilities.isSeason(media_type): summaryInfo = globals.traktapi.getShowSummary(trakt_id) userInfo = globals.traktapi.getSeasonRatingForUser(trakt_id, data['season'], 'trakt') elif utilities.isShow(media_type): summaryInfo = globals.traktapi.getShowSummary(trakt_id) userInfo = globals.traktapi.getShowRatingForUser(trakt_id, 'trakt') elif utilities.isMovie(media_type): summaryInfo = globals.traktapi.getMovieSummary(trakt_id) userInfo = globals.traktapi.getMovieRatingForUser(trakt_id, 'trakt') if summaryInfo is not None: summaryInfo = summaryInfo.to_dict() summaryInfo['user'] = {'ratings': userInfo} if utilities.isEpisode(media_type): summaryInfo['season'] = data['season'] summaryInfo['number'] = data['episode'] summaryInfo['episodeid'] = data.get('dbid') elif utilities.isSeason(media_type): summaryInfo['season'] = data['season'] elif utilities.isMovie(media_type): summaryInfo['movieid'] = data.get('dbid') elif utilities.isShow(media_type): summaryInfo['tvshowid'] = data.get('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 doAddToWatchlist(self, data): media_type = data['media_type'] if utilities.isMovie(media_type): summaryInfo = globals.traktapi.getMovieSummary(data['id']).to_dict() if summaryInfo: s = utilities.getFormattedItemName(media_type, summaryInfo) logger.debug("doAddToWatchlist(): '%s' trying to add to users watchlist." % s) params = {'movies': [summaryInfo]} logger.debug("doAddToWatchlist(): %s" % str(params)) result = globals.traktapi.addToWatchlist(params) if result: kodiUtilities.notification(kodiUtilities.getString(32165), s) else: kodiUtilities.notification(kodiUtilities.getString(32166), s) elif utilities.isEpisode(media_type): summaryInfo = {'shows': [{'ids': utilities.parseIdToTraktIds(data['id'], media_type)[0], 'seasons': [{'number': data['season'], 'episodes': [{'number':data['number']}]}]}]} logger.debug("doAddToWatchlist(): %s" % str(summaryInfo)) s = utilities.getFormattedItemName(media_type, data) result = globals.traktapi.addToWatchlist(summaryInfo) if result: kodiUtilities.notification(kodiUtilities.getString(32165), s) else: kodiUtilities.notification(kodiUtilities.getString(32166), s) elif utilities.isSeason(media_type): summaryInfo = {'shows': [{'ids': utilities.parseIdToTraktIds(data['id'], media_type)[0], 'seasons': [{'number': data['season']}]}]} s = utilities.getFormattedItemName(media_type, data) logger.debug("doAddToWatchlist(): '%s - Season %d' trying to add to users watchlist." % (data['id'], data['season'])) result = globals.traktapi.addToWatchlist(summaryInfo) if result: kodiUtilities.notification(kodiUtilities.getString(32165), s) else: kodiUtilities.notification(kodiUtilities.getString(32166), s) elif utilities.isShow(media_type): summaryInfo = {'shows': [{'ids': utilities.parseIdToTraktIds(data['id'], media_type)[0]}]} s = utilities.getFormattedItemName(media_type, data) logger.debug("doAddToWatchlist(): %s" % str(summaryInfo)) result = globals.traktapi.addToWatchlist(summaryInfo) if result: kodiUtilities.notification(kodiUtilities.getString(32165), s) else: kodiUtilities.notification(kodiUtilities.getString(32166), s) def doMarkWatched(self, data): media_type = data['media_type'] if utilities.isMovie(media_type): summaryInfo = globals.traktapi.getMovieSummary(data['id']).to_dict() 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) params = {'movies': [summaryInfo]} logger.debug("doMarkWatched(): %s" % str(params)) result = globals.traktapi.addToHistory(params) if result: kodiUtilities.notification(kodiUtilities.getString(32113), s) else: kodiUtilities.notification(kodiUtilities.getString(32114), s) elif utilities.isEpisode(media_type): summaryInfo = {'shows': [{'ids':utilities.parseIdToTraktIds(data['id'],media_type)[0], 'seasons': [{'number': data['season'], 'episodes': [{'number':data['number']}]}]}]} logger.debug("doMarkWatched(): %s" % str(summaryInfo)) s = utilities.getFormattedItemName(media_type, data) result = globals.traktapi.addToHistory(summaryInfo) if result: kodiUtilities.notification(kodiUtilities.getString(32113), s) else: kodiUtilities.notification(kodiUtilities.getString(32114), s) elif utilities.isSeason(media_type): summaryInfo = {'shows': [{'ids':utilities.parseIdToTraktIds(data['id'],media_type)[0], 'seasons': [{'number': data['season'], 'episodes': []}]}]} s = utilities.getFormattedItemName(media_type, data) for ep in data['episodes']: summaryInfo['shows'][0]['seasons'][0]['episodes'].append({'number': ep}) logger.debug("doMarkWatched(): '%s - Season %d' has %d episode(s) that are going to be marked as watched." % (data['id'], data['season'], len(summaryInfo['shows'][0]['seasons'][0]['episodes']))) self.addEpisodesToHistory(summaryInfo, s) elif utilities.isShow(media_type): summaryInfo = {'shows': [{'ids':utilities.parseIdToTraktIds(data['id'],media_type)[0], 'seasons': []}]} if summaryInfo: s = utilities.getFormattedItemName(media_type, data) logger.debug('data: %s' % data) for season in data['seasons']: episodeJson = [] for episode in data['seasons'][season]: episodeJson.append({'number': episode}) summaryInfo['shows'][0]['seasons'].append({'number': season, 'episodes': episodeJson}) self.addEpisodesToHistory(summaryInfo, s) def addEpisodesToHistory(self, summaryInfo, s): if len(summaryInfo['shows'][0]['seasons'][0]['episodes']) > 0: logger.debug("doMarkWatched(): %s" % str(summaryInfo)) result = globals.traktapi.addToHistory(summaryInfo) if result: kodiUtilities.notification(kodiUtilities.getString(32113), kodiUtilities.getString(32115) % (result['added']['episodes'], s)) else: kodiUtilities.notification(kodiUtilities.getString(32114), s) def doSync(self, manual=False, silent=False, library="all"): self.syncThread = syncThread(manual, silent, library) self.syncThread.start() def callback(self, data): logger.debug('Callback received - Nextup skipped to the next episode') self.scrobbler.playbackEnded()
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): 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 == '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) except Exception as ex: message = utilities.createError(ex) logger.fatal(message) 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()