def GetPlexMetadata(key): """ Returns raw API metadata for key as an etree XML. Can be called with either Plex key '/library/metadata/xxxx'metadata OR with the digits 'xxxx' only. Returns None if something went wrong """ key = str(key) if '/library/metadata/' in key: url = "{server}" + key else: url = "{server}/library/metadata/" + key arguments = { 'checkFiles': 1, # No idea 'includeExtras': 1, # Trailers and Extras => Extras # 'includeRelated': 1, # Similar movies => Video -> Related # 'includeRelatedCount': 5, # 'includeOnDeck': 1, 'includeChapters': 1, 'includePopularLeaves': 1, 'includeConcerts': 1 } url = url + '?' + urlencode(arguments) xml = downloadutils.DownloadUtils().downloadUrl(url) # Did we receive a valid XML? try: xml.attrib # Nope we did not receive a valid XML except AttributeError: logMsg(title, "Error retrieving metadata for %s" % url, -1) xml = None return xml
def GetPlexPlaylist(itemid, librarySectionUUID, mediatype='movie'): """ Returns raw API metadata XML dump for a playlist with e.g. trailers. """ trailerNumber = settings('trailerNumber') if not trailerNumber: trailerNumber = '3' url = "{server}/playQueues" args = { 'type': mediatype, 'uri': 'library://' + librarySectionUUID + '/item/%2Flibrary%2Fmetadata%2F' + itemid, 'includeChapters': '1', 'extrasPrefixCount': trailerNumber, 'shuffle': '0', 'repeat': '0' } xml = downloadutils.DownloadUtils().downloadUrl( url + '?' + urlencode(args), action_type="POST") try: xml[0].tag except (IndexError, TypeError, AttributeError): logMsg(title, "Error retrieving metadata for %s" % url, -1) return None return xml
def PMSHttpsEnabled(url): """ Returns True if the PMS can talk https, False otherwise. None if error occured, e.g. the connection timed out Call with e.g. url='192.168.0.1:32400' (NO http/https) This is done by GET /identity (returns an error if https is enabled and we are trying to use http) Prefers HTTPS over HTTP """ doUtils = downloadutils.DownloadUtils().downloadUrl res = doUtils('https://%s/identity' % url, authenticate=False, verifySSL=False) try: res.attrib except AttributeError: # Might have SSL deactivated. Try with http res = doUtils('http://%s/identity' % url, authenticate=False, verifySSL=False) try: res.attrib except AttributeError: logMsg(title, "Could not contact PMS %s" % url, -1) return None else: # Received a valid XML. Server wants to talk HTTP return False else: # Received a valid XML. Server wants to talk HTTPS return True
def refreshPlaylist(): lib = librarysync.LibrarySync() dialog = xbmcgui.Dialog() try: # First remove playlists utils.deletePlaylists() # Remove video nodes utils.deleteNodes() # Refresh views lib.refreshViews() dialog.notification( heading="Emby for Kodi", message="Emby playlists/nodes refreshed", icon="special://home/addons/plugin.video.emby/icon.png", time=1000, sound=False) except Exception as e: utils.logMsg("EMBY", "Refresh playlists/nodes failed: %s" % e, 1) dialog.notification( heading="Emby for Kodi", message="Emby playlists/nodes refresh failed", icon=xbmcgui.NOTIFICATION_ERROR, time=1000, sound=False)
def getSongTags(file): # Get the actual ID3 tags for music songs as the server is lacking that info rating = 0 comment = "" hasEmbeddedCover = False isTemp, filename = getRealFileName(file) logMsg("getting song ID3 tags for " + filename) try: ###### FLAC FILES ############# if filename.lower().endswith(".flac"): audio = FLAC(filename) if audio.get("comment"): comment = audio.get("comment")[0] for pic in audio.pictures: if pic.type == 3 and pic.data: #the file has an embedded cover hasEmbeddedCover = True break if audio.get("rating"): rating = float(audio.get("rating")[0]) #flac rating is 0-100 and needs to be converted to 0-5 range if rating > 5: rating = (rating / 100) * 5 ###### MP3 FILES ############# elif filename.lower().endswith(".mp3"): audio = ID3(filename) if audio.get("APIC:Front Cover"): if audio.get("APIC:Front Cover").data: hasEmbeddedCover = True if audio.get("comment"): comment = audio.get("comment")[0] if audio.get("POPM:Windows Media Player 9 Series"): if audio.get("POPM:Windows Media Player 9 Series").rating: rating = float( audio.get("POPM:Windows Media Player 9 Series").rating) #POPM rating is 0-255 and needs to be converted to 0-5 range if rating > 5: rating = (rating / 255) * 5 else: logMsg("Not supported fileformat or unable to access file: %s" % (filename)) #the rating must be a round value rating = int(round(rating, 0)) except Exception as e: #file in use ? utils.logMsg("Exception in getSongTags", str(e), 0) rating = None #remove tempfile if needed.... if isTemp: xbmcvfs.delete(filename) return (rating, comment, hasEmbeddedCover)
def getSongTags(file): # Get the actual ID3 tags for music songs as the server is lacking that info rating = 0 comment = "" hasEmbeddedCover = False isTemp,filename = getRealFileName(file) logMsg( "getting song ID3 tags for " + filename) try: ###### FLAC FILES ############# if filename.lower().endswith(".flac"): audio = FLAC(filename) if audio.get("comment"): comment = audio.get("comment")[0] for pic in audio.pictures: if pic.type == 3 and pic.data: #the file has an embedded cover hasEmbeddedCover = True break if audio.get("rating"): rating = float(audio.get("rating")[0]) #flac rating is 0-100 and needs to be converted to 0-5 range if rating > 5: rating = (rating / 100) * 5 ###### MP3 FILES ############# elif filename.lower().endswith(".mp3"): audio = ID3(filename) if audio.get("APIC:Front Cover"): if audio.get("APIC:Front Cover").data: hasEmbeddedCover = True if audio.get("comment"): comment = audio.get("comment")[0] if audio.get("POPM:Windows Media Player 9 Series"): if audio.get("POPM:Windows Media Player 9 Series").rating: rating = float(audio.get("POPM:Windows Media Player 9 Series").rating) #POPM rating is 0-255 and needs to be converted to 0-5 range if rating > 5: rating = (rating / 255) * 5 else: logMsg( "Not supported fileformat or unable to access file: %s" %(filename)) #the rating must be a round value rating = int(round(rating,0)) except Exception as e: #file in use ? utils.logMsg("Exception in getSongTags", str(e),0) rating = None #remove tempfile if needed.... if isTemp: xbmcvfs.delete(filename) return (rating, comment, hasEmbeddedCover)
def __init__(self): self.enableTextureCache = utils.settings('enableTextureCache') == "true" self.imageCacheLimitThreads = int(utils.settings("imageCacheLimit")) self.imageCacheLimitThreads = int(self.imageCacheLimitThreads * 5); utils.logMsg("Using Image Cache Thread Count: " + str(self.imageCacheLimitThreads), 1) if not self.xbmc_port and self.enableTextureCache: self.setKodiWebServerDetails() self.userId = utils.window('currUserId') self.server = utils.window('pms_server')
def resetAuth(): # User tried login and failed too many times resp = xbmcgui.Dialog().yesno( heading="Warning", line1=( "Emby might lock your account if you fail to log in too many times. " "Proceed anyway?")) if resp == 1: utils.logMsg("EMBY", "Reset login attempts.", 1) utils.window('emby_serverStatus', value="Auth") else: xbmc.executebuiltin('Addon.OpenSettings(plugin.video.emby)')
def __init__(self): self.clientinfo = clientinfo.ClientInfo() self.addonName = self.clientinfo.getAddonName() self.enableTextureCache = utils.settings('enableTextureCache') == "true" self.imageCacheLimitThreads = int(utils.settings("imageCacheLimit")) self.imageCacheLimitThreads = int(self.imageCacheLimitThreads * 5); utils.logMsg("Using Image Cache Thread Count: " + str(self.imageCacheLimitThreads), 1) if not self.xbmc_port and self.enableTextureCache: self.setKodiWebServerDetails() self.userId = utils.window('emby_currUser') self.server = utils.window('emby_server%s' % self.userId)
def scrobble(ratingKey, state): """ Tells the PMS to set an item's watched state to state="watched" or state="unwatched" """ args = {'key': ratingKey, 'identifier': 'com.plexapp.plugins.library'} if state == "watched": url = "{server}/:/scrobble?" + urlencode(args) elif state == "unwatched": url = "{server}/:/unscrobble?" + urlencode(args) else: return downloadutils.DownloadUtils().downloadUrl(url, type="GET") logMsg(title, "Toggled watched state for Plex item %s" % ratingKey, 1)
def scrobble(ratingKey, state): """ Tells the PMS to set an item's watched state to state="watched" or state="unwatched" """ args = { 'key': ratingKey, 'identifier': 'com.plexapp.plugins.library' } if state == "watched": url = "{server}/:/scrobble?" + urlencode(args) elif state == "unwatched": url = "{server}/:/unscrobble?" + urlencode(args) else: return downloadutils.DownloadUtils().downloadUrl(url) logMsg(title, "Toggled watched state for Plex item %s" % ratingKey, 1)
def GetMachineIdentifier(url): """ Returns the unique PMS machine identifier of url Returns None if something went wrong """ xml = downloadutils.DownloadUtils().downloadUrl(url + '/identity') try: xml.attrib except: logMsg(title, 'Could not get the PMS machineIdentifier for %s' % url, -1) return None machineIdentifier = xml.attrib.get('machineIdentifier') logMsg(title, 'Found machineIdentifier %s for %s' % (machineIdentifier, url), 1) return machineIdentifier
def __init__(self): self.clientinfo = clientinfo.ClientInfo() self.addonName = self.clientinfo.getAddonName() self.enableTextureCache = utils.settings( 'enableTextureCache') == "true" self.imageCacheLimitThreads = int(utils.settings("imageCacheLimit")) self.imageCacheLimitThreads = int(self.imageCacheLimitThreads * 5) utils.logMsg( "Using Image Cache Thread Count: " + str(self.imageCacheLimitThreads), 1) if not self.xbmc_port and self.enableTextureCache: self.setKodiWebServerDetails() self.userId = utils.window('emby_currUser') self.server = utils.window('emby_server%s' % self.userId)
def GetMachineIdentifier(url): """ Returns the unique PMS machine identifier of url Returns None if something went wrong """ xml = downloadutils.DownloadUtils().downloadUrl(url + '/identity', type="GET") try: xml.attrib except: logMsg(title, 'Could not get the PMS machineIdentifier for %s' % url, -1) return None machineIdentifier = xml.attrib.get('machineIdentifier') logMsg(title, 'Found machineIdentifier %s for %s' % (machineIdentifier, url), 1) return machineIdentifier
def GetMachineIdentifier(url): """ Returns the unique PMS machine identifier of url Returns None if something went wrong """ xml = downloadutils.DownloadUtils().downloadUrl('%s/identity' % url, authenticate=False, verifySSL=False, timeout=4) try: machineIdentifier = xml.attrib['machineIdentifier'] except (AttributeError, KeyError): logMsg(title, 'Could not get the PMS machineIdentifier for %s' % url, -1) return None logMsg(title, 'Found machineIdentifier %s for the PMS %s' % (machineIdentifier, url), 1) return machineIdentifier
def resetDeviceId(): dialog = xbmcgui.Dialog() language = utils.language deviceId_old = utils.window('emby_deviceId') try: utils.window('emby_deviceId', clear=True) deviceId = clientinfo.ClientInfo().getDeviceId(reset=True) except Exception as e: utils.logMsg("EMBY", "Failed to generate a new device Id: %s" % e, 1) dialog.ok( heading="Emby for Kodi", line1=language(33032)) else: utils.logMsg("EMBY", "Successfully removed old deviceId: %s New deviceId: %s" % (deviceId_old, deviceId), 1) dialog.ok( heading="Emby for Kodi", line1=language(33033)) xbmc.executebuiltin('RestartApp')
def DownloadChunks(url, containerSize): """ Downloads PMS url in chunks of containerSize (int). If containerSize is None: ONE xml is fetched directly url MUST end with '?' (if no other url encoded args are present) or '&' Returns a stitched-together xml or None. """ if containerSize is None: # Get rid of '?' or '&' at the end of url xml = downloadutils.DownloadUtils().downloadUrl(url[:-1]) try: xml.attrib except AttributeError: # Nope, not an XML, abort logMsg(title, "Error getting url %s" % url[:-1], -1) return None else: return xml xml = None pos = 0 errorCounter = 0 while errorCounter < 10: args = { 'X-Plex-Container-Size': containerSize, 'X-Plex-Container-Start': pos } xmlpart = downloadutils.DownloadUtils().downloadUrl( url + urlencode(args)) # If something went wrong - skip in the hope that it works next time try: xmlpart.attrib except AttributeError: logMsg(title, 'Error while downloading chunks: %s' % (url + urlencode(args)), -1) pos += containerSize errorCounter += 1 continue # Very first run: starting xml (to retain data in xml's root!) if xml is None: xml = deepcopy(xmlpart) if len(xmlpart) < containerSize: break else: pos += containerSize continue # Build answer xml - containing the entire library for child in xmlpart: xml.append(child) # Done as soon as we don't receive a full complement of items if len(xmlpart) < containerSize: break pos += containerSize if errorCounter == 10: logMsg(title, 'Fatal error while downloading chunks for %s' % url, -1) return None return xml
def getExtraFanArt(embyId,embyPath): emby = embyserver.Read_EmbyServer() art = artwork.Artwork() # Get extrafanart for listitem # will be called by skinhelper script to get the extrafanart try: # for tvshows we get the embyid just from the path if not embyId: if "plugin.video.emby" in embyPath: embyId = embyPath.split("/")[-2] if embyId: #only proceed if we actually have a emby id utils.logMsg("EMBY", "Requesting extrafanart for Id: %s" % embyId, 0) # We need to store the images locally for this to work # because of the caching system in xbmc fanartDir = xbmc.translatePath("special://thumbnails/emby/%s/" % embyId).decode('utf-8') if not xbmcvfs.exists(fanartDir): # Download the images to the cache directory xbmcvfs.mkdirs(fanartDir) item = emby.getItem(embyId) if item: backdrops = art.getAllArtwork(item)['Backdrop'] tags = item['BackdropImageTags'] count = 0 for backdrop in backdrops: # Same ordering as in artwork tag = tags[count] if os.path.supports_unicode_filenames: fanartFile = os.path.join(fanartDir, "fanart%s.jpg" % tag) else: fanartFile = os.path.join(fanartDir.encode("utf-8"), "fanart%s.jpg" % tag.encode("utf-8")) li = xbmcgui.ListItem(tag, path=fanartFile) xbmcplugin.addDirectoryItem( handle=int(sys.argv[1]), url=fanartFile, listitem=li) xbmcvfs.copy(backdrop, fanartFile) count += 1 else: utils.logMsg("EMBY", "Found cached backdrop.", 2) # Use existing cached images dirs, files = xbmcvfs.listdir(fanartDir) for file in files: fanartFile = os.path.join(fanartDir, file.decode('utf-8')) li = xbmcgui.ListItem(file, path=fanartFile) xbmcplugin.addDirectoryItem( handle=int(sys.argv[1]), url=fanartFile, listitem=li) except Exception as e: utils.logMsg("EMBY", "Error getting extrafanart: %s" % e, 0) # Always do endofdirectory to prevent errors in the logs xbmcplugin.endOfDirectory(int(sys.argv[1]))
def deleteItem(): # Serves as a keymap action if xbmc.getInfoLabel('ListItem.Property(embyid)'): # If we already have the embyid embyid = xbmc.getInfoLabel('ListItem.Property(embyid)') else: dbid = xbmc.getInfoLabel('ListItem.DBID') itemtype = xbmc.getInfoLabel('ListItem.DBTYPE') if not itemtype: if xbmc.getCondVisibility('Container.Content(albums)'): itemtype = "album" elif xbmc.getCondVisibility('Container.Content(artists)'): itemtype = "artist" elif xbmc.getCondVisibility('Container.Content(songs)'): itemtype = "song" elif xbmc.getCondVisibility('Container.Content(pictures)'): itemtype = "picture" else: utils.logMsg("EMBY delete", "Unknown type, unable to proceed.", 1) return embyconn = utils.kodiSQL('emby') embycursor = embyconn.cursor() emby_db = embydb.Embydb_Functions(embycursor) item = emby_db.getItem_byKodiId(dbid, itemtype) embycursor.close() try: embyid = item[0] except TypeError: utils.logMsg("EMBY delete", "Unknown embyId, unable to proceed.", 1) return if utils.settings('skipContextMenu') != "true": resp = xbmcgui.Dialog().yesno( heading="Confirm delete", line1=("Delete file from Emby Server? This will " "also delete the file(s) from disk!")) if not resp: utils.logMsg("EMBY delete", "User skipped deletion for: %s." % embyid, 1) return doUtils = downloadutils.DownloadUtils() url = "{server}/emby/Items/%s?format=json" % embyid utils.logMsg("EMBY delete", "Deleting request: %s" % embyid, 0) doUtils.downloadUrl(url, type="DELETE")
def GetPlexCollections(mediatype): """ Input: mediatype String or list of strings with possible values 'movie', 'show', 'artist', 'photo' Output: List with an entry of the form: { 'name': xxx Plex title for the media section 'type': xxx Plex type: 'movie', 'show', 'artist', 'photo' 'id': xxx Plex unique key for the section (1, 2, 3...) 'uuid': xxx Other unique Plex key, e.g. 74aec9f2-a312-4723-9436-de2ea43843c1 } Returns an empty list if nothing is found. """ collections = [] url = "{server}/library/sections" xml = downloadutils.DownloadUtils().downloadUrl(url) try: xml.attrib except AttributeError: logMsg(title, 'Could not download PMS sections for %s' % url, -1) return {} for item in xml: contentType = item['type'] if contentType in mediatype: name = item['title'] contentId = item['key'] uuid = item['uuid'] collections.append({ 'name': name, 'type': contentType, 'id': str(contentId), 'uuid': uuid }) return collections
def GetPlexMetadata(key): """ Returns raw API metadata for key as an etree XML. Can be called with either Plex key '/library/metadata/xxxx'metadata OR with the digits 'xxxx' only. Returns None if something went wrong """ key = str(key) if '/library/metadata/' in key: url = "{server}" + key else: url = "{server}/library/metadata/" + key arguments = { 'checkFiles': 1, # No idea 'includeExtras': 1, # Trailers and Extras => Extras # 'includeRelated': 1, # Similar movies => Video -> Related # 'includeRelatedCount': 5, # 'includeOnDeck': 1, 'includeChapters': 1, 'includePopularLeaves': 1, 'includeConcerts': 1 } url = url + '?' + urlencode(arguments) xml = downloadutils.DownloadUtils().downloadUrl(url) if xml == 401: # Either unauthorized (taken care of by doUtils) or PMS under strain return 401 # Did we receive a valid XML? try: xml.attrib # Nope we did not receive a valid XML except AttributeError: logMsg(title, "Error retrieving metadata for %s" % url, -1) xml = None return xml
def PMSHttpsEnabled(url): """ Returns True if the PMS wants to talk https, False otherwise. None if error occured, e.g. the connection timed out With with e.g. url=192.168.0.1:32400 (NO http/https) This is done by GET /identity (returns an error if https is enabled and we are trying to use http) Prefers HTTPS over HTTP """ # True if https, False if http answer = True try: # Don't use downloadutils here, otherwise we may get un-authorized! res = requests.get('https://%s/identity' % url, headers={}, verify=False, timeout=(3, 10)) # Don't verify SSL since we can connect for sure then! except requests.exceptions.ConnectionError as e: # Might have SSL deactivated. Try with http try: res = requests.get('http://%s/identity' % url, headers={}, timeout=(3, 10)) except requests.exceptions.ConnectionError as e: logMsg(title, "Server is offline or cannot be reached. Url: %s" ", Error message: %s" % (url, e), -1) return None except requests.exceptions.ReadTimeout: logMsg(title, "Server timeout reached for Url %s" % url, -1) return None else: answer = False except requests.exceptions.ReadTimeout: logMsg(title, "Server timeout reached for Url %s" % url, -1) return None if res.status_code == requests.codes.ok: return answer else: return None
def __init__(self): # Parse parameters base_url = sys.argv[0] params = urlparse.parse_qs(sys.argv[2][1:]) xbmc.log("Parameter string: %s" % sys.argv[2]) try: mode = params["mode"][0] itemid = params.get("id") if itemid: itemid = itemid[0] except: params = {} mode = "" modes = { "reset": utils.reset, "resetauth": entrypoint.resetAuth, "extrafanart": entrypoint.getExtraFanArt, "play": entrypoint.doPlayback, "passwords": utils.passwordsXML, "adduser": entrypoint.addUser, "thememedia": entrypoint.getThemeMedia, "channels": entrypoint.BrowseChannels, "channelsfolder": entrypoint.BrowseChannels, "browsecontent": entrypoint.BrowseContent, "getsubfolders": entrypoint.GetSubFolders, "nextup": entrypoint.getNextUpEpisodes, "inprogressepisodes": entrypoint.getInProgressEpisodes, "recentepisodes": entrypoint.getRecentEpisodes, "refreshplaylist": entrypoint.refreshPlaylist, } if "extrafanart" in sys.argv[0]: entrypoint.getExtraFanArt() if modes.get(mode): # Simple functions if mode == "play": dbid = params.get("dbid") modes[mode](itemid, dbid) elif mode in ("nextup", "inprogressepisodes", "recentepisodes"): limit = int(params["limit"][0]) modes[mode](itemid, limit) elif mode in ["channels", "getsubfolders"]: modes[mode](itemid) elif mode == "browsecontent": modes[mode](itemid, params.get("type", [""])[0], params.get("folderid", [""])[0]) elif mode == "channelsfolder": folderid = params["folderid"][0] modes[mode](itemid, folderid) else: modes[mode]() else: # Other functions if mode == "settings": xbmc.executebuiltin("Addon.OpenSettings(plugin.video.emby)") elif mode in ("manualsync", "repair"): if utils.window("emby_dbScan") != "true": import librarysync lib = librarysync.LibrarySync() if mode == "manualsync": librarysync.ManualSync() else: lib.fullSync(repair=True) else: utils.logMsg("EMBY", "Database scan is already running.", 1) elif mode == "texturecache": import artwork artwork.Artwork().FullTextureCacheSync() else: entrypoint.doMainListing()
def logMsg(msg, lvl=1): utils.logMsg("%s %s" % ("EMBY", "Contextmenu"), msg, lvl)
def logMsg(msg, lvl=1): utils.logMsg("%s %s" % ("PlexKodiConnect", "Contextmenu"), msg, lvl)
def logMsg(msg, lvl=1): utils.logMsg("%s %s" % ("Emby", "musictools"), msg, lvl)
def __init__(self): # Parse parameters base_url = sys.argv[0] params = urlparse.parse_qs(sys.argv[2][1:]) xbmc.log("Parameter string: %s" % sys.argv[2]) try: mode = params['mode'][0] itemid = params.get('id') if itemid: itemid = itemid[0] except: params = {} mode = "" modes = { 'reset': utils.reset, 'resetauth': entrypoint.resetAuth, 'extrafanart': entrypoint.getExtraFanArt, 'play': entrypoint.doPlayback, 'passwords': utils.passwordsXML, 'adduser': entrypoint.addUser, 'thememedia': entrypoint.getThemeMedia, 'channels': entrypoint.BrowseChannels, 'channelsfolder': entrypoint.BrowseChannels, 'browsecontent': entrypoint.BrowseContent, 'getsubfolders': entrypoint.GetSubFolders, 'nextup': entrypoint.getNextUpEpisodes, 'inprogressepisodes': entrypoint.getInProgressEpisodes, 'recentepisodes': entrypoint.getRecentEpisodes, 'refreshplaylist': entrypoint.refreshPlaylist } if "extrafanart" in sys.argv[0]: entrypoint.getExtraFanArt() if modes.get(mode): # Simple functions if mode == "play": dbid = params.get('dbid') modes[mode](itemid, dbid) elif mode in ("nextup", "inprogressepisodes", "recentepisodes"): limit = int(params['limit'][0]) modes[mode](itemid, limit) elif mode in ["channels","getsubfolders"]: modes[mode](itemid) elif mode == "browsecontent": modes[mode]( itemid, params.get('type',[""])[0], params.get('folderid',[""])[0] ) elif mode == "channelsfolder": folderid = params['folderid'][0] modes[mode](itemid, folderid) else: modes[mode]() else: # Other functions if mode == "settings": xbmc.executebuiltin('Addon.OpenSettings(plugin.video.emby)') elif mode in ("manualsync", "repair"): if utils.window('emby_online') != "true": # Server is not online, do not run the sync xbmcgui.Dialog().ok(heading="Emby for Kodi", line1=("Unable to run the sync, the add-on is not " "connected to the Emby server.")) utils.logMsg("EMBY", "Not connected to the emby server.", 1) return if utils.window('emby_dbScan') != "true": import librarysync lib = librarysync.LibrarySync() if mode == "manualsync": librarysync.ManualSync() else: lib.fullSync(repair=True) else: utils.logMsg("EMBY", "Database scan is already running.", 1) elif mode == "texturecache": import artwork artwork.Artwork().FullTextureCacheSync() else: entrypoint.doMainListing()
def logMsg(self, msg, lvl=1): className = self.__class__.__name__ utils.logMsg("%s" % className, msg, lvl)
def DownloadChunks(url, containerSize): """ Downloads PMS url in chunks of containerSize (int). If containerSize is None: ONE xml is fetched directly url MUST end with '?' (if no other url encoded args are present) or '&' Returns a stitched-together xml or None. """ if containerSize is None: # Get rid of '?' or '&' at the end of url xml = downloadutils.DownloadUtils().downloadUrl(url[:-1]) if xml == 401: return 401 try: xml.attrib except AttributeError: # Nope, not an XML, abort logMsg(title, "Error getting url %s" % url[:-1], -1) return None else: return xml xml = None pos = 0 errorCounter = 0 while errorCounter < 10: args = { 'X-Plex-Container-Size': containerSize, 'X-Plex-Container-Start': pos } xmlpart = downloadutils.DownloadUtils().downloadUrl( url + urlencode(args)) # If something went wrong - skip in the hope that it works next time try: xmlpart.attrib except AttributeError: logMsg(title, 'Error while downloading chunks: %s' % (url + urlencode(args)), -1) pos += containerSize errorCounter += 1 continue # Very first run: starting xml (to retain data in xml's root!) if xml is None: xml = deepcopy(xmlpart) if len(xmlpart) < containerSize: break else: pos += containerSize continue # Build answer xml - containing the entire library for child in xmlpart: xml.append(child) # Done as soon as we don't receive a full complement of items if len(xmlpart) < containerSize: break pos += containerSize if errorCounter == 10: logMsg(title, 'Fatal error while downloading chunks for %s' % url, -1) return None return xml
def getSettings(): client = clientinfo.ClientInfo() options = {} title = 'PlexCompanion Settings' options['gdm_debug'] = settings('companionGDMDebugging') options['gdm_debug'] = True if options['gdm_debug'] == 'true' else False options['client_name'] = settings('deviceName') # XBMC web server options options['webserver_enabled'] = (getGUI('webserver') == "true") logMsg(title, 'Webserver is set to %s' % options['webserver_enabled'], 0) webserverport = getGUI('webserverport') try: webserverport = int(webserverport) logMsg(title, 'Using webserver port %s' % str(webserverport), 0) except: logMsg( title, 'No setting for webserver port found in guisettings.xml.' 'Using default fallback port 8080', 0) webserverport = 8080 options['port'] = webserverport options['user'] = getGUI('webserverusername') options['passwd'] = getGUI('webserverpassword') logMsg( title, 'Webserver username: %s, password: %s' % (options['user'], options['passwd']), 1) options['addonName'] = client.getAddonName() options['uuid'] = settings('plex_client_Id') options['platform'] = client.getPlatform() options['version'] = client.getVersion() options['plexbmc_version'] = options['version'] options['myplex_user'] = settings('username') try: options['myport'] = int(settings('companionPort')) logMsg(title, 'Using Plex Companion Port %s' % str(options['myport']), 0) except: logMsg( title, 'Error getting Plex Companion Port from file settings. ' 'Using fallback port 39005', -1) options['myport'] = 39005 return options
def __init__(self): # Parse parameters xbmc.log("PlexKodiConnect - Full sys.argv received: %s" % sys.argv) base_url = sys.argv[0] params = urlparse.parse_qs(sys.argv[2][1:]) xbmc.log("PlexKodiConnect - Parameter string: %s" % sys.argv[2]) try: mode = params['mode'][0] itemid = params.get('id', '') if itemid: try: itemid = itemid[0] except: pass except: params = {} mode = "" modes = { 'reset': utils.reset, 'resetauth': entrypoint.resetAuth, 'play': entrypoint.doPlayback, 'passwords': utils.passwordsXML, 'adduser': entrypoint.addUser, 'thememedia': entrypoint.getThemeMedia, 'channels': entrypoint.BrowseChannels, 'channelsfolder': entrypoint.BrowseChannels, 'browsecontent': entrypoint.BrowseContent, 'getsubfolders': entrypoint.GetSubFolders, 'nextup': entrypoint.getNextUpEpisodes, 'inprogressepisodes': entrypoint.getInProgressEpisodes, 'recentepisodes': entrypoint.getRecentEpisodes, 'refreshplaylist': entrypoint.refreshPlaylist, 'companion': entrypoint.plexCompanion, 'switchuser': entrypoint.switchPlexUser, 'deviceid': entrypoint.resetDeviceId, 'reConnect': entrypoint.reConnect, 'delete': entrypoint.deleteItem, 'browseplex': entrypoint.BrowsePlexContent, 'ondeck': entrypoint.getOnDeck, 'chooseServer': entrypoint.chooseServer, 'watchlater': entrypoint.watchlater } if "/extrafanart" in sys.argv[0]: embypath = sys.argv[2][1:] embyid = params.get('id',[""])[0] entrypoint.getExtraFanArt(embyid,embypath) # Called by e.g. 3rd party plugin video extras if ("/Extras" in sys.argv[0] or "/VideoFiles" in sys.argv[0] or "/Extras" in sys.argv[2]): plexId = params.get('id', [None])[0] entrypoint.getVideoFiles(plexId, params) if modes.get(mode): # Simple functions if mode == "play": dbid = params.get('dbid') # modes[mode](itemid, dbid) modes[mode](itemid, dbid) elif mode in ("nextup", "inprogressepisodes"): limit = int(params['limit'][0]) modes[mode](itemid, limit) elif mode in ["channels","getsubfolders"]: modes[mode](itemid) elif mode == "browsecontent": modes[mode]( itemid, params.get('type',[""])[0], params.get('folderid',[""])[0] ) elif mode == 'browseplex': modes[mode]( itemid, params.get('type', [""])[0], params.get('folderid', [""])[0]) elif mode in ('ondeck', 'recentepisodes'): modes[mode]( itemid, params.get('type', [""])[0], params.get('tagname', [""])[0], int(params.get('limit', [""])[0])) elif mode == "channelsfolder": folderid = params['folderid'][0] modes[mode](itemid, folderid) elif mode == "companion": modes[mode](itemid, params=sys.argv[2]) else: modes[mode]() else: # Other functions if mode == "settings": xbmc.executebuiltin('Addon.OpenSettings(plugin.video.plexkodiconnect)') elif mode in ("manualsync", "repair"): if utils.window('emby_online') != "true": # Server is not online, do not run the sync xbmcgui.Dialog().ok(heading="PlexKodiConnect", line1=("Unable to run the sync, the add-on is not " "connected to the Emby server.")) utils.logMsg("PLEX", "Not connected to the emby server.", 1) return else: if mode == 'repair': utils.window('plex_runLibScan', value="repair") utils.logMsg("PLEX", "Requesting repair lib sync", 1) elif mode == 'manualsync': utils.logMsg("PLEX", "Requesting full library scan", 1) utils.window('plex_runLibScan', value="full") elif mode == "texturecache": import artwork artwork.Artwork().FullTextureCacheSync() else: entrypoint.doMainListing()
def BrowseContent(viewname, type="", folderid=""): emby = embyserver.Read_EmbyServer() art = artwork.Artwork() doUtils = downloadutils.DownloadUtils() #folderid used as filter ? if folderid in ["recent","recentepisodes","inprogress","inprogressepisodes","unwatched","nextepisodes","sets","genres","random","recommended"]: filter = folderid folderid = "" else: filter = "" xbmcplugin.setPluginCategory(int(sys.argv[1]), viewname) #get views for root level if not folderid: views = emby.getViews(type) for view in views: if view.get("name") == viewname.decode('utf-8'): folderid = view.get("id") utils.logMsg("BrowseContent","viewname: %s - type: %s - folderid: %s - filter: %s" %(viewname.decode('utf-8'), type.decode('utf-8'), folderid.decode('utf-8'), filter.decode('utf-8'))) #set the correct params for the content type #only proceed if we have a folderid if folderid: if type.lower() == "homevideos": xbmcplugin.setContent(int(sys.argv[1]), 'episodes') itemtype = "Video,Folder,PhotoAlbum" elif type.lower() == "photos": xbmcplugin.setContent(int(sys.argv[1]), 'files') itemtype = "Photo,PhotoAlbum,Folder" else: itemtype = "" #get the actual listing if type == "recordings": listing = emby.getTvRecordings(folderid) elif type == "tvchannels": listing = emby.getTvChannels() elif filter == "recent": listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[0], sortby="DateCreated", recursive=True, limit=25, sortorder="Descending") elif filter == "random": listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[0], sortby="Random", recursive=True, limit=150, sortorder="Descending") elif filter == "recommended": listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[0], sortby="SortName", recursive=True, limit=25, sortorder="Ascending", filter="IsFavorite") elif filter == "sets": listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[1], sortby="SortName", recursive=True, limit=25, sortorder="Ascending", filter="IsFavorite") else: listing = emby.getFilteredSection(folderid, itemtype=itemtype, recursive=False) #process the listing if listing: for item in listing.get("Items"): li = createListItemFromEmbyItem(item,art,doUtils) if item.get("IsFolder") == True: #for folders we add an additional browse request, passing the folderId path = "%s?id=%s&mode=browsecontent&type=%s&folderid=%s" % (sys.argv[0].decode('utf-8'), viewname.decode('utf-8'), type.decode('utf-8'), item.get("Id").decode('utf-8')) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=path, listitem=li, isFolder=True) else: #playable item, set plugin path and mediastreams xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=li.getProperty("path"), listitem=li) if filter == "recent": xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE) else: xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE) xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE) xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_RATING) xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_RUNTIME) xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
def getThemeMedia(): doUtils = downloadutils.DownloadUtils() dialog = xbmcgui.Dialog() playback = None # Choose playback method resp = dialog.select("Playback method for your themes", ["Direct Play", "Direct Stream"]) if resp == 0: playback = "DirectPlay" elif resp == 1: playback = "DirectStream" else: return library = xbmc.translatePath( "special://profile/addon_data/plugin.video.emby/library/").decode('utf-8') # Create library directory if not xbmcvfs.exists(library): xbmcvfs.mkdir(library) # Set custom path for user tvtunes_path = xbmc.translatePath( "special://profile/addon_data/script.tvtunes/").decode('utf-8') if xbmcvfs.exists(tvtunes_path): tvtunes = xbmcaddon.Addon(id="script.tvtunes") tvtunes.setSetting('custom_path_enable', "true") tvtunes.setSetting('custom_path', library) utils.logMsg("EMBY", "TV Tunes custom path is enabled and set.", 1) else: # if it does not exist this will not work so warn user # often they need to edit the settings first for it to be created. dialog.ok( heading="Warning", line1=( "The settings file does not exist in tvtunes. ", "Go to the tvtunes addon and change a setting, then come back and re-run.")) xbmc.executebuiltin('Addon.OpenSettings(script.tvtunes)') return # Get every user view Id embyconn = utils.kodiSQL('emby') embycursor = embyconn.cursor() emby_db = embydb.Embydb_Functions(embycursor) viewids = emby_db.getViews() embycursor.close() # Get Ids with Theme Videos itemIds = {} for view in viewids: url = "{server}/emby/Users/{UserId}/Items?HasThemeVideo=True&ParentId=%s&format=json" % view result = doUtils.downloadUrl(url) if result['TotalRecordCount'] != 0: for item in result['Items']: itemId = item['Id'] folderName = item['Name'] folderName = utils.normalize_string(folderName.encode('utf-8')) itemIds[itemId] = folderName # Get paths for theme videos for itemId in itemIds: nfo_path = xbmc.translatePath( "special://profile/addon_data/plugin.video.emby/library/%s/" % itemIds[itemId]) # Create folders for each content if not xbmcvfs.exists(nfo_path): xbmcvfs.mkdir(nfo_path) # Where to put the nfos nfo_path = "%s%s" % (nfo_path, "tvtunes.nfo") url = "{server}/emby/Items/%s/ThemeVideos?format=json" % itemId result = doUtils.downloadUrl(url) # Create nfo and write themes to it nfo_file = xbmcvfs.File(nfo_path, 'w') pathstowrite = "" # May be more than one theme for theme in result['Items']: putils = playutils.PlayUtils(theme) if playback == "DirectPlay": playurl = putils.directPlay() else: playurl = putils.directStream() pathstowrite += ('<file>%s</file>' % playurl.encode('utf-8')) # Check if the item has theme songs and add them url = "{server}/emby/Items/%s/ThemeSongs?format=json" % itemId result = doUtils.downloadUrl(url) # May be more than one theme for theme in result['Items']: putils = playutils.PlayUtils(theme) if playback == "DirectPlay": playurl = putils.directPlay() else: playurl = putils.directStream() pathstowrite += ('<file>%s</file>' % playurl.encode('utf-8')) nfo_file.write( '<tvtunes>%s</tvtunes>' % pathstowrite ) # Close nfo file nfo_file.close() # Get Ids with Theme songs musicitemIds = {} for view in viewids: url = "{server}/emby/Users/{UserId}/Items?HasThemeSong=True&ParentId=%s&format=json" % view result = doUtils.downloadUrl(url) if result['TotalRecordCount'] != 0: for item in result['Items']: itemId = item['Id'] folderName = item['Name'] folderName = utils.normalize_string(folderName.encode('utf-8')) musicitemIds[itemId] = folderName # Get paths for itemId in musicitemIds: # if the item was already processed with video themes back out if itemId in itemIds: continue nfo_path = xbmc.translatePath( "special://profile/addon_data/plugin.video.emby/library/%s/" % musicitemIds[itemId]) # Create folders for each content if not xbmcvfs.exists(nfo_path): xbmcvfs.mkdir(nfo_path) # Where to put the nfos nfo_path = "%s%s" % (nfo_path, "tvtunes.nfo") url = "{server}/emby/Items/%s/ThemeSongs?format=json" % itemId result = doUtils.downloadUrl(url) # Create nfo and write themes to it nfo_file = xbmcvfs.File(nfo_path, 'w') pathstowrite = "" # May be more than one theme for theme in result['Items']: putils = playutils.PlayUtils(theme) if playback == "DirectPlay": playurl = putils.directPlay() else: playurl = putils.directStream() pathstowrite += ('<file>%s</file>' % playurl.encode('utf-8')) nfo_file.write( '<tvtunes>%s</tvtunes>' % pathstowrite ) # Close nfo file nfo_file.close() dialog.notification( heading="Emby for Kodi", message="Themes added!", icon="special://home/addons/plugin.video.emby/icon.png", time=1000, sound=False)
def __init__(self): # Parse parameters xbmc.log("PlexKodiConnect - Full sys.argv received: %s" % sys.argv) base_url = sys.argv[0] params = urlparse.parse_qs(sys.argv[2][1:]) xbmc.log("PlexKodiConnect - Parameter string: %s" % sys.argv[2]) try: mode = params['mode'][0] itemid = params.get('id', '') if itemid: try: itemid = itemid[0] except: pass except: params = {} mode = "" modes = { 'reset': utils.reset, 'resetauth': entrypoint.resetAuth, 'play': entrypoint.doPlayback, 'passwords': utils.passwordsXML, 'adduser': entrypoint.addUser, 'thememedia': entrypoint.getThemeMedia, 'channels': entrypoint.BrowseChannels, 'channelsfolder': entrypoint.BrowseChannels, 'browsecontent': entrypoint.BrowseContent, 'getsubfolders': entrypoint.GetSubFolders, 'nextup': entrypoint.getNextUpEpisodes, 'inprogressepisodes': entrypoint.getInProgressEpisodes, 'recentepisodes': entrypoint.getRecentEpisodes, 'refreshplaylist': entrypoint.refreshPlaylist, 'companion': entrypoint.plexCompanion, 'switchuser': entrypoint.switchPlexUser, 'deviceid': entrypoint.resetDeviceId, 'reConnect': entrypoint.reConnect, 'delete': entrypoint.deleteItem, 'browseplex': entrypoint.BrowsePlexContent, 'ondeck': entrypoint.getOnDeck, 'chooseServer': entrypoint.chooseServer, 'watchlater': entrypoint.watchlater } if "/extrafanart" in sys.argv[0]: embypath = sys.argv[2][1:] embyid = params.get('id', [""])[0] entrypoint.getExtraFanArt(embyid, embypath) # Called by e.g. 3rd party plugin video extras if ("/Extras" in sys.argv[0] or "/VideoFiles" in sys.argv[0] or "/Extras" in sys.argv[2]): plexId = params.get('id', [None])[0] entrypoint.getVideoFiles(plexId, params) if modes.get(mode): # Simple functions if mode == "play": dbid = params.get('dbid') # modes[mode](itemid, dbid) modes[mode](itemid, dbid) elif mode in ("nextup", "inprogressepisodes"): limit = int(params['limit'][0]) modes[mode](itemid, limit) elif mode in ["channels", "getsubfolders"]: modes[mode](itemid) elif mode == "browsecontent": modes[mode](itemid, params.get('type', [""])[0], params.get('folderid', [""])[0]) elif mode == 'browseplex': modes[mode](itemid, params.get('type', [""])[0], params.get('folderid', [""])[0]) elif mode in ('ondeck', 'recentepisodes'): modes[mode](itemid, params.get('type', [""])[0], params.get('tagname', [""])[0], int(params.get('limit', [""])[0])) elif mode == "channelsfolder": folderid = params['folderid'][0] modes[mode](itemid, folderid) elif mode == "companion": modes[mode](itemid, params=sys.argv[2]) else: modes[mode]() else: # Other functions if mode == "settings": xbmc.executebuiltin( 'Addon.OpenSettings(plugin.video.plexkodiconnect)') elif mode in ("manualsync", "repair"): if utils.window('emby_online') != "true": # Server is not online, do not run the sync xbmcgui.Dialog().ok( heading="PlexKodiConnect", line1=("Unable to run the sync, the add-on is not " "connected to the Emby server.")) utils.logMsg("PLEX", "Not connected to the emby server.", 1) return else: if mode == 'repair': utils.window('plex_runLibScan', value="repair") utils.logMsg("PLEX", "Requesting repair lib sync", 1) elif mode == 'manualsync': utils.logMsg("PLEX", "Requesting full library scan", 1) utils.window('plex_runLibScan', value="full") elif mode == "texturecache": import artwork artwork.Artwork().FullTextureCacheSync() else: entrypoint.doMainListing()
def logMsg(self, msg, lvl=1): className = self.__class__.__name__ utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
def __init__(self): # Parse parameters base_url = sys.argv[0] params = urlparse.parse_qs(sys.argv[2][1:]) xbmc.log("Parameter string: %s" % sys.argv[2]) try: mode = params['mode'][0] itemid = params.get('id') if itemid: itemid = itemid[0] except: params = {} mode = "" modes = { 'reset': utils.reset, 'resetauth': entrypoint.resetAuth, 'play': entrypoint.doPlayback, 'passwords': utils.passwordsXML, 'adduser': entrypoint.addUser, 'thememedia': entrypoint.getThemeMedia, 'channels': entrypoint.BrowseChannels, 'channelsfolder': entrypoint.BrowseChannels, 'browsecontent': entrypoint.BrowseContent, 'getsubfolders': entrypoint.GetSubFolders, 'nextup': entrypoint.getNextUpEpisodes, 'inprogressepisodes': entrypoint.getInProgressEpisodes, 'recentepisodes': entrypoint.getRecentEpisodes, 'refreshplaylist': entrypoint.refreshPlaylist, 'deviceid': entrypoint.resetDeviceId, 'delete': entrypoint.deleteItem } if "/extrafanart" in sys.argv[0]: embypath = sys.argv[2][1:] embyid = params.get('id', [""])[0] entrypoint.getExtraFanArt(embyid, embypath) if "/Extras" in sys.argv[0] or "/VideoFiles" in sys.argv[0]: embypath = sys.argv[2][1:] embyid = params.get('id', [""])[0] entrypoint.getVideoFiles(embyid, embypath) if modes.get(mode): # Simple functions if mode == "play": dbid = params.get('dbid') modes[mode](itemid, dbid) elif mode in ("nextup", "inprogressepisodes", "recentepisodes"): limit = int(params['limit'][0]) modes[mode](itemid, limit) elif mode in ["channels", "getsubfolders"]: modes[mode](itemid) elif mode == "browsecontent": modes[mode](itemid, params.get('type', [""])[0], params.get('folderid', [""])[0]) elif mode == "channelsfolder": folderid = params['folderid'][0] modes[mode](itemid, folderid) else: modes[mode]() else: # Other functions if mode == "settings": xbmc.executebuiltin('Addon.OpenSettings(plugin.video.emby)') elif mode in ("manualsync", "repair"): if utils.window('emby_online') != "true": # Server is not online, do not run the sync xbmcgui.Dialog().ok( heading="Emby for Kodi", line1=("Unable to run the sync, the add-on is not " "connected to the Emby server.")) utils.logMsg("EMBY", "Not connected to the emby server.", 1) return if utils.window('emby_dbScan') != "true": import librarysync lib = librarysync.LibrarySync() if mode == "manualsync": librarysync.ManualSync().sync(dialog=True) else: lib.fullSync(repair=True) else: utils.logMsg("EMBY", "Database scan is already running.", 1) elif mode == "texturecache": import artwork artwork.Artwork().FullTextureCacheSync() else: entrypoint.doMainListing()
def addUser(): doUtils = downloadutils.DownloadUtils() art = artwork.Artwork() clientInfo = clientinfo.ClientInfo() deviceId = clientInfo.getDeviceId() deviceName = clientInfo.getDeviceName() userid = utils.window('emby_currUser') dialog = xbmcgui.Dialog() # Get session url = "{server}/emby/Sessions?DeviceId=%s&format=json" % deviceId result = doUtils.downloadUrl(url) try: sessionId = result[0]['Id'] additionalUsers = result[0]['AdditionalUsers'] # Add user to session userlist = {} users = [] url = "{server}/emby/Users?IsDisabled=false&IsHidden=false&format=json" result = doUtils.downloadUrl(url) # pull the list of users for user in result: name = user['Name'] userId = user['Id'] if userid != userId: userlist[name] = userId users.append(name) # Display dialog if there's additional users if additionalUsers: option = dialog.select("Add/Remove user from the session", ["Add user", "Remove user"]) # Users currently in the session additionalUserlist = {} additionalUsername = [] # Users currently in the session for user in additionalUsers: name = user['UserName'] userId = user['UserId'] additionalUserlist[name] = userId additionalUsername.append(name) if option == 1: # User selected Remove user resp = dialog.select("Remove user from the session", additionalUsername) if resp > -1: selected = additionalUsername[resp] selected_userId = additionalUserlist[selected] url = "{server}/emby/Sessions/%s/Users/%s" % (sessionId, selected_userId) doUtils.downloadUrl(url, postBody={}, type="DELETE") dialog.notification( heading="Success!", message="%s removed from viewing session" % selected, icon="special://home/addons/plugin.video.emby/icon.png", time=1000) # clear picture position = utils.window('EmbyAdditionalUserPosition.%s' % selected_userId) utils.window('EmbyAdditionalUserImage.%s' % position, clear=True) return else: return elif option == 0: # User selected Add user for adduser in additionalUsername: try: # Remove from selected already added users. It is possible they are hidden. users.remove(adduser) except: pass elif option < 0: # User cancelled return # Subtract any additional users utils.logMsg("EMBY", "Displaying list of users: %s" % users) resp = dialog.select("Add user to the session", users) # post additional user if resp > -1: selected = users[resp] selected_userId = userlist[selected] url = "{server}/emby/Sessions/%s/Users/%s" % (sessionId, selected_userId) doUtils.downloadUrl(url, postBody={}, type="POST") dialog.notification( heading="Success!", message="%s added to viewing session" % selected, icon="special://home/addons/plugin.video.emby/icon.png", time=1000) except: utils.logMsg("EMBY", "Failed to add user to session.") dialog.notification( heading="Error", message="Unable to add/remove user from the session.", icon=xbmcgui.NOTIFICATION_ERROR) # Add additional user images # always clear the individual items first totalNodes = 10 for i in range(totalNodes): if not utils.window('EmbyAdditionalUserImage.%s' % i): break utils.window('EmbyAdditionalUserImage.%s' % i, clear=True) url = "{server}/emby/Sessions?DeviceId=%s" % deviceId result = doUtils.downloadUrl(url) additionalUsers = result[0]['AdditionalUsers'] count = 0 for additionaluser in additionalUsers: userid = additionaluser['UserId'] url = "{server}/emby/Users/%s?format=json" % userid result = doUtils.downloadUrl(url) utils.window('EmbyAdditionalUserImage.%s' % count, value=art.getUserArtwork(result['Id'], 'Primary')) utils.window('EmbyAdditionalUserPosition.%s' % userid, value=str(count)) count +=1