def chooseServer(): """ Lets user choose from list of PMS """ log.info("Choosing PMS server requested, starting") import initialsetup setup = initialsetup.InitialSetup() server = setup.pick_pms(showDialog=True) if server is None: log.error('We did not connect to a new PMS, aborting') plex_command('SUSPEND_USER_CLIENT', 'False') plex_command('SUSPEND_LIBRARY_THREAD', 'False') return log.info("User chose server %s" % server['name']) setup.write_pms_to_settings(server) if not __LogOut(): return from utils import wipe_database # Wipe Kodi and Plex database as well as playlists and video nodes wipe_database() # Log in again __LogIn() log.info("Choosing new PMS complete") # '<PMS> connected' dialog('notification', lang(29999), '%s %s' % (server['name'], lang(39220)), icon='{plex}', time=3000, sound=False)
def _play(cls, data): item_ids = data['ItemIds'] command = data['PlayCommand'] playlist_ = playlist.Playlist() if command == 'PlayNow': startat = data.get('StartPositionTicks', 0) playlist_.play_all(item_ids, startat) dialog(type_="notification", heading="{emby}", message="%s %s" % (len(item_ids), lang(33004)), icon="{emby}", sound=False) elif command == 'PlayNext': new_playlist = playlist_.modify_playlist(item_ids) dialog(type_="notification", heading="{emby}", message="%s %s" % (len(item_ids), lang(33005)), icon="{emby}", sound=False) player = xbmc.Player() if not player.isPlaying(): # Only start the playlist if nothing is playing player.play(new_playlist)
def texture_cache_sync(self): # This method will sync all Kodi artwork to textures13.db # and cache them locally. This takes diskspace! if not dialog(type_="yesno", heading="{emby}", line1=lang(33042)): return log.info("Doing Image Cache Sync") pdialog = xbmcgui.DialogProgress() pdialog.create(lang(29999), lang(33043)) # ask to rest all existing or not if dialog(type_="yesno", heading="{emby}", line1=lang(33044)): log.info("Resetting all cache data first") self.delete_cache() # Cache all entries in video DB self._cache_all_video_entries(pdialog) # Cache all entries in music DB self._cache_all_music_entries(pdialog) pdialog.update(100, "%s %s" % (lang(33046), len(self.image_cache_threads))) log.info("Waiting for all threads to exit") while len(self.image_cache_threads): for thread in self.image_cache_threads: if thread.is_finished: self.image_cache_threads.remove(thread) pdialog.update(100, "%s %s" % (lang(33046), len(self.image_cache_threads))) log.info("Waiting for all threads to exit: %s", len(self.image_cache_threads)) xbmc.sleep(500) pdialog.close()
def _startup(self): serverId = settings('serverId') if (serverId != None): serverId = hashlib.md5(serverId).hexdigest() ga = GoogleAnalytics() ga.sendEventData("Application", "Startup", serverId) try: ga.sendEventData("Version", "OS", platform.platform()) ga.sendEventData("Version", "Python", platform.python_version()) except Exception: pass # Start up events self.warn_auth = True username = self.userclient_thread.get_username() if settings('connectMsg') == "true" and username: # Get additional users add_users = settings('additionalUsers') if add_users: add_users = ", " + ", ".join(add_users.split(',')) dialog(type_="notification", heading="{emby}", message=("%s %s%s" % (lang(33000), username.decode('utf-8'), add_users.decode('utf-8'))), icon="{emby}", time=2000, sound=False) return True
def _startup(self): serverId = settings('serverId') if(serverId != None): serverId = hashlib.md5(serverId).hexdigest() ga = GoogleAnalytics() ga.sendEventData("Application", "Startup", serverId) try: ga.sendEventData("Version", "OS", platform.platform()) ga.sendEventData("Version", "Python", platform.python_version()) except Exception: pass # Start up events self.warn_auth = True username = self.userclient_thread.get_username() if settings('connectMsg') == "true" and username: # Get additional users add_users = settings('additionalUsers') if add_users: add_users = ", "+", ".join(add_users.split(',')) dialog(type_="notification", heading="{emby}", message=("%s %s%s" % (lang(33000), username.decode('utf-8'), add_users.decode('utf-8'))), icon="{emby}", time=2000, sound=False) return True
def _server_restarting(cls): if settings('supressRestartMsg') == "true": dialog(type_="notification", heading="{emby}", message=lang(33006), icon="{emby}") window('emby_online', value="false")
def _auto_pick_pms(self): """ Will try to pick PMS based on machineIdentifier saved in file settings but only once Returns server or None if unsuccessful """ https_updated = False checked_plex_tv = False server = None while True: if https_updated is False: serverlist = PF.discover_pms(self.plex_token) for item in serverlist: if item.get('machineIdentifier') == self.serverid: server = item if server is None: name = settings('plex_servername') LOG.warn( 'The PMS you have used before with a unique ' 'machineIdentifier of %s and name %s is ' 'offline', self.serverid, name) return chk = self._check_pms_connectivity(server) if chk == 504 and https_updated is False: # switch HTTPS to HTTP or vice-versa if server['scheme'] == 'https': server['scheme'] = 'http' else: server['scheme'] = 'https' https_updated = True continue if chk == 401: LOG.warn('Not yet authorized for Plex server %s', server['name']) if self.check_plex_tv_sign_in() is True: if checked_plex_tv is False: # Try again checked_plex_tv = True https_updated = False continue else: LOG.warn('Not authorized even though we are signed ' ' in to plex.tv correctly') dialog( 'ok', lang(29999), '%s %s' % (lang(39214), try_encode(server['name']))) return else: return # Problems connecting elif chk >= 400 or chk is False: LOG.warn('Problems connecting to server %s. chk is %s', server['name'], chk) return LOG.info('We found a server to automatically connect to: %s', server['name']) return server
def playback_init(plex_id, plex_type, playqueue): """ Playback setup if Kodi starts playing an item for the first time. """ LOG.info('Initializing PKC playback') xml = GetPlexMetadata(plex_id) try: xml[0].attrib except (IndexError, TypeError, AttributeError): LOG.error('Could not get a PMS xml for plex id %s', plex_id) # "Play error" dialog('notification', lang(29999), lang(30128), icon='{error}') return trailers = False api = API(xml[0]) if (plex_type == v.PLEX_TYPE_MOVIE and not api.getResume() and settings('enableCinema') == "true"): if settings('askCinema') == "true": # "Play trailers?" trailers = dialog('yesno', lang(29999), lang(33016)) trailers = True if trailers else False else: trailers = True LOG.debug('Playing trailers: %s', trailers) playqueue.clear() if plex_type != v.PLEX_TYPE_CLIP: # Post to the PMS to create a playqueue - in any case due to Companion xml = init_plex_playqueue(plex_id, xml.attrib.get('librarySectionUUID'), mediatype=plex_type, trailers=trailers) if xml is None: LOG.error('Could not get a playqueue xml for plex id %s, UUID %s', plex_id, xml.attrib.get('librarySectionUUID')) # "Play error" dialog('notification', lang(29999), lang(30128), icon='{error}') return # Should already be empty, but just in case PL.get_playlist_details_from_xml(playqueue, xml) stack = _prep_playlist_stack(xml) # Sleep a bit to let setResolvedUrl do its thing - bit ugly sleep(200) _process_stack(playqueue, stack) # Reset some playback variables state.CONTEXT_MENU_PLAY = False state.FORCE_TRANSCODE = False # New thread to release this one sooner (e.g. harddisk spinning up) thread = Thread(target=Player().play, args=(playqueue.kodi_pl, )) thread.setDaemon(True) LOG.info('Done initializing PKC playback, starting Kodi player') # By design, PKC will start Kodi playback using Player().play(). Kodi # caches paths like our plugin://pkc. If we use Player().play() between # 2 consecutive startups of exactly the same Kodi library item, Kodi's # cache will have been flushed for some reason. Hence the 2nd call for # plugin://pkc will be lost; Kodi will try to startup playback for an empty # path: log entry is "CGUIWindowVideoBase::OnPlayMedia <missing path>" thread.start()
def content_pop(self, name): # It's possible for the time to be 0. It should be considered disabled in this case. if not self.pdialog and self.content_msg and self.new_time: dialog(type_="notification", heading="{emby}", message="%s %s" % (lang(33049), name), icon="{emby}", time=self.new_time, sound=False)
def fullTextureCacheSync(self): """ This method will sync all Kodi artwork to textures13.db and cache them locally. This takes diskspace! """ if not dialog('yesno', "Image Texture Cache", lang(39250)): return log.info("Doing Image Cache Sync") # ask to rest all existing or not if dialog('yesno', "Image Texture Cache", lang(39251)): log.info("Resetting all cache data first") # Remove all existing textures first path = translatePath("special://thumbnails/") if exists(path): rmtree(path, ignore_errors=True) # remove all existing data from texture DB connection = kodiSQL('texture') cursor = connection.cursor() query = 'SELECT tbl_name FROM sqlite_master WHERE type=?' cursor.execute(query, ('table', )) rows = cursor.fetchall() for row in rows: tableName = row[0] if tableName != "version": query = "DELETE FROM ?" cursor.execute(query, (tableName, )) connection.commit() connection.close() # Cache all entries in video DB connection = kodiSQL('video') cursor = connection.cursor() # dont include actors query = "SELECT url FROM art WHERE media_type != ?" cursor.execute(query, ('actor', )) result = cursor.fetchall() total = len(result) log.info("Image cache sync about to process %s video images" % total) connection.close() for url in result: self.cacheTexture(url[0]) # Cache all entries in music DB connection = kodiSQL('music') cursor = connection.cursor() cursor.execute("SELECT url FROM art") result = cursor.fetchall() total = len(result) log.info("Image cache sync about to process %s music images" % total) connection.close() for url in result: self.cacheTexture(url[0])
def _delete_item(self): """ Delete item on PMS """ delete = True if settings('skipContextMenu') != "true": if not dialog("yesno", heading="{plex}", line1=lang(33041)): LOG.info("User skipped deletion for: %s", self.plex_id) delete = False if delete: LOG.info("Deleting Plex item with id %s", self.plex_id) if delete_item_from_pms(self.plex_id) is False: dialog("ok", heading="{plex}", line1=lang(30414))
def _delete_item(self): delete = True if settings('skipContextMenu') != "true": if not dialog("yesno", heading=lang(29999), line1=lang(33041)): log.info("User skipped deletion for: %s", self.item_id) delete = False if delete: log.info("Deleting Plex item with id %s", self.item_id) if delete_item_from_pms(self.item_id) is False: dialog("ok", heading="{plex}", line1=lang(30414))
def _delete_item(self): delete = True if settings('skipContextMenu') != "true": if not dialog(type_="yesno", heading=addonName, line1=lang(33041)): log.info("User skipped deletion for: %s", self.item_id) delete = False if delete: log.info("Deleting Plex item with id %s", self.item_id) if delete_item_from_pms(self.item_id) is False: dialog(type_="ok", heading="{plex}", line1=lang(30414))
def deviceid(self): deviceId_old = window('plex_client_Id') from clientinfo import getDeviceId try: deviceId = getDeviceId(reset=True) except Exception as e: log.error('Failed to generate a new device Id: %s' % e) dialog('ok', lang(29999), lang(33032)) else: log.info('Successfully removed old device ID: %s New deviceId:' '%s' % (deviceId_old, deviceId)) # 'Kodi will now restart to apply the changes' dialog('ok', lang(29999), lang(33033)) executebuiltin('RestartApp')
def _rate_song(self): conn = kodiSQL('music') cursor = conn.cursor() query = "SELECT rating FROM song WHERE idSong = ?" cursor.execute(query, (self.kodi_id, )) try: value = cursor.fetchone()[0] current_value = int(round(float(value), 0)) except TypeError: pass else: new_value = dialog("numeric", 0, lang(30411), str(current_value)) if new_value > -1: new_value = int(new_value) if new_value > 5: new_value = 5 if settings('enableUpdateSongRating') == "true": musicutils.updateRatingToFile(new_value, self.api.get_file_path()) query = "UPDATE song SET rating = ? WHERE idSong = ?" cursor.execute(query, ( new_value, self.kodi_id, )) conn.commit() finally: cursor.close()
def _rate_song(self): conn = kodiSQL('music') cursor = conn.cursor() query = "SELECT rating FROM song WHERE idSong = ?" cursor.execute(query, (self.kodi_id,)) try: value = cursor.fetchone()[0] current_value = int(round(float(value), 0)) except TypeError: pass else: new_value = dialog("numeric", 0, lang(30411), str(current_value)) if new_value > -1: new_value = int(new_value) if new_value > 5: new_value = 5 if settings('enableUpdateSongRating') == "true": musicutils.updateRatingToFile(new_value, self.api.get_file_path()) query = "UPDATE song SET rating = ? WHERE idSong = ?" cursor.execute(query, (new_value, self.kodi_id,)) conn.commit() finally: cursor.close()
def resetAuth(): # User tried login and failed too many times resp = dialog('yesno', heading="{plex}", line1=lang(39206)) if resp == 1: log.info("Reset login attempts.") window('plex_serverStatus', value="Auth") else: executebuiltin('Addon.OpenSettings(plugin.video.plexkodiconnect)')
def resetAuth(): # User tried login and failed too many times resp = dialog('yesno', heading="{plex}", line1=lang(39206)) if resp == 1: log.info("Reset login attempts.") plex_command('PMS_STATUS', 'Auth') else: executebuiltin('Addon.OpenSettings(plugin.video.plexkodiconnect)')
def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True): """ Hit this function for addon path playback, Plex trailers, etc. Will setup playback first, then on second call complete playback. Will set Playback_Successful() with potentially a PKC_ListItem() attached (to be consumed by setResolvedURL in default.py) If trailers or additional (movie-)parts are added, default.py is released and a completely new player instance is called with a new playlist. This circumvents most issues with Kodi & playqueues Set resolve to False if you do not want setResolvedUrl to be called on the first pass - e.g. if you're calling this function from the original service.py Python instance """ LOG.info('playback_triage called with plex_id %s, plex_type %s, path %s', plex_id, plex_type, path) if not state.AUTHENTICATED: LOG.error('Not yet authenticated for PMS, abort starting playback') if resolve is True: # Release default.py pickle_me(Playback_Successful()) # "Unauthorized for PMS" dialog('notification', lang(29999), lang(30017)) return playqueue = PQ.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[plex_type]) pos = js.get_position(playqueue.playlistid) # Can return -1 (as in "no playlist") pos = pos if pos != -1 else 0 LOG.debug('playQueue position: %s for %s', pos, playqueue) # Have we already initiated playback? try: playqueue.items[pos] except IndexError: # Release our default.py before starting our own Kodi player instance if resolve is True: state.PKC_CAUSED_STOP = True result = Playback_Successful() result.listitem = PKC_ListItem(path='PKC_Dummy_Path_Which_Fails') pickle_me(result) playback_init(plex_id, plex_type, playqueue) else: # kick off playback on second pass conclude_playback(playqueue, pos)
def check_plex_tv_sign_in(self): """ Checks existing connection to plex.tv. If not, triggers sign in Returns True if signed in, False otherwise """ answer = True chk = PF.check_connection('plex.tv', token=self.plex_token) if chk in (401, 403): # HTTP Error: unauthorized. Token is no longer valid LOG.info('plex.tv connection returned HTTP %s', str(chk)) # Delete token in the settings settings('plexToken', value='') settings('plexLogin', value='') # Could not login, please try again dialog('ok', lang(29999), lang(39009)) answer = self.plex_tv_sign_in() elif chk is False or chk >= 400: # Problems connecting to plex.tv. Network or internet issue? LOG.info( 'Problems connecting to plex.tv; connection returned ' 'HTTP %s', str(chk)) dialog('ok', lang(29999), lang(39010)) answer = False else: LOG.info('plex.tv connection with token successful') settings('plex_status', value=lang(39227)) # Refresh the info from Plex.tv xml = DU().downloadUrl( 'https://plex.tv/users/account', authenticate=False, headerOptions={'X-Plex-Token': self.plex_token}) try: self.plex_login = xml.attrib['title'] except (AttributeError, KeyError): LOG.error('Failed to update Plex info from plex.tv') else: settings('plexLogin', value=self.plex_login) home = 'true' if xml.attrib.get('home') == '1' else 'false' settings('plexhome', value=home) settings('plexAvatar', value=xml.attrib.get('thumb')) settings('plexHomeSize', value=xml.attrib.get('homeSize', '1')) LOG.info('Updated Plex info from plex.tv') return answer
def _library_sync(cls, mode): if window('emby_online') != "true": # Server is not online, do not run the sync dialog(type_="ok", heading="{emby}", line1=lang(33034)) log.warn("Not connected to the emby server") elif window('emby_dbScan') != "true": import librarysync library_sync = librarysync.LibrarySync() if mode == 'manualsync': librarysync.ManualSync().sync() elif mode == 'fastsync': library_sync.startSync() else: library_sync.fullSync(repair=True) else: log.warn("Database scan is already running")
def emby_connect(): # Login user to emby connect connect = connectmanager.ConnectManager() try: connectUser = connect.login_connect() except RuntimeError: return else: user = connectUser['User'] token = connectUser['AccessToken'] username = user['Name'] dialog(type_="notification", heading="{emby}", message="%s %s" % (lang(33000), username.decode('utf-8')), icon=user.get('ImageUrl') or "{emby}", time=2000, sound=False) settings('connectUsername', value=username)
def play(): """ Start up playback_starter in main Python thread """ # Put the request into the 'queue' plex_command('PLAY', argv[2]) # Wait for the result while not pickl_window('plex_result'): sleep(50) result = unpickle_me() if result is None: log.error('Error encountered, aborting') dialog('notification', heading='{plex}', message=lang(30128), icon='{error}', time=3000) setResolvedUrl(HANDLE, False, ListItem()) elif result.listitem: listitem = convert_PKC_to_listitem(result.listitem) setResolvedUrl(HANDLE, True, listitem)
def _delete_item(self): delete = True if settings('skipContextMenu') != "true": if not dialog(type_="yesno", heading="{emby}", line1=lang(33041)): log.info("User skipped deletion for: %s", self.item_id) delete = False if delete: log.info("Deleting request: %s", self.item_id) self.emby.deleteItem(self.item_id)
def emby_backup(): # Create a backup at specified location path = settings('backupPath') # filename default_value = "Kodi%s.%s" % (xbmc.getInfoLabel('System.BuildVersion')[:2], xbmc.getInfoLabel('System.Date(dd-mm-yy)')) folder_name = dialog(type_="input", heading=lang(33089), defaultt=default_value) if not folder_name: return backup = os.path.join(path, folder_name) log.info("Backup: %s", backup) # Create directory if xbmcvfs.exists(backup+"\\"): log.info("Existing directory!") if not dialog(type_="yesno", heading="{emby}", line1=lang(33090)): return emby_backup() shutil.rmtree(backup) # Addon_data addon_data = xbmc.translatePath("special://profile/addon_data/emby.for.kodi").decode('utf-8') try: shutil.copytree(src=addon_data, dst=os.path.join(backup, "addon_data", "emby.for.kodi")) except shutil.Error as error: log.error(error) # Database files database_folder = os.path.join(backup, "Database") if not xbmcvfs.mkdir(database_folder): try: os.makedirs(database_folder) except OSError as error: log.error(error) dialog(type_="ok", heading="{emby}", line1="Failed to create backup") return # Emby database emby_path = database.emby_database() xbmcvfs.copy(emby_path, os.path.join(database_folder, ntpath.basename(emby_path))) # Videos database video_path = database.video_database() xbmcvfs.copy(video_path, os.path.join(database_folder, ntpath.basename(video_path))) # Music database if settings('enableMusic') == "true": music_path = database.music_database() xbmcvfs.copy(music_path, os.path.join(database_folder, ntpath.basename(music_path))) dialog(type_="ok", heading="{emby}", line1="%s: %s" % (lang(33091), backup))
def togglePlexTV(): if settings('plexToken'): log.info('Reseting plex.tv credentials in settings') settings('plexLogin', value="") settings('plexToken', value=""), settings('plexid', value="") settings('plexHomeSize', value="1") settings('plexAvatar', value="") settings('plex_status', value="Not logged in to plex.tv") window('plex_token', clear=True) window('plex_username', clear=True) else: log.info('Login to plex.tv') import initialsetup initialsetup.InitialSetup().PlexTVSignIn() dialog('notification', lang(29999), lang(39221), icon='{plex}', time=3000, sound=False)
def _process_alexa(self, data): xml = GetPlexMetadata(data['key']) try: xml[0].attrib except (AttributeError, IndexError, TypeError): LOG.error('Could not download Plex metadata for: %s', data) return api = API(xml[0]) if api.plex_type() == v.PLEX_TYPE_ALBUM: LOG.debug('Plex music album detected') PQ.init_playqueue_from_plex_children( api.plex_id(), transient_token=data.get('token')) elif data['containerKey'].startswith('/playQueues/'): _, container_key, _ = ParseContainerKey(data['containerKey']) xml = DownloadChunks('{server}/playQueues/%s?' % container_key) if xml is None: # "Play error" dialog('notification', lang(29999), lang(30128), icon='{error}') return playqueue = PQ.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.plex_type()]) playqueue.clear() get_playlist_details_from_xml(playqueue, xml) playqueue.plex_transient_token = data.get('token') if data.get('offset') != '0': offset = float(data['offset']) / 1000.0 else: offset = None play_xml(playqueue, xml, offset) else: state.PLEX_TRANSIENT_TOKEN = data.get('token') if data.get('offset') != '0': state.RESUMABLE = True state.RESUME_PLAYBACK = True playback_triage(api.plex_id(), api.plex_type(), resolve=False)
def togglePlexTV(): if settings('plexToken'): log.info('Reseting plex.tv credentials in settings') settings('plexLogin', value="") settings('plexToken', value="") settings('plexid', value="") settings('plexHomeSize', value="1") settings('plexAvatar', value="") settings('plex_status', value=lang(39226)) window('plex_token', clear=True) plex_command('PLEX_TOKEN', '') plex_command('PLEX_USERNAME', '') else: log.info('Login to plex.tv') import initialsetup initialsetup.InitialSetup().plex_tv_sign_in() dialog('notification', lang(29999), lang(39221), icon='{plex}', time=3000, sound=False)
def chooseServer(): """ Lets user choose from list of PMS """ log.info("Choosing PMS server requested, starting") import initialsetup setup = initialsetup.InitialSetup() server = setup.PickPMS(showDialog=True) if server is None: log.error('We did not connect to a new PMS, aborting') plex_command('SUSPEND_USER_CLIENT', 'False') plex_command('SUSPEND_LIBRARY_THREAD', 'False') return log.info("User chose server %s" % server['name']) setup.WritePMStoSettings(server) if not __LogOut(): return from utils import deletePlaylists, deleteNodes # First remove playlists deletePlaylists() # Remove video nodes deleteNodes() # Log in again __LogIn() log.info("Choosing new PMS complete") # '<PMS> connected' dialog('notification', lang(29999), '%s %s' % (server['name'], lang(39220)), icon='{plex}', time=3000, sound=False)
def chooseServer(): """ Lets user choose from list of PMS """ log.info("Choosing PMS server requested, starting") import initialsetup setup = initialsetup.InitialSetup() server = setup.PickPMS(showDialog=True) if server is None: log.error('We did not connect to a new PMS, aborting') window('suspend_Userclient', clear=True) window('suspend_LibraryThread', clear=True) return log.info("User chose server %s" % server['name']) setup.WritePMStoSettings(server) if not __LogOut(): return from utils import deletePlaylists, deleteNodes # First remove playlists deletePlaylists() # Remove video nodes deleteNodes() # Log in again __LogIn() log.info("Choosing new PMS complete") # '<PMS> connected' dialog('notification', lang(29999), '%s %s' % (server['name'], lang(39220)), icon='{plex}', time=3000, sound=False)
def path_validation(cls, path): # Verify if direct path is accessible or not verify_path = path if not os.path.supports_unicode_filenames: verify_path = path.encode('utf-8') if window('emby_pathverified') != "true" and not xbmcvfs.exists(verify_path): if dialog(type_="yesno", heading="{emby}", line1="%s %s. %s" % (lang(33047), path, lang(33048))): window('emby_shouldStop', value="true") return False return True
def play(self): """ Start up playback_starter in main Python thread """ # Put the request into the 'queue' while window('plex_play_new_item'): sleep(50) window('plex_play_new_item', value='%s%s' % ('play', argv[2])) # Wait for the result while not pickl_window('plex_result'): sleep(50) result = unpickle_me() if result is None: log.error('Error encountered, aborting') dialog('notification', heading='{plex}', message=lang(30128), icon='{error}', time=3000) setResolvedUrl(HANDLE, False, ListItem()) elif result.listitem: listitem = convert_PKC_to_listitem(result.listitem) setResolvedUrl(HANDLE, True, listitem)
def texture_cache_sync(self): # This method will sync all Kodi artwork to textures13.db # and cache them locally. This takes diskspace! if not dialog(type_="yesno", heading="{emby}", line1=lang(33042)): return log.info("Doing Image Cache Sync") pdialog = xbmcgui.DialogProgress() pdialog.create(lang(29999), lang(33043)) # ask to rest all existing or not if dialog(type_="yesno", heading="{emby}", line1=lang(33044)): log.info("Resetting all cache data first") self.delete_cache() # Cache all entries in video DB self._cache_all_video_entries(pdialog) # Cache all entries in music DB self._cache_all_music_entries(pdialog) pdialog.update(100, "%s %s" % (lang(33046), len(self.image_cache_threads))) log.info("Waiting for all threads to exit") while len(self.image_cache_threads): for thread in self.image_cache_threads: if thread.is_finished: self.image_cache_threads.remove(thread) pdialog.update( 100, "%s %s" % (lang(33046), len(self.image_cache_threads))) log.info("Waiting for all threads to exit: %s", len(self.image_cache_threads)) xbmc.sleep(500) pdialog.close()
def __LogOut(): """ Finishes lib scans, logs out user. The following window attributes are set: suspend_LibraryThread: 'true' suspend_Userclient: 'true' Returns True if successfully signed out, False otherwise """ # Resetting, please wait dialog('notification', lang(29999), lang(39207), icon='{plex}', time=3000, sound=False) # Pause library sync thread window('suspend_LibraryThread', value='true') # Wait max for 10 seconds for all lib scans to shutdown counter = 0 while window('plex_dbScan') == 'true': if counter > 200: # Failed to reset PMS and plex.tv connects. Try to restart Kodi. dialog('ok', lang(29999), lang(39208)) # Resuming threads, just in case window('suspend_LibraryThread', clear=True) log.error("Could not stop library sync, aborting") return False counter += 1 sleep(50) log.debug("Successfully stopped library sync") # Log out currently signed in user: window('plex_serverStatus', value="401") # Above method needs to have run its course! Hence wait counter = 0 while window('plex_serverStatus') == "401": if counter > 100: # 'Failed to reset PKC. Try to restart Kodi.' dialog('ok', lang(29999), lang(39208)) log.error("Could not sign out user, aborting") return False counter += 1 sleep(50) # Suspend the user client during procedure window('suspend_Userclient', value='true') return True
def enterPMS(): """ Opens dialogs for the user the plug in the PMS details """ # "Enter your Plex Media Server's IP or URL. Examples are:" dialog('ok', lang(29999), lang(39215), '192.168.1.2', 'plex.myServer.org') ip = dialog('input', "Enter PMS IP or URL") if ip == '': return port = dialog('input', "Enter PMS port", '32400', type='{numeric}') if port == '': return url = '%s:%s' % (ip, port) # "Does your Plex Media Server support SSL connections? # (https instead of http)" https = dialog('yesno', lang(29999), lang(39217)) if https: url = 'https://%s' % url else: url = 'http://%s' % url https = 'true' if https else 'false' machineIdentifier = GetMachineIdentifier(url) if machineIdentifier is None: # "Error contacting url # Abort (Yes) or save address anyway (No)" if dialog('yesno', lang(29999), '%s %s. %s' % (lang(39218), url, lang(39219))): return else: settings('plex_machineIdentifier', '') else: settings('plex_machineIdentifier', machineIdentifier) log.info('Set new PMS to https %s, ip %s, port %s, machineIdentifier %s' % (https, ip, port, machineIdentifier)) settings('https', value=https) settings('ipaddress', value=ip) settings('port', value=port) # Chances are this is a local PMS, so disable SSL certificate check settings('sslverify', value='false') # Sign out to trigger new login if __LogOut(): # Only login again if logout was successful __LogIn()
def __LogOut(): """ Finishes lib scans, logs out user. Returns True if successfully signed out, False otherwise """ # Resetting, please wait dialog('notification', lang(29999), lang(39207), icon='{plex}', time=3000, sound=False) # Pause library sync thread plex_command('SUSPEND_LIBRARY_THREAD', 'True') # Wait max for 10 seconds for all lib scans to shutdown counter = 0 while window('plex_dbScan') == 'true': if counter > 200: # Failed to reset PMS and plex.tv connects. Try to restart Kodi. dialog('ok', lang(29999), lang(39208)) # Resuming threads, just in case plex_command('SUSPEND_LIBRARY_THREAD', 'False') log.error("Could not stop library sync, aborting") return False counter += 1 sleep(50) log.debug("Successfully stopped library sync") counter = 0 # Log out currently signed in user: window('plex_serverStatus', value='401') plex_command('PMS_STATUS', '401') # Above method needs to have run its course! Hence wait while window('plex_serverStatus') == "401": if counter > 100: # 'Failed to reset PKC. Try to restart Kodi.' dialog('ok', lang(29999), lang(39208)) log.error("Could not sign out user, aborting") return False counter += 1 sleep(50) # Suspend the user client during procedure plex_command('SUSPEND_USER_CLIENT', 'True') return True
def fullTextureCacheSync(self): """ This method will sync all Kodi artwork to textures13.db and cache them locally. This takes diskspace! """ if not dialog('yesno', "Image Texture Cache", lang(39250)): return log.info("Doing Image Cache Sync") # ask to rest all existing or not if dialog('yesno', "Image Texture Cache", lang(39251)): log.info("Resetting all cache data first") # Remove all existing textures first path = tryDecode(translatePath("special://thumbnails/")) if IfExists(path): allDirs, allFiles = listdir(path) for dir in allDirs: allDirs, allFiles = listdir(path+dir) for file in allFiles: if os_path.supports_unicode_filenames: delete(os_path.join( path + tryDecode(dir), tryDecode(file))) else: delete(os_path.join( tryEncode(path) + dir, file)) # remove all existing data from texture DB connection = kodiSQL('texture') cursor = connection.cursor() cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"') rows = cursor.fetchall() for row in rows: tableName = row[0] if tableName != "version": cursor.execute("DELETE FROM " + tableName) connection.commit() connection.close() # Cache all entries in video DB connection = kodiSQL('video') cursor = connection.cursor() # dont include actors cursor.execute("SELECT url FROM art WHERE media_type != 'actor'") result = cursor.fetchall() total = len(result) log.info("Image cache sync about to process %s video images" % total) connection.close() for url in result: self.cacheTexture(url[0]) # Cache all entries in music DB connection = kodiSQL('music') cursor = connection.cursor() cursor.execute("SELECT url FROM art") result = cursor.fetchall() total = len(result) log.info("Image cache sync about to process %s music images" % total) connection.close() for url in result: self.cacheTexture(url[0])
def ServiceEntryPoint(self): # Important: Threads depending on abortRequest will not trigger # if profile switch happens more than once. monitor = self.monitor kodiProfile = v.KODI_PROFILE # Detect playback start early on self.monitor_kodi_play = Monitor_Kodi_Play(self) self.monitor_kodi_play.start() # Server auto-detect initialsetup.InitialSetup().setup() # Initialize important threads, handing over self for callback purposes self.user = UserClient(self) self.ws = PMS_Websocket(self) self.alexa = Alexa_Websocket(self) self.library = LibrarySync(self) self.plexCompanion = PlexCompanion(self) self.playqueue = Playqueue(self) self.playback_starter = Playback_Starter(self) if settings('enableTextureCache') == "true": self.image_cache_thread = Image_Cache_Thread() plx = PlexAPI.PlexAPI() welcome_msg = True counter = 0 while not monitor.abortRequested(): if window('plex_kodiProfile') != kodiProfile: # Profile change happened, terminate this thread and others log.warn("Kodi profile was: %s and changed to: %s. " "Terminating old PlexKodiConnect thread." % (kodiProfile, window('plex_kodiProfile'))) break # Before proceeding, need to make sure: # 1. Server is online # 2. User is set # 3. User has access to the server if window('plex_online') == "true": # Plex server is online # Verify if user is set and has access to the server if (self.user.currUser is not None) and self.user.HasAccess: if not self.kodimonitor_running: # Start up events self.warn_auth = True if welcome_msg is True: # Reset authentication warnings welcome_msg = False dialog('notification', lang(29999), "%s %s" % (lang(33000), self.user.currUser), icon='{plex}', time=2000, sound=False) # Start monitoring kodi events self.kodimonitor_running = KodiMonitor(self) # Start playqueue client if not self.playqueue_running: self.playqueue_running = True self.playqueue.start() # Start the Websocket Client if not self.ws_running: self.ws_running = True self.ws.start() # Start the Alexa thread if (not self.alexa_running and settings('enable_alexa') == 'true'): self.alexa_running = True self.alexa.start() # Start the syncing thread if not self.library_running: self.library_running = True self.library.start() # Start the Plex Companion thread if not self.plexCompanion_running: self.plexCompanion_running = True self.plexCompanion.start() if not self.playback_starter_running: self.playback_starter_running = True self.playback_starter.start() if (not self.image_cache_thread_running and settings('enableTextureCache') == "true"): self.image_cache_thread_running = True self.image_cache_thread.start() else: if (self.user.currUser is None) and self.warn_auth: # Alert user is not authenticated and suppress future # warning self.warn_auth = False log.warn("Not authenticated yet.") # User access is restricted. # Keep verifying until access is granted # unless server goes offline or Kodi is shut down. while self.user.HasAccess is False: # Verify access with an API call self.user.hasAccess() if window('plex_online') != "true": # Server went offline break if monitor.waitForAbort(5): # Abort was requested while waiting. We should exit break sleep(50) else: # Wait until Plex server is online # or Kodi is shut down. while not monitor.abortRequested(): server = self.user.getServer() if server is False: # No server info set in add-on settings pass elif plx.CheckConnection(server, verifySSL=True) is False: # Server is offline or cannot be reached # Alert the user and suppress future warning if self.server_online: self.server_online = False window('plex_online', value="false") # Suspend threads window('suspend_LibraryThread', value='true') log.error("Plex Media Server went offline") if settings('show_pms_offline') == 'true': dialog('notification', lang(33001), "%s %s" % (lang(29999), lang(33002)), icon='{plex}', sound=False) counter += 1 # Periodically check if the IP changed, e.g. per minute if counter > 20: counter = 0 setup = initialsetup.InitialSetup() tmp = setup.PickPMS() if tmp is not None: setup.WritePMStoSettings(tmp) else: # Server is online counter = 0 if not self.server_online: # Server was offline when Kodi started. # Wait for server to be fully established. if monitor.waitForAbort(5): # Abort was requested while waiting. break self.server_online = True # Alert the user that server is online. if (welcome_msg is False and settings('show_pms_offline') == 'true'): dialog('notification', lang(29999), lang(33003), icon='{plex}', time=5000, sound=False) log.info("Server %s is online and ready." % server) window('plex_online', value="true") if window('plex_authenticated') == 'true': # Server got offline when we were authenticated. # Hence resume threads window('suspend_LibraryThread', clear=True) # Start the userclient thread if not self.user_running: self.user_running = True self.user.start() break if monitor.waitForAbort(3): # Abort was requested while waiting. break if monitor.waitForAbort(0.05): # Abort was requested while waiting. We should exit break # Terminating PlexKodiConnect # Tell all threads to terminate (e.g. several lib sync threads) window('plex_terminateNow', value='true') try: self.plexCompanion.stopThread() except: log.warn('plexCompanion already shut down') try: self.library.stopThread() except: log.warn('Library sync already shut down') try: self.ws.stopThread() except: log.warn('Websocket client already shut down') try: self.alexa.stopThread() except: log.warn('Websocket client already shut down') try: self.user.stopThread() except: log.warn('User client already shut down') try: downloadutils.DownloadUtils().stopSession() except: pass window('plex_service_started', clear=True) log.warn("======== STOP %s ========" % v.ADDON_NAME)
def __init__(self): log.debug('Full sys.argv received: %s' % argv) # Parse parameters params = dict(parse_qsl(argv[2][1:])) mode = params.get('mode', '') itemid = params.get('id', '') if mode == 'play': self.play() elif mode == 'plex_node': self.play() elif mode == 'ondeck': entrypoint.getOnDeck(itemid, params.get('type'), params.get('tagname'), int(params.get('limit'))) elif mode == 'recentepisodes': entrypoint.getRecentEpisodes(itemid, params.get('type'), params.get('tagname'), int(params.get('limit'))) elif mode == 'nextup': entrypoint.getNextUpEpisodes(params['tagname'], int(params['limit'])) elif mode == 'inprogressepisodes': entrypoint.getInProgressEpisodes(params['tagname'], int(params['limit'])) elif mode == 'browseplex': entrypoint.browse_plex(key=params.get('key'), plex_section_id=params.get('id')) elif mode == 'getsubfolders': entrypoint.GetSubFolders(itemid) elif mode == 'watchlater': entrypoint.watchlater() elif mode == 'channels': entrypoint.channels() elif mode == 'settings': executebuiltin('Addon.OpenSettings(%s)' % v.ADDON_ID) elif mode == 'enterPMS': entrypoint.enterPMS() elif mode == 'reset': reset() elif mode == 'togglePlexTV': entrypoint.togglePlexTV() elif mode == 'resetauth': entrypoint.resetAuth() elif mode == 'passwords': passwordsXML() elif mode == 'switchuser': entrypoint.switchPlexUser() elif mode in ('manualsync', 'repair'): if window('plex_online') != 'true': # Server is not online, do not run the sync dialog('ok', heading=lang(29999), message=lang(39205)) log.error('Not connected to a PMS.') else: if mode == 'repair': window('plex_runLibScan', value='repair') log.info('Requesting repair lib sync') elif mode == 'manualsync': log.info('Requesting full library scan') window('plex_runLibScan', value='full') elif mode == 'texturecache': window('plex_runLibScan', value='del_textures') elif mode == 'chooseServer': entrypoint.chooseServer() elif mode == 'refreshplaylist': log.info('Requesting playlist/nodes refresh') window('plex_runLibScan', value='views') elif mode == 'deviceid': self.deviceid() elif mode == 'fanart': log.info('User requested fanarttv refresh') window('plex_runLibScan', value='fanart') elif '/extrafanart' in argv[0]: plexpath = argv[2][1:] plexid = itemid entrypoint.getExtraFanArt(plexid, plexpath) entrypoint.getVideoFiles(plexid, plexpath) # Called by e.g. 3rd party plugin video extras elif ('/Extras' in argv[0] or '/VideoFiles' in argv[0] or '/Extras' in argv[2]): plexId = itemid or None entrypoint.getVideoFiles(plexId, params) else: entrypoint.doMainListing(content_type=params.get('content_type'))
def downloadUrl(self, url, action_type="GET", postBody=None, parameters=None, authenticate=True, headerOptions=None, verifySSL=True, timeout=None): """ Override SSL check with verifySSL=False If authenticate=True, existing request session will be used/started Otherwise, 'empty' request will be made Returns: None If an error occured True If connection worked but no body was received 401, ... integer if PMS answered with HTTP error 401 (unauthorized) or other http error codes xml xml etree root object, if applicable JSON json() object, if applicable """ kwargs = {'timeout': self.timeout} if authenticate is True: # Get requests session try: s = self.s except AttributeError: log.info("Request session does not exist: start one") self.startSession() s = self.s # Replace for the real values url = url.replace("{server}", self.server) else: # User is not (yet) authenticated. Used to communicate with # plex.tv and to check for PMS servers s = requests headerOptions = self.getHeader(options=headerOptions) if settings('sslcert') != 'None': kwargs['cert'] = settings('sslcert') # Set the variables we were passed (fallback to request session # otherwise - faster) kwargs['url'] = url if verifySSL is False: kwargs['verify'] = False if headerOptions is not None: kwargs['headers'] = headerOptions if postBody is not None: kwargs['data'] = postBody if parameters is not None: kwargs['params'] = parameters if timeout is not None: kwargs['timeout'] = timeout # ACTUAL DOWNLOAD HAPPENING HERE try: r = self._doDownload(s, action_type, **kwargs) # THE EXCEPTIONS except requests.exceptions.ConnectionError as e: # Connection error log.debug("Server unreachable at: %s" % url) log.debug(e) except requests.exceptions.Timeout as e: log.debug("Server timeout at: %s" % url) log.debug(e) except requests.exceptions.HTTPError as e: log.warn('HTTP Error at %s' % url) log.warn(e) except requests.exceptions.SSLError as e: log.warn("Invalid SSL certificate for: %s" % url) log.warn(e) except requests.exceptions.TooManyRedirects as e: log.warn("Too many redirects connecting to: %s" % url) log.warn(e) except requests.exceptions.RequestException as e: log.warn("Unknown error connecting to: %s" % url) log.warn(e) except SystemExit: log.info('SystemExit detected, aborting download') self.stopSession() except: log.warn('Unknown error while downloading. Traceback:') import traceback log.warn(traceback.format_exc()) # THE RESPONSE ##### else: # We COULD contact the PMS, hence it ain't dead if authenticate is True: window('countError', value='0') if r.status_code != 401: window('countUnauthorized', value='0') if r.status_code == 204: # No body in the response # But read (empty) content to release connection back to pool # (see requests: keep-alive documentation) r.content return True elif r.status_code == 401: if authenticate is False: # Called when checking a connect - no need for rash action return 401 r.encoding = 'utf-8' log.warn('HTTP error 401 from PMS %s' % url) log.info(r.text) if '401 Unauthorized' in r.text: # Truly unauthorized window('countUnauthorized', value=str(int(window('countUnauthorized')) + 1)) if (int(window('countUnauthorized')) >= self.unauthorizedAttempts): log.warn('We seem to be truly unauthorized for PMS' ' %s ' % url) if window('plex_serverStatus') not in ('401', 'Auth'): # Tell userclient token has been revoked. log.debug('Setting PMS server status to ' 'unauthorized') window('plex_serverStatus', value="401") dialog('notification', lang(29999), lang(30017), icon='{error}') else: # there might be other 401 where e.g. PMS under strain log.info('PMS might only be under strain') return 401 elif r.status_code in (200, 201): # 200: OK # 201: Created try: # xml response r = etree.fromstring(r.content) return r except: r.encoding = 'utf-8' if r.text == '': # Answer does not contain a body return True try: # UNICODE - JSON object r = r.json() return r except: if '200 OK' in r.text: # Received f****d up OK from PMS on playstate # update pass else: log.error("Unable to convert the response for: " "%s" % url) log.info("Received headers were: %s" % r.headers) log.info('Received text:') log.info(r.text) return True elif r.status_code == 403: # E.g. deleting a PMS item log.error('PMS sent 403: Forbidden error for url %s' % url) return None else: log.error('Unknown answer from PMS %s with status code %s. ' 'Message:' % (url, r.status_code)) r.encoding = 'utf-8' log.info(r.text) return True # And now deal with the consequences of the exceptions if authenticate is True: # Make the addon aware of status try: window('countError', value=str(int(window('countError')) + 1)) if int(window('countError')) >= self.connectionAttempts: log.warn('Failed to connect to %s too many times. ' 'Declare PMS dead' % url) window('plex_online', value="false") except: # 'countError' not yet set pass return None
def _general_commands(cls, data): command = data['Name'] arguments = data['Arguments'] if command in ('Mute', 'Unmute', 'SetVolume', 'SetSubtitleStreamIndex', 'SetAudioStreamIndex'): player = xbmc.Player() # These commands need to be reported back if command == 'Mute': xbmc.executebuiltin('Mute') elif command == 'Unmute': xbmc.executebuiltin('Mute') elif command == 'SetVolume': volume = arguments['Volume'] xbmc.executebuiltin('SetVolume(%s[,showvolumebar])' % volume) elif command == 'SetAudioStreamIndex': index = int(arguments['Index']) player.setAudioStream(index - 1) elif command == 'SetSubtitleStreamIndex': emby_index = int(arguments['Index']) current_file = player.getPlayingFile() mapping = window('emby_%s.indexMapping' % current_file) if emby_index == -1: player.showSubtitles(False) elif mapping: external_index = json.loads(mapping) # If there's external subtitles added via playbackutils for index in external_index: if external_index[index] == emby_index: player.setSubtitleStream(int(index)) break else: # User selected internal subtitles external = len(external_index) audio_tracks = len(player.getAvailableAudioStreams()) player.setSubtitleStream(external + emby_index - audio_tracks - 1) else: # Emby merges audio and subtitle index together audio_tracks = len(player.getAvailableAudioStreams()) player.setSubtitleStream(emby_index - audio_tracks - 1) # Let service know window('emby_command', value="true") elif command == 'DisplayMessage': header = arguments['Header'] text = arguments['Text'] dialog(type_="notification", heading=header, message=text, icon="{emby}", time=int(settings('displayMessage'))*1000) elif command == 'SendString': params = { 'text': arguments['String'], 'done': False } JSONRPC('Input.SendText').execute(params) elif command in ('MoveUp', 'MoveDown', 'MoveRight', 'MoveLeft'): # Commands that should wake up display actions = { 'MoveUp': "Input.Up", 'MoveDown': "Input.Down", 'MoveRight': "Input.Right", 'MoveLeft': "Input.Left" } JSONRPC(actions[command]).execute() elif command == 'GoHome': JSONRPC('GUI.ActivateWindow').execute({'window': "home"}) else: builtin = { 'ToggleFullscreen': 'Action(FullScreen)', 'ToggleOsdMenu': 'Action(OSD)', 'ToggleContextMenu': 'Action(ContextMenu)', 'Select': 'Action(Select)', 'Back': 'Action(back)', 'PageUp': 'Action(PageUp)', 'NextLetter': 'Action(NextLetter)', 'GoToSearch': 'VideoLibrary.Search', 'GoToSettings': 'ActivateWindow(Settings)', 'PageDown': 'Action(PageDown)', 'PreviousLetter': 'Action(PrevLetter)', 'TakeScreenshot': 'TakeScreenshot', 'ToggleMute': 'Mute', 'VolumeUp': 'Action(VolumeUp)', 'VolumeDown': 'Action(VolumeDown)', } if command in builtin: xbmc.executebuiltin(builtin[command])
def _server_online_check(self): # Set emby_online true/false property user_client = self.userclient_thread while not self.monitor.abortRequested(): if user_client.get_server() is None: # No server info set in add-on settings pass elif not user_client.verify_server(): # Server is offline. # Alert the user and suppress future warning if self.server_online: log.info("Server is offline") window('emby_online', value="false") if settings('offlineMsg') == "true": dialog(type_="notification", heading=lang(33001), message="%s %s" % (self.addon_name, lang(33002)), icon="{emby}", sound=False) self.server_online = False elif window('emby_online') in ("sleep", "reset"): # device going to sleep if self.websocket_running: self.websocket_thread.stop_client() self.websocket_thread = wsc.WebSocketClient() self.websocket_running = False if self.library_running: self.library_thread.stopThread() self.library_thread = librarysync.LibrarySync() self.library_running = False else: # Server is online if not self.server_online: # Server was offline when Kodi started. # Wait for server to be fully established. if self.monitor.waitForAbort(5): # Abort was requested while waiting. break # Alert the user that server is online. dialog(type_="notification", heading="{emby}", message=lang(33003), icon="{emby}", time=2000, sound=False) self.server_online = True window('emby_online', value="true") log.info("Server is online and ready") # Start the userclient thread if not self.userclient_running: self.userclient_running = True user_client.start() break if self.monitor.waitForAbort(1): # Abort was requested while waiting. break
def client_update(self): self.update_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) update_sock = self.update_sock # Set socket reuse, may not work on all OSs. try: update_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) except: pass # Attempt to bind to the socket to recieve and send data. If we cant # do this, then we cannot send registration try: update_sock.bind(('0.0.0.0', self.client_update_port)) except: log.error("Unable to bind to port [%s] - Plex Companion will not " "be registered. Change the Plex Companion update port!" % self.client_update_port) if settings('companion_show_gdm_port_warning') == 'true': if dialog('yesno', language(29999), 'Port %s' % self.client_update_port, language(39079), yeslabel=language(30013), # Never show again nolabel=language(30012)): # OK settings('companion_show_gdm_port_warning', value='false') from xbmc import executebuiltin executebuiltin( 'Addon.OpenSettings(plugin.video.plexkodiconnect)') return update_sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255) update_sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton( self._multicast_address) + socket.inet_aton('0.0.0.0')) update_sock.setblocking(0) # Send initial client registration self.register_as_client() # Now, listen format client discovery reguests and respond. while self._registration_is_running: try: data, addr = update_sock.recvfrom(1024) log.debug("Recieved UDP packet from [%s] containing [%s]" % (addr, data.strip())) except socket.error: pass else: if "M-SEARCH * HTTP/1." in data: log.debug("Detected client discovery request from %s. " " Replying" % str(addr)) try: update_sock.sendto("HTTP/1.0 200 OK\n%s" % self.client_data, addr) except: log.error("Unable to send client update message") log.debug("Sending registration data HTTP/1.0 200 OK") self.client_registered = True sleep(500) log.info("Client Update loop stopped") # When we are finished, then send a final goodbye message to # deregister cleanly. log.debug("Sending registration data: BYE %s\n%s" % (self.client_header, self.client_data)) try: update_sock.sendto("BYE %s\n%s" % (self.client_header, self.client_data), self.client_register_group) except: log.error("Unable to send client update message") self.client_registered = False
def RunLibScan(mode): if window('plex_online') != "true": # Server is not online, do not run the sync dialog('ok', lang(29999), lang(39205)) else: window('plex_runLibScan', value='full')