def listArtistsResults(): cm = [] for artist in result['artists']: params = { 'path': 'artist', 'name': utils.tryEncode(artist['name']) } if 'artistId' in artist: params = { 'path': 'search_result', 'artistid': artist['artistId'], 'query': utils.tryEncode(artist['name']) } cm = [(self.lang(30301), "XBMC.RunPlugin(%s?action=play_all&artist_id=%s)" % (utils.addon_url, artist['artistId']))] art = artist[ 'artistArtRef'] if 'artistArtRef' in artist else utils.addon.getAddonInfo( 'icon') listItems.append( self.createFolder(artist['name'], params, cm, arturl=art, fanarturl=art))
def getVideoFiles(plexId, params): """ GET VIDEO EXTRAS FOR LISTITEM returns the video files for the item as plugin listing, can be used for browsing the actual files or videoextras etc. """ if plexId is None: filename = params.get('filename') if filename is not None: filename = filename[0] import re regex = re.compile(r'''library/metadata/(\d+)''') filename = regex.findall(filename) try: plexId = filename[0] except IndexError: pass if plexId is None: log.info('No Plex ID found, abort getting Extras') return xbmcplugin.endOfDirectory(HANDLE) item = GetPlexMetadata(plexId) try: path = item[0][0][0].attrib['file'] except: log.error('Could not get file path for item %s' % plexId) return xbmcplugin.endOfDirectory(HANDLE) # Assign network protocol if path.startswith('\\\\'): path = path.replace('\\\\', 'smb://') path = path.replace('\\', '/') # Plex returns Windows paths as e.g. 'c:\slfkjelf\slfje\file.mkv' elif '\\' in path: path = path.replace('\\', '\\\\') # Directory only, get rid of filename (!! exists() needs / or \ at end) path = path.replace(os_path.basename(path), '') # Only proceed if we can access this folder import xbmcvfs if xbmcvfs.exists(path): # Careful, returns encoded strings! dirs, files = xbmcvfs.listdir(path) for file in files: file = path + tryDecode(file) li = ListItem(file, path=file) xbmcplugin.addDirectoryItem(handle=HANDLE, url=tryEncode(file), listitem=li) for dir in dirs: dir = path + tryDecode(dir) li = ListItem(dir, path=dir) xbmcplugin.addDirectoryItem(handle=HANDLE, url=tryEncode(dir), listitem=li, isFolder=True) else: log.warn('Kodi cannot access folder %s' % path) xbmcplugin.endOfDirectory(HANDLE)
def singleNode(self, indexnumber, tagname, mediatype, itemtype): tagname = tryEncode(tagname) cleantagname = tryDecode(normalize_nodes(tagname)) nodepath = tryDecode( xbmc.translatePath("special://profile/library/video/")) nodeXML = "%splex_%s.xml" % (nodepath, cleantagname) path = "library://video/plex_%s.xml" % cleantagname if v.KODIVERSION >= 17: # Krypton windowpath = "ActivateWindow(Videos,%s,return)" % path else: windowpath = "ActivateWindow(Video,%s,return)" % path # Create the video node directory if not exists_dir(nodepath): # We need to copy over the default items copytree( src=tryDecode( xbmc.translatePath("special://xbmc/system/library/video")), dst=tryDecode( xbmc.translatePath("special://profile/library/video"))) labels = { 'Favorite movies': 30180, 'Favorite tvshows': 30181, 'channels': 30173 } label = lang(labels[tagname]) embynode = "Plex.nodes.%s" % indexnumber window('%s.title' % embynode, value=label) window('%s.path' % embynode, value=windowpath) window('%s.content' % embynode, value=path) window('%s.type' % embynode, value=itemtype) if exists(tryEncode(nodeXML)): # Don't recreate xml if already exists return if itemtype == "channels": root = self.commonRoot(order=1, label=label, tagname=tagname, roottype=2) etree.SubElement( root, 'path' ).text = "plugin://plugin.video.plexkodiconnect/?id=0&mode=channels" else: root = self.commonRoot(order=1, label=label, tagname=tagname) etree.SubElement(root, 'order', { 'direction': "ascending" }).text = "sorttitle" etree.SubElement(root, 'content').text = mediatype try: indent(root) except: pass etree.ElementTree(root).write(nodeXML, encoding="UTF-8")
def getVideoFiles(plexId, params): """ GET VIDEO EXTRAS FOR LISTITEM returns the video files for the item as plugin listing, can be used for browsing the actual files or videoextras etc. """ if plexId is None: filename = params.get('filename') if filename is not None: filename = filename[0] import re regex = re.compile(r'''library/metadata/(\d+)''') filename = regex.findall(filename) try: plexId = filename[0] except IndexError: pass if plexId is None: log.info('No Plex ID found, abort getting Extras') return xbmcplugin.endOfDirectory(HANDLE) item = GetPlexMetadata(plexId) try: path = item[0][0][0].attrib['file'] except: log.error('Could not get file path for item %s' % plexId) return xbmcplugin.endOfDirectory(HANDLE) # Assign network protocol if path.startswith('\\\\'): path = path.replace('\\\\', 'smb://') path = path.replace('\\', '/') # Plex returns Windows paths as e.g. 'c:\slfkjelf\slfje\file.mkv' elif '\\' in path: path = path.replace('\\', '\\\\') # Directory only, get rid of filename path = path.replace(basename(path), '') if exists_dir(path): for root, dirs, files in walk(path): for directory in dirs: item_path = tryEncode(join(root, directory)) li = ListItem(item_path, path=item_path) xbmcplugin.addDirectoryItem(handle=HANDLE, url=item_path, listitem=li, isFolder=True) for file in files: item_path = tryEncode(join(root, file)) li = ListItem(item_path, path=item_path) xbmcplugin.addDirectoryItem(handle=HANDLE, url=file, listitem=li) break else: log.error('Kodi cannot access folder %s' % path) xbmcplugin.endOfDirectory(HANDLE)
def getExtraFanArt(plexid, plexPath): """ Get extrafanart for listitem will be called by skinhelper script to get the extrafanart for tvshows we get the plexid just from the path """ import xbmcvfs log.debug('Called with plexid: %s, plexPath: %s' % (plexid, plexPath)) if not plexid: if "plugin.video.plexkodiconnect" in plexPath: plexid = plexPath.split("/")[-2] if not plexid: log.error('Could not get a plexid, aborting') return xbmcplugin.endOfDirectory(HANDLE) # We need to store the images locally for this to work # because of the caching system in xbmc fanartDir = tryDecode(translatePath( "special://thumbnails/plex/%s/" % plexid)) if not xbmcvfs.exists(fanartDir): # Download the images to the cache directory xbmcvfs.mkdirs(tryEncode(fanartDir)) xml = GetPlexMetadata(plexid) if xml is None: log.error('Could not download metadata for %s' % plexid) return xbmcplugin.endOfDirectory(HANDLE) api = API(xml[0]) backdrops = api.getAllArtwork()['Backdrop'] for count, backdrop in enumerate(backdrops): # Same ordering as in artwork if os_path.supports_unicode_filenames: fanartFile = os_path.join(fanartDir, "fanart%.3d.jpg" % count) else: fanartFile = os_path.join( tryEncode(fanartDir), tryEncode("fanart%.3d.jpg" % count)) li = ListItem("%.3d" % count, path=fanartFile) xbmcplugin.addDirectoryItem( handle=HANDLE, url=fanartFile, listitem=li) xbmcvfs.copy(backdrop, fanartFile) else: log.info("Found cached backdrop.") # Use existing cached images dirs, files = xbmcvfs.listdir(fanartDir) for file in files: fanartFile = os_path.join(fanartDir, tryDecode(file)) li = ListItem(file, path=fanartFile) xbmcplugin.addDirectoryItem(handle=HANDLE, url=fanartFile, listitem=li) xbmcplugin.endOfDirectory(HANDLE)
def getExtraFanArt(plexid, plexPath): """ Get extrafanart for listitem will be called by skinhelper script to get the extrafanart for tvshows we get the plexid just from the path """ import xbmcvfs log.debug('Called with plexid: %s, plexPath: %s' % (plexid, plexPath)) if not plexid: if "plugin.video.plexkodiconnect" in plexPath: plexid = plexPath.split("/")[-2] if not plexid: log.error('Could not get a plexid, aborting') return xbmcplugin.endOfDirectory(HANDLE) # We need to store the images locally for this to work # because of the caching system in xbmc fanartDir = tryDecode( translatePath("special://thumbnails/plex/%s/" % plexid)) if not xbmcvfs.exists(fanartDir): # Download the images to the cache directory xbmcvfs.mkdirs(tryEncode(fanartDir)) xml = GetPlexMetadata(plexid) if xml is None: log.error('Could not download metadata for %s' % plexid) return xbmcplugin.endOfDirectory(HANDLE) api = API(xml[0]) backdrops = api.getAllArtwork()['Backdrop'] for count, backdrop in enumerate(backdrops): # Same ordering as in artwork if os_path.supports_unicode_filenames: fanartFile = os_path.join(fanartDir, "fanart%.3d.jpg" % count) else: fanartFile = os_path.join(tryEncode(fanartDir), tryEncode("fanart%.3d.jpg" % count)) li = ListItem("%.3d" % count, path=fanartFile) xbmcplugin.addDirectoryItem(handle=HANDLE, url=fanartFile, listitem=li) xbmcvfs.copy(backdrop, fanartFile) else: log.info("Found cached backdrop.") # Use existing cached images dirs, files = xbmcvfs.listdir(fanartDir) for file in files: fanartFile = os_path.join(fanartDir, tryDecode(file)) li = ListItem(file, path=fanartFile) xbmcplugin.addDirectoryItem(handle=HANDLE, url=fanartFile, listitem=li) xbmcplugin.endOfDirectory(HANDLE)
def listAlbumsResults(): for album in result['albums']: if 'albumId' in album: listItems.extend(self.createAlbumFolder([album])) else: params = { 'path': 'album', 'album': utils.tryEncode(album['name']), 'artist': utils.tryEncode(album['artist']) } cm = self.getFilterContextMenuItems('album', album['name']) listItems.append( self.createFolder( "[%s] %s" % (album['artist'], album['name']), params, cm, album['albumart'], album['artistart']))
def emit(self, record): if self._get_log_level(record.levelno): try: xbmc.log(self.format(record), level=xbmc.LOGNOTICE) except UnicodeEncodeError: xbmc.log(tryEncode(self.format(record)), level=xbmc.LOGNOTICE)
def single_urlencode(self, text): text = urllib.urlencode({'blahblahblah': utils.tryEncode(text) }) #urlencode needs a utf- string text = text[13:] return utils.tryDecode(text) #return the result again as unicode
def getRealFileName(filename, isTemp=False): #get the filename path accessible by python if possible... if not xbmcvfs.exists(filename): logMsg( "File does not exist! %s" %(filename), 0) return (False, "") #if we use os.path method on older python versions (sunch as some android builds), we need to pass arguments as string if os.path.supports_unicode_filenames: checkfile = filename else: checkfile = utils.tryEncode(filename) # determine if our python module is able to access the file directly... if os.path.exists(checkfile): filename = filename elif os.path.exists(checkfile.replace("smb://","\\\\").replace("/","\\")): filename = filename.replace("smb://","\\\\").replace("/","\\") else: #file can not be accessed by python directly, we copy it for processing... isTemp = True if "/" in filename: filepart = filename.split("/")[-1] else: filepart = filename.split("\\")[-1] tempfile = "special://temp/"+filepart xbmcvfs.copy(filename, tempfile) filename = utils.tryDecode(xbmc.translatePath(tempfile)) return (isTemp,filename)
def play_resume(playqueue, xml, stack): """ If there exists a resume point, Kodi will ask the user whether to continue playback. We thus need to use setResolvedUrl "correctly". Mind that there might be several parts! """ result = Playback_Successful() listitem = PKC_ListItem() # Only get the very first item of our playqueue (i.e. the very first part) stack_item = stack.pop(0) api = API(xml[0]) item = PL.playlist_item_from_xml(playqueue, xml[0], kodi_id=stack_item['kodi_id'], kodi_type=stack_item['kodi_type']) api.setPartNumber(item.part) item.playcount = stack_item['playcount'] item.offset = stack_item['offset'] item.part = stack_item['part'] api.CreateListItemFromPlexItem(listitem) playutils = PlayUtils(api, item) playurl = playutils.getPlayUrl() listitem.setPath(tryEncode(playurl)) if item.playmethod in ('DirectStream', 'DirectPlay'): listitem.setSubtitles(api.externalSubs()) else: playutils.audio_subtitle_prefs(listitem) result.listitem = listitem # Add to our playlist playqueue.items.append(item) # This will release default.py with setResolvedUrl pickle_me(result) # Add remaining parts to the playlist, if any if stack: _process_stack(playqueue, stack)
def getCategoryStations(self, items): listItems = [] default_thumb = utils.addon.getAddonInfo('icon') default_fanart = utils.addon.getAddonInfo('fanart') for item in items: #utils.log("STATION: "+repr(item)) params = { 'path': 'create_station', 'name': utils.tryEncode(item['name']) } params.update(self.getStationSeed(item['seed'])) url1 = item['compositeArtRefs'][0][ 'url'] if 'compositeArtRefs' in item and item[ 'compositeArtRefs'] else default_thumb url2 = item['imageUrls'][0]['url'] if 'imageUrls' in item and item[ 'imageUrls'] else default_fanart folder = self.createFolder(item['name'], params, arturl=url1, fanarturl=url2) folder[1].setInfo( type='Music', infoLabels={ 'comment': item.get('description', 'No description'), 'date': time.strftime( '%d.%m.%Y', time.gmtime(item.get('recentTimestamp', 0) / 1000000)) }) listItems.append(folder) return listItems
def getRealFileName(filename, isTemp=False): #get the filename path accessible by python if possible... if not xbmcvfs.exists(filename): logMsg("File does not exist! %s" % (filename), 0) return (False, "") #if we use os.path method on older python versions (sunch as some android builds), we need to pass arguments as string if os.path.supports_unicode_filenames: checkfile = filename else: checkfile = utils.tryEncode(filename) # determine if our python module is able to access the file directly... if os.path.exists(checkfile): filename = filename elif os.path.exists( checkfile.replace("smb://", "\\\\").replace("/", "\\")): filename = filename.replace("smb://", "\\\\").replace("/", "\\") else: #file can not be accessed by python directly, we copy it for processing... isTemp = True if "/" in filename: filepart = filename.split("/")[-1] else: filepart = filename.split("\\")[-1] tempfile = "special://temp/" + filepart xbmcvfs.copy(filename, tempfile) filename = utils.tryDecode(xbmc.translatePath(tempfile)) return (isTemp, filename)
def getRadioContextMenuItems(self, name, radio_id): cm = [] name = quote_plus(utils.tryEncode(name)) cm.append((self.lang(30301), "XBMC.RunPlugin(%s?action=play_all&radio_id=%s)" % (utils.addon_url, radio_id))) cm.append( (self.lang(30302), "XBMC.RunPlugin(%s?action=play_all&radio_id=%s&shuffle=true)" % (utils.addon_url, radio_id))) cm.append((self.lang(30312), "XBMC.RunPlugin(%s?action=play_all_yt&radio_id=%s)" % (utils.addon_url, radio_id))) cm.append( (self.lang(30321), "XBMC.RunPlugin(%s?action=play_all_yt&radio_id=%s&shuffle=true)" % (utils.addon_url, radio_id))) cm.append((self.lang( 30306 ), "XBMC.RunPlugin(%s?action=add_favourite&path=playlist&radio_id=%s&title=%s)" % (utils.addon_url, radio_id, name))) cm.append((self.lang(30315), "XBMC.RunPlugin(%s?action=add_to_queue&radio_id=%s)" % (utils.addon_url, radio_id))) cm.append( (self.lang(30318), "XBMC.RunPlugin(%s?action=delete_station&radio_id=%s&title=%s)" % (utils.addon_url, radio_id, name))) return cm
def __repr__(self): answ = "<%s: " % (self.__class__.__name__) for key in self.__dict__: if type(getattr(self, key)) in (str, unicode): answ += '%s: %s, ' % (key, tryEncode(getattr(self, key))) else: # e.g. int answ += '%s: %s, ' % (key, str(getattr(self, key))) return answ[:-2] + ">"
def listAlbumsResults(): listItems.append(self.createFolder('[COLOR orange]*** '+self.lang(30206)+' ***[/COLOR]',{'path':'none'})) cm = [] for album in result['albums']: params = {'path':"search_result",'query':utils.tryEncode(album[0])} if len(album) > 3: cm = [(self.lang(30301), "XBMC.RunPlugin(%s?action=play_all&album_id=%s)" % (utils.addon_url, album[3]))] params['albumid'] = album[3] listItems.append(self.createFolder("[%s] %s"%(album[1], album[0]), params, cm, album[2]))
def getPlayUrl(self, partNumber=None): """ Returns the playurl for the part with number partNumber (movie might consist of several files) playurl is utf-8 encoded! """ self.API.setPartNumber(partNumber) self.API.getMediastreamNumber() playurl = self.isDirectPlay() if playurl is not None: log.info("File is direct playing.") playurl = tryEncode(playurl) # Set playmethod property window('plex_%s.playmethod' % playurl, "DirectPlay") elif self.isDirectStream(): log.info("File is direct streaming.") playurl = tryEncode(self.API.getTranscodeVideoPath('DirectStream')) # Set playmethod property window('plex_%s.playmethod' % playurl, "DirectStream") else: log.info("File is transcoding.") playurl = tryEncode( self.API.getTranscodeVideoPath( 'Transcode', quality={ 'maxVideoBitrate': self.get_bitrate(), 'videoResolution': self.get_resolution(), 'videoQuality': '100', 'mediaBufferSize': int(settings('kodi_video_cache')) / 1024, })) # Set playmethod property window('plex_%s.playmethod' % playurl, value="Transcode") log.info("The playurl is: %s" % playurl) return playurl
def _AutoPickPMS(self): """ Will try to pick PMS based on machineIdentifier saved in file settings but only once Returns server or None if unsuccessful """ httpsUpdated = False checkedPlexTV = False server = None while True: if httpsUpdated is False: serverlist = self._getServerList() 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._checkServerCon(server) if chk == 504 and httpsUpdated is False: # Not able to use HTTP, try HTTPs for now server['scheme'] = 'https' httpsUpdated = True continue if chk == 401: LOG.warn('Not yet authorized for Plex server %s', server['name']) if self.CheckPlexTVSignIn() is True: if checkedPlexTV is False: # Try again checkedPlexTV = True httpsUpdated = False continue else: LOG.warn('Not authorized even though we are signed ' ' in to plex.tv correctly') self.dialog.ok( lang(29999), '%s %s' % (lang(39214), tryEncode(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 getSongContextMenu(self, song_id, title, display_name, song_type): cm = [] title = quote_plus(utils.tryEncode(title)) display_name = quote_plus(utils.tryEncode(display_name)) if song_id.startswith('T'): cm.append((self.lang(30309), "XBMC.RunPlugin(%s?action=add_library&song_id=%s)" % (utils.addon_url, song_id))) cm.append((self.lang(30319), "XBMC.RunPlugin(%s?action=artist_topsongs&song_id=%s)" % (utils.addon_url, song_id))) cm.append((self.lang(30320), "XBMC.RunPlugin(%s?action=related_artists&song_id=%s)" % (utils.addon_url, song_id))) if song_type == 'library': cm.append((self.lang(30307), "XBMC.RunPlugin(%s?action=add_playlist&song_id=%s)" % (utils.addon_url, song_id))) elif song_type.startswith('playlist'): pl_id = song_type[8:] cm.append((self.lang( 30322 ), "XBMC.RunPlugin(%s?action=play_all&from_here=%s&playlist_id=%s)" % (utils.addon_url, song_id, pl_id))) cm.append((self.lang( 30308 ), "XBMC.RunPlugin(%s?action=del_from_playlist&song_id=%s&playlist_id=%s)" % (utils.addon_url, song_id, pl_id))) cm.append((self.lang(30409), "XBMC.RunPlugin(%s?action=set_thumbs&song_id=%s)" % (utils.addon_url, song_id))) cm.append((self.lang(30313), "XBMC.RunPlugin(%s?action=play_yt&display_name=%s)" % (utils.addon_url, display_name))) cm.append((self.lang(30311), "XBMC.RunPlugin(%s?action=search_yt&display_name=%s)" % (utils.addon_url, display_name))) cm.append( (self.lang(30310), "XBMC.RunPlugin(%s?action=start_radio&song_id=%s&title=%s)" % (utils.addon_url, song_id, title))) return cm
def singleNode(self, indexnumber, tagname, mediatype, itemtype): tagname = tryEncode(tagname) cleantagname = normalize_nodes(tagname) nodepath = tryDecode(xbmc.translatePath( "special://profile/library/video/")) nodeXML = "%splex_%s.xml" % (nodepath, cleantagname) path = "library://video/plex_%s.xml" % cleantagname if self.kodiversion >= 17: # Krypton windowpath = "ActivateWindow(Videos,%s,return)" % path else: windowpath = "ActivateWindow(Video,%s,return)" % path # Create the video node directory if not xbmcvfs.exists(nodepath): # We need to copy over the default items shutil.copytree( src=tryDecode(xbmc.translatePath( "special://xbmc/system/library/video")), dst=tryDecode(xbmc.translatePath( "special://profile/library/video"))) xbmcvfs.exists(path) labels = { 'Favorite movies': 30180, 'Favorite tvshows': 30181, 'channels': 30173 } label = lang(labels[tagname]) embynode = "Plex.nodes.%s" % indexnumber window('%s.title' % embynode, value=label) window('%s.path' % embynode, value=windowpath) window('%s.content' % embynode, value=path) window('%s.type' % embynode, value=itemtype) if xbmcvfs.exists(nodeXML): # Don't recreate xml if already exists return if itemtype == "channels": root = self.commonRoot(order=1, label=label, tagname=tagname, roottype=2) etree.SubElement(root, 'path').text = "plugin://plugin.video.plexkodiconnect/?id=0&mode=channels" else: root = self.commonRoot(order=1, label=label, tagname=tagname) etree.SubElement(root, 'order', {'direction': "ascending"}).text = "sorttitle" etree.SubElement(root, 'content').text = mediatype try: indent(root) except: pass etree.ElementTree(root).write(nodeXML)
def _AutoPickPMS(self): """ Will try to pick PMS based on machineIdentifier saved in file settings but only once Returns server or None if unsuccessful """ httpsUpdated = False checkedPlexTV = False server = None while True: if httpsUpdated is False: serverlist = self._getServerList() 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._checkServerCon(server) if chk == 504 and httpsUpdated is False: # Not able to use HTTP, try HTTPs for now server['scheme'] = 'https' httpsUpdated = True continue if chk == 401: log.warn('Not yet authorized for Plex server %s' % server['name']) if self.CheckPlexTVSignIn() is True: if checkedPlexTV is False: # Try again checkedPlexTV = True httpsUpdated = False continue else: log.warn('Not authorized even though we are signed ' ' in to plex.tv correctly') self.dialog.ok(lang(29999), '%s %s' % (lang(39214), tryEncode(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 getPlayUrl(self, partNumber=None): """ Returns the playurl for the part with number partNumber (movie might consist of several files) playurl is utf-8 encoded! """ log = self.logMsg window = utils.window self.API.setPartNumber(partNumber) playurl = self.isDirectPlay() if playurl: log("File is direct playing.", 1) playurl = utils.tryEncode(playurl) # Set playmethod property window('emby_%s.playmethod' % playurl, "DirectPlay") elif self.isDirectStream(): self.logMsg("File is direct streaming.", 1) playurl = utils.tryEncode( self.API.getTranscodeVideoPath('DirectStream')) # Set playmethod property utils.window('emby_%s.playmethod' % playurl, "DirectStream") else: self.logMsg("File is transcoding.", 1) quality = { 'maxVideoBitrate': self.getBitrate(), 'videoResolution': self.getResolution(), 'videoQuality': '100' } playurl = utils.tryEncode(self.API.getTranscodeVideoPath( 'Transcode', quality=quality)) # Set playmethod property window('emby_%s.playmethod' % playurl, value="Transcode") log("The playurl is: %s" % playurl, 1) return playurl
def getPlayUrl(self, partNumber=None): """ Returns the playurl for the part with number partNumber (movie might consist of several files) playurl is utf-8 encoded! """ log = self.logMsg window = utils.window self.API.setPartNumber(partNumber) playurl = self.isDirectPlay() if playurl: log("File is direct playing.", 1) playurl = utils.tryEncode(playurl) # Set playmethod property window('emby_%s.playmethod' % playurl, "DirectPlay") elif self.isDirectStream(): self.logMsg("File is direct streaming.", 1) playurl = utils.tryEncode( self.API.getTranscodeVideoPath('DirectStream')) # Set playmethod property utils.window('emby_%s.playmethod' % playurl, "DirectStream") else: self.logMsg("File is transcoding.", 1) quality = { 'maxVideoBitrate': self.getBitrate(), 'videoResolution': self.getResolution(), 'videoQuality': '100' } playurl = utils.tryEncode( self.API.getTranscodeVideoPath('Transcode', quality=quality)) # Set playmethod property window('emby_%s.playmethod' % playurl, value="Transcode") log("The playurl is: %s" % playurl, 1) return playurl
def __repr__(self): answ = "<%s: " % (self.__class__.__name__) # For some reason, can't use dir directly answ += "ID: %s, " % self.ID answ += "items: %s, " % self.items for key in self.__dict__: if key not in ("ID", 'items'): if type(getattr(self, key)) in (str, unicode): answ += '%s: %s, ' % (key, tryEncode(getattr(self, key))) else: # e.g. int answ += '%s: %s, ' % (key, str(getattr(self, key))) return answ[:-2] + ">"
def getFilterContextMenuItems(self, filter_type, filter_criteria, artist=''): cm = [] filter_criteria = quote_plus(utils.tryEncode(filter_criteria)) #filter_criteria = quote_plus(filter_criteria.encode('utf-8'), safe=':/'.encode('utf-8')) artist = quote_plus(utils.tryEncode(artist)) cm.append( (self.lang(30306), "XBMC.RunPlugin(%s?action=add_favourite&path=%s&name=%s&title=%s)" % (utils.addon_url, filter_type, filter_criteria, filter_criteria))) cm.append((self.lang( 30301 ), "XBMC.RunPlugin(%s?action=play_all&filter_type=%s&filter_criteria=%s&artist=%s)" % (utils.addon_url, filter_type, filter_criteria, artist))) cm.append((self.lang( 30302 ), "XBMC.RunPlugin(%s?action=play_all&filter_type=%s&filter_criteria=%s&shuffle=true&artist=%s)" % (utils.addon_url, filter_type, filter_criteria, artist))) cm.append((self.lang( 30312 ), "XBMC.RunPlugin(%s?action=play_all_yt&filter_type=%s&filter_criteria=%s&artist=%s)" % (utils.addon_url, filter_type, filter_criteria, artist))) cm.append((self.lang( 30321 ), "XBMC.RunPlugin(%s?action=play_all_yt&filter_type=%s&filter_criteria=%s&shuffle=true&artist=%s)" % (utils.addon_url, filter_type, filter_criteria, artist))) cm.append((self.lang( 30208 ), "XBMC.RunPlugin(%s?action=search&filter_type=%s&filter_criteria=%s&artist=%s)" % (utils.addon_url, filter_type, filter_criteria, artist))) cm.append((self.lang( 30315 ), "XBMC.RunPlugin(%s?action=add_to_queue&filter_type=album&filter_criteria=%s&artist=%s)" % (utils.addon_url, filter_criteria, artist))) return cm
def _addtoPlaylist(self, dbid=None, mediatype=None, url=None): pl = { 'jsonrpc': "2.0", 'id': 1, 'method': "Playlist.Add", 'params': { 'playlistid': self.playlistId } } if dbid is not None: pl['params']['item'] = {'%sid' % tryEncode(mediatype): int(dbid)} else: pl['params']['item'] = {'file': url} log.debug(xbmc.executeJSONRPC(json.dumps(pl)))
def create_frame(data, opcode): """ create frame to send text, binary and other data. data: data to send. This is string value(byte array). if opcode is OPCODE_TEXT and this value is uniocde, data value is conveted into unicode string, automatically. opcode: operation code. please see OPCODE_XXX. """ if opcode == ABNF.OPCODE_TEXT and isinstance(data, unicode): data = utils.tryEncode(data) # mask must be set if send data from client return ABNF(1, 0, 0, 0, opcode, 1, data)
def conclude_playback(playqueue, pos): """ ONLY if actually being played (e.g. at 5th position of a playqueue). Decide on direct play, direct stream, transcoding path to direct paths: file itself PMS URL Web URL audiostream (e.g. let user choose) subtitle stream (e.g. let user choose) Init Kodi Playback (depending on situation): start playback return PKC listitem attached to result """ LOG.info('Concluding playback for playqueue position %s', pos) result = Playback_Successful() listitem = PKC_ListItem() item = playqueue.items[pos] if item.xml is not None: # Got a Plex element api = API(item.xml) api.setPartNumber(item.part) api.CreateListItemFromPlexItem(listitem) playutils = PlayUtils(api, item) playurl = playutils.getPlayUrl() else: playurl = item.file listitem.setPath(tryEncode(playurl)) if item.playmethod in ('DirectStream', 'DirectPlay'): listitem.setSubtitles(api.externalSubs()) else: playutils.audio_subtitle_prefs(listitem) if state.RESUME_PLAYBACK is True: state.RESUME_PLAYBACK = False if (item.offset is None and item.plex_type not in (v.PLEX_TYPE_SONG, v.PLEX_TYPE_CLIP)): with plexdb.Get_Plex_DB() as plex_db: plex_dbitem = plex_db.getItem_byId(item.plex_id) file_id = plex_dbitem[1] if plex_dbitem else None with kodidb.GetKodiDB('video') as kodi_db: item.offset = kodi_db.get_resume(file_id) LOG.info('Resuming playback at %s', item.offset) listitem.setProperty('StartOffset', str(item.offset)) listitem.setProperty('resumetime', str(item.offset)) # Reset the resumable flag state.RESUMABLE = False result.listitem = listitem pickle_me(result) LOG.info('Done concluding playback')
def getPlaylistsContextMenuItems(self, name, playlist_type): cm = [] name = quote_plus(utils.tryEncode(name)) cm.append( (self.lang(30304), "XBMC.RunPlugin(%s?action=update_playlists&playlist_type=%s)" % (utils.addon_url, playlist_type))) cm.append((self.lang( 30306 ), "XBMC.RunPlugin(%s?action=add_favourite&path=playlists&playlist_type=%s&title=%s)" % (utils.addon_url, playlist_type, name))) cm.append( (self.lang(30316), "XBMC.RunPlugin(%s?action=create_playlist)" % utils.addon_url)) return cm
def __repr__(self): """ Print the playlist, e.g. to log """ answ = '{\'%s\': {' % (self.__class__.__name__) # For some reason, can't use dir directly answ += '\'id\': %s, ' % self.id for key in self.__dict__: if key in ('id', 'items', 'kodi_pl'): continue if isinstance(getattr(self, key), (str, unicode)): answ += '\'%s\': \'%s\', ' % (key, tryEncode(getattr(self, key))) else: # e.g. int answ += '\'%s\': %s, ' % (key, str(getattr(self, key))) return answ + '\'items\': %s}}' % self.items
def _prep_playlist_stack(xml): stack = [] for item in xml: api = API(item) if (state.CONTEXT_MENU_PLAY is False and api.getType() != v.PLEX_TYPE_CLIP): # If user chose to play via PMS or force transcode, do not # use the item path stored in the Kodi DB with plexdb.Get_Plex_DB() as plex_db: plex_dbitem = plex_db.getItem_byId(api.getRatingKey()) kodi_id = plex_dbitem[0] if plex_dbitem else None kodi_type = plex_dbitem[4] if plex_dbitem else None else: # We will never store clips (trailers) in the Kodi DB kodi_id = None kodi_type = None for part, _ in enumerate(item[0]): api.setPartNumber(part) if kodi_id is None: # Need to redirect again to PKC to conclude playback params = { 'mode': 'play', 'plex_id': api.getRatingKey(), 'plex_type': api.getType() } path = ('plugin://plugin.video.plexkodiconnect?%s' % urlencode(params)) listitem = api.CreateListItemFromPlexItem() listitem.setPath(tryEncode(path)) else: # Will add directly via the Kodi DB path = None listitem = None stack.append({ 'kodi_id': kodi_id, 'kodi_type': kodi_type, 'file': path, 'xml_video_element': item, 'listitem': listitem, 'part': part, 'playcount': api.getViewCount(), 'offset': api.getResume(), 'id': api.getItemId() }) return stack
def add_to_Kodi_playlist(playlist, xml_video_element): """ Adds a new item to the Kodi playlist via JSON (at the end of the playlist). Pass in the PMS xml's video element (one level underneath MediaContainer). Returns a Playlist_Item or None if it did not work """ item = playlist_item_from_xml(playlist, xml_video_element) params = {'playlistid': playlist.playlistid} if item.kodi_id: params['item'] = {'%sid' % item.kodi_type: item.kodi_id} else: params['item'] = {'file': tryEncode(item.file)} reply = JSONRPC('Playlist.Add').execute(params) if reply.get('error') is not None: log.error('Could not add item %s to Kodi playlist. Error: %s' % (xml_video_element, reply)) return None else: return item
def __repr__(self): """ Print the playlist item, e.g. to log """ answ = '{\'%s\': {' % (self.__class__.__name__) answ += '\'id\': \'%s\', ' % self.id answ += '\'plex_id\': \'%s\', ' % self.plex_id for key in self.__dict__: if key in ('id', 'plex_id', 'xml'): continue if isinstance(getattr(self, key), (str, unicode)): answ += '\'%s\': \'%s\', ' % (key, tryEncode(getattr(self, key))) else: # e.g. int answ += '\'%s\': %s, ' % (key, str(getattr(self, key))) if self.xml is None: answ += '\'xml\': None}}' else: answ += '\'xml\': \'%s\'}}' % self.xml.tag return answ
def getListennow(self, items): listItems = [] default_art = utils.addon.getAddonInfo('icon') for item in items: suggestion = item.get('suggestion_text') image = item['images'][0][ 'url'] if 'images' in item else default_art if item['type'] == '1': album = item['album'] listItems.extend( self.createAlbumFolder([{ 'name': album['title'] + ' (' + suggestion + ')', 'artist': album['artist_name'], 'albumArtRef': image, 'albumId': album['id']['metajamCompactKey'] }])) elif item['type'] == '3': radio = item['radio_station'] params = { 'path': 'create_station', 'name': utils.tryEncode('Radio %s (%s)' % (radio['title'], suggestion)) } params.update(self.getStationSeed(radio['id']['seeds'][0])) listItems.append( self.createFolder(params['name'], params, arturl=image)) else: utils.log("ERROR item type unknown " + repr(item['type'])) return listItems
def getSearch(self, query): listItems = [] def listAlbumsResults(): listItems.append(self.createFolder('[COLOR orange]*** '+self.lang(30206)+' ***[/COLOR]',{'path':'none'})) cm = [] for album in result['albums']: params = {'path':"search_result",'query':utils.tryEncode(album[0])} if len(album) > 3: cm = [(self.lang(30301), "XBMC.RunPlugin(%s?action=play_all&album_id=%s)" % (utils.addon_url, album[3]))] params['albumid'] = album[3] listItems.append(self.createFolder("[%s] %s"%(album[1], album[0]), params, cm, album[2])) if isinstance(query,basestring): result = self.api.getSearch(query) if result['albums']: listAlbumsResults() if result['artists']: listItems.append(self.createFolder('[COLOR orange]*** '+self.lang(30205)+' ***[/COLOR]',{'path':'none'})) cm = [] for artist in result['artists']: params = {'path':"search_result",'query':utils.tryEncode(artist[0])} if len(artist) > 2: cm = [(self.lang(30301), "XBMC.RunPlugin(%s?action=play_all&artist_id=%s)" % (utils.addon_url, artist[2]))] params['artistid'] = artist[2] listItems.append(self.createFolder(artist[0], params, cm, artist[1])) if result['tracks']: listItems.append(self.createFolder('[COLOR orange]*** '+self.lang(30213)+' ***[/COLOR]',{'path':'none'})) listItems.extend(self.addSongsFromLibrary(result['tracks'], 'library')) elif 'artistid' in query: result = self.api.getSearch(unquote_plus(query['query'])) if result['albums']: listAlbumsResults() listItems.append(self.createFolder('[COLOR orange]*** '+self.lang(30213)+' ***[/COLOR]',{'path':'none'})) listItems.extend(self.addSongsFromLibrary(self.api.getArtist(query['artistid']), 'library')) elif 'albumid' in query: listItems.extend(self.addSongsFromLibrary(self.api.getAlbum(query['albumid']), 'library')) else: #listItems.extend(self.addSongsFromLibrary(self.api.getSearch(unquote_plus(query['query']))['tracks'], 'library')) listItems.extend(self.getSearch(unquote_plus(query['query']))) return listItems
def audioSubsPref(self, listitem, url, part=None): lang = utils.language dialog = xbmcgui.Dialog() # For transcoding only # Present the list of audio to select from audioStreamsList = [] audioStreams = [] # audioStreamsChannelsList = [] subtitleStreamsList = [] subtitleStreams = ['1 No subtitles'] downloadableStreams = [] # selectAudioIndex = "" selectSubsIndex = "" playurlprefs = {} # Set part where we're at self.API.setPartNumber(part) if part is None: part = 0 try: mediastreams = self.item[0][part] except (TypeError, KeyError, IndexError): return url audioNum = 0 # Remember 'no subtitles' subNum = 1 for stream in mediastreams: # Since Emby returns all possible tracks together, have to sort them. index = stream.attrib.get('id') type = stream.attrib.get('streamType') # Audio if type == "2": codec = stream.attrib.get('codec') channelLayout = stream.attrib.get('audioChannelLayout', "") try: track = "%s %s - %s %s" % (audioNum+1, stream.attrib['language'], codec, channelLayout) except: track = "%s 'unknown' - %s %s" % (audioNum+1, codec, channelLayout) #audioStreamsChannelsList[audioNum] = stream.attrib['channels'] audioStreamsList.append(index) audioStreams.append(utils.tryEncode(track)) audioNum += 1 # Subtitles elif type == "3": '''if stream['IsExternal']: continue''' try: track = "%s %s" % (subNum+1, stream.attrib['language']) except: track = "%s 'unknown' (%s)" % (subNum+1, stream.attrib.get('codec')) default = stream.attrib.get('default') forced = stream.attrib.get('forced') downloadable = stream.attrib.get('key') if default: track = "%s - Default" % track if forced: track = "%s - Forced" % track if downloadable: downloadableStreams.append(index) subtitleStreamsList.append(index) subtitleStreams.append(utils.tryEncode(track)) subNum += 1 if audioNum > 1: resp = dialog.select(lang(33013), audioStreams) if resp > -1: # User selected audio playurlprefs['audioStreamID'] = audioStreamsList[resp] else: # User backed out of selection - let PMS decide pass else: # There's only one audiotrack. playurlprefs['audioStreamID'] = audioStreamsList[0] # Add audio boost playurlprefs['audioBoost'] = utils.settings('audioBoost') if subNum > 1: resp = dialog.select(lang(33014), subtitleStreams) if resp == 0: # User selected no subtitles playurlprefs["skipSubtitles"] = 1 elif resp > -1: # User selected subtitles selectSubsIndex = subtitleStreamsList[resp-1] # Load subtitles in the listitem if downloadable if selectSubsIndex in downloadableStreams: url = "%s/library/streams/%s" \ % (self.server, selectSubsIndex) url = self.API.addPlexHeadersToUrl(url) self.logMsg("Downloadable sub: %s: %s" % (selectSubsIndex, url), 1) listitem.setSubtitles([utils.tryEncode(url)]) else: self.logMsg('Need to burn in subtitle %s' % selectSubsIndex, 1) playurlprefs["subtitleStreamID"] = selectSubsIndex playurlprefs["subtitleSize"] = utils.settings('subtitleSize') else: # User backed out of selection pass # Tell the PMS what we want with a PUT request # url = self.server + '/library/parts/' + self.item[0][part].attrib['id'] # PlexFunctions.SelectStreams(url, playurlprefs) url += '&' + urlencode(playurlprefs) # Get number of channels for selected audio track # audioChannels = audioStreamsChannelsList.get(selectAudioIndex, 0) # if audioChannels > 2: # playurlprefs += "&AudioBitrate=384000" # else: # playurlprefs += "&AudioBitrate=192000" return url
def viewNode(self, indexnumber, tagname, mediatype, viewtype, viewid, delete=False): # Plex: reassign mediatype due to Kodi inner workings # How many items do we get at most? limit = "100" mediatypes = { 'movie': 'movies', 'show': 'tvshows', 'photo': 'photos', 'homevideo': 'homevideos', 'musicvideos': 'musicvideos' } mediatype = mediatypes[mediatype] if viewtype == "mixed": dirname = "%s-%s" % (viewid, mediatype) else: dirname = viewid path = tryDecode(xbmc.translatePath( "special://profile/library/video/")) nodepath = tryDecode(xbmc.translatePath( "special://profile/library/video/Plex-%s/" % dirname)) # Verify the video directory # KODI BUG # Kodi caches the result of exists for directories # so try creating a file if IfExists(path) is False: shutil.copytree( src=tryDecode(xbmc.translatePath( "special://xbmc/system/library/video")), dst=tryDecode(xbmc.translatePath( "special://profile/library/video"))) # Create the node directory if mediatype != "photos": if IfExists(nodepath) is False: # folder does not exist yet log.debug('Creating folder %s' % nodepath) xbmcvfs.mkdirs(tryEncode(nodepath)) if delete: dirs, files = xbmcvfs.listdir(tryEncode(nodepath)) for file in files: xbmcvfs.delete(tryEncode( (nodepath + tryDecode(file)))) log.info("Sucessfully removed videonode: %s." % tagname) return # Create index entry nodeXML = "%sindex.xml" % nodepath # Set windows property path = "library://video/Plex-%s/" % dirname for i in range(1, indexnumber): # Verify to make sure we don't create duplicates if window('Plex.nodes.%s.index' % i) == path: return if mediatype == "photos": path = "plugin://plugin.video.plexkodiconnect/?id=%s&mode=getsubfolders" % indexnumber window('Plex.nodes.%s.index' % indexnumber, value=path) # Root if not mediatype == "photos": if viewtype == "mixed": specialtag = "%s-%s" % (tagname, mediatype) root = self.commonRoot(order=0, label=specialtag, tagname=tagname, roottype=0) else: root = self.commonRoot(order=0, label=tagname, tagname=tagname, roottype=0) try: indent(root) except: pass etree.ElementTree(root).write(nodeXML) nodetypes = { '1': "all", '2': "recent", '3': "recentepisodes", '4': "inprogress", '5': "inprogressepisodes", '6': "unwatched", '7': "nextepisodes", '8': "sets", '9': "genres", '10': "random", '11': "recommended", '12': "ondeck" } mediatypes = { # label according to nodetype per mediatype 'movies': { '1': tagname, '2': 30174, # '4': 30177, # '6': 30189, '8': 39501, '9': 135, '10': 30227, '11': 30230, '12': 39500, }, 'tvshows': { '1': tagname, # '2': 30170, '3': 30174, # '4': 30171, # '5': 30178, # '7': 30179, '9': 135, '10': 30227, # '11': 30230, '12': 39500, }, 'homevideos': { '1': tagname, '2': 30251, '11': 30253 }, 'photos': { '1': tagname, '2': 30252, '8': 30255, '11': 30254 }, 'musicvideos': { '1': tagname, '2': 30256, '4': 30257, '6': 30258 } } # Key: nodetypes, value: sort order in Kodi sortorder = { '1': '3', # "all", '2': '2', # "recent", '3': '2', # "recentepisodes", # '4': # "inprogress", # '5': # "inprogressepisodes", # '6': # "unwatched", # '7': # "nextepisodes", '8': '7', # "sets", '9': '6', # "genres", '10': '8', # "random", '11': '5', # "recommended", '12': '1', # "ondeck" } nodes = mediatypes[mediatype] for node in nodes: nodetype = nodetypes[node] nodeXML = "%s%s_%s.xml" % (nodepath, viewid, nodetype) # Get label stringid = nodes[node] if node != "1": label = lang(stringid) if not label: label = xbmc.getLocalizedString(stringid) else: label = stringid # Set window properties if (mediatype == "homevideos" or mediatype == "photos") and nodetype == "all": # Custom query path = ("plugin://plugin.video.plexkodiconnect/?id=%s&mode=browseplex&type=%s" % (viewid, mediatype)) elif (mediatype == "homevideos" or mediatype == "photos"): # Custom query path = ("plugin://plugin.video.plexkodiconnect/?id=%s&mode=browseplex&type=%s&folderid=%s" % (viewid, mediatype, nodetype)) elif nodetype == "nextepisodes": # Custom query path = "plugin://plugin.video.plexkodiconnect/?id=%s&mode=nextup&limit=%s" % (tagname, limit) # elif kodiversion == 14 and nodetype == "recentepisodes": elif nodetype == "recentepisodes": # Custom query path = ("plugin://plugin.video.plexkodiconnect/?id=%s&mode=recentepisodes&type=%s&tagname=%s&limit=%s" % (viewid, mediatype, tagname, limit)) elif self.kodiversion == 14 and nodetype == "inprogressepisodes": # Custom query path = "plugin://plugin.video.plexkodiconnect/?id=%s&mode=inprogressepisodes&limit=%s" % (tagname, limit) elif nodetype == 'ondeck': # PLEX custom query if mediatype == "tvshows": path = ("plugin://plugin.video.plexkodiconnect/?id=%s&mode=ondeck&type=%s&tagname=%s&limit=%s" % (viewid, mediatype, tagname, limit)) elif mediatype =="movies": # Reset nodetype; we got the label nodetype = 'inprogress' else: path = "library://video/Plex-%s/%s_%s.xml" % (dirname, viewid, nodetype) if mediatype == "photos": windowpath = "ActivateWindow(Pictures,%s,return)" % path else: if self.kodiversion >= 17: # Krypton windowpath = "ActivateWindow(Videos,%s,return)" % path else: windowpath = "ActivateWindow(Video,%s,return)" % path if nodetype == "all": if viewtype == "mixed": templabel = "%s-%s" % (tagname, mediatype) else: templabel = label embynode = "Plex.nodes.%s" % indexnumber window('%s.title' % embynode, value=templabel) window('%s.path' % embynode, value=windowpath) window('%s.content' % embynode, value=path) window('%s.type' % embynode, value=mediatype) else: embynode = "Plex.nodes.%s.%s" % (indexnumber, nodetype) window('%s.title' % embynode, value=label) window('%s.path' % embynode, value=windowpath) window('%s.content' % embynode, value=path) if mediatype == "photos": # For photos, we do not create a node in videos but we do want the window props # to be created. # To do: add our photos nodes to kodi picture sources somehow continue if xbmcvfs.exists(tryEncode(nodeXML)): # Don't recreate xml if already exists continue # Create the root if (nodetype in ("nextepisodes", "ondeck", 'recentepisodes') or mediatype == "homevideos"): # Folder type with plugin path root = self.commonRoot(order=sortorder[node], label=label, tagname=tagname, roottype=2) etree.SubElement(root, 'path').text = path etree.SubElement(root, 'content').text = "episodes" else: root = self.commonRoot(order=sortorder[node], label=label, tagname=tagname) if nodetype in ('recentepisodes', 'inprogressepisodes'): etree.SubElement(root, 'content').text = "episodes" else: etree.SubElement(root, 'content').text = mediatype # Elements per nodetype if nodetype == "all": etree.SubElement(root, 'order', {'direction': "ascending"}).text = "sorttitle" elif nodetype == "recent": etree.SubElement(root, 'order', {'direction': "descending"}).text = "dateadded" etree.SubElement(root, 'limit').text = limit if settings('MovieShowWatched') == 'false': rule = etree.SubElement(root, 'rule', {'field': "playcount", 'operator': "is"}) etree.SubElement(rule, 'value').text = "0" elif nodetype == "inprogress": etree.SubElement(root, 'rule', {'field': "inprogress", 'operator': "true"}) etree.SubElement(root, 'limit').text = limit etree.SubElement( root, 'order', {'direction': 'descending'} ).text = 'lastplayed' elif nodetype == "genres": etree.SubElement(root, 'order', {'direction': "ascending"}).text = "sorttitle" etree.SubElement(root, 'group').text = "genres" elif nodetype == "unwatched": etree.SubElement(root, 'order', {'direction': "ascending"}).text = "sorttitle" rule = etree.SubElement(root, "rule", {'field': "playcount", 'operator': "is"}) etree.SubElement(rule, 'value').text = "0" elif nodetype == "sets": etree.SubElement(root, 'order', {'direction': "ascending"}).text = "sorttitle" etree.SubElement(root, 'group').text = "tags" elif nodetype == "random": etree.SubElement(root, 'order', {'direction': "ascending"}).text = "random" etree.SubElement(root, 'limit').text = limit elif nodetype == "recommended": etree.SubElement(root, 'order', {'direction': "descending"}).text = "rating" etree.SubElement(root, 'limit').text = limit rule = etree.SubElement(root, 'rule', {'field': "playcount", 'operator': "is"}) etree.SubElement(rule, 'value').text = "0" rule2 = etree.SubElement(root, 'rule', attrib={'field': "rating", 'operator': "greaterthan"}) etree.SubElement(rule2, 'value').text = "7" elif nodetype == "recentepisodes": # Kodi Isengard, Jarvis etree.SubElement(root, 'order', {'direction': "descending"}).text = "dateadded" etree.SubElement(root, 'limit').text = limit rule = etree.SubElement(root, 'rule', {'field': "playcount", 'operator': "is"}) etree.SubElement(rule, 'value').text = "0" elif nodetype == "inprogressepisodes": # Kodi Isengard, Jarvis etree.SubElement(root, 'limit').text = limit rule = etree.SubElement(root, 'rule', attrib={'field': "inprogress", 'operator':"true"}) try: indent(root) except: pass etree.ElementTree(root).write(nodeXML)
def notify(self, text): xbmc.executebuiltin("XBMC.Notification(%s,%s,5000,%s)" % (utils.plugin, utils.tryEncode(text), self.icon))
addon = utils.addon # if version changed clear cache if not addon.getSetting('version') or addon.getSetting('version') != addon.getAddonInfo('version'): storage.clearCache() login.clearCookie() addon.setSetting('version', addon.getAddonInfo('version')) # check for initing cookies, db and library only on main menu storage.checkDbInit() login.checkCredentials() login.checkCookie() login.initDevice() # check if library needs to be loaded if addon.getSetting('fetched_all_songs') == '0': xbmc.executebuiltin("XBMC.Notification(%s,%s,5000,%s)" % (utils.plugin, utils.tryEncode(addon.getLocalizedString(30105)) ,addon.getAddonInfo('icon'))) utils.log('Loading library') navigation.api.loadLibrary() if addon.getSetting('auto_export')=='true' and addon.getSetting('export_path'): import GoogleMusicActions GoogleMusicActions.GoogleMusicActions().exportLibrary(addon.getSetting('export_path')) navigation.listMenu() else: utils.log(" ARGV Nothing done.. verify params " + repr(params))
def cacheTexture(self, url): # Cache a single image url to the texture cache if url and self.enableTextureCache: self.queue.put(double_urlencode(tryEncode(url)))
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 FullTextureCacheSync(self): # This method will sync all Kodi artwork to textures13.db # and cache them locally. This takes diskspace! import xbmcaddon string = xbmcaddon.Addon().getLocalizedString if not xbmcgui.Dialog().yesno( "Image Texture Cache", string(39250)): return self.logMsg("Doing Image Cache Sync", 1) dialog = xbmcgui.DialogProgress() dialog.create("Emby for Kodi", "Image Cache Sync") # ask to rest all existing or not if xbmcgui.Dialog().yesno( "Image Texture Cache", string(39251), ""): self.logMsg("Resetting all cache data first", 1) # Remove all existing textures first path = utils.tryDecode(xbmc.translatePath("special://thumbnails/")) if utils.IfExists(path): allDirs, allFiles = xbmcvfs.listdir(path) for dir in allDirs: allDirs, allFiles = xbmcvfs.listdir(path+dir) for file in allFiles: if os.path.supports_unicode_filenames: xbmcvfs.delete(os.path.join( path + utils.tryDecode(dir), utils.tryDecode(file))) else: xbmcvfs.delete(os.path.join( utils.tryEncode(path) + dir, file)) # remove all existing data from texture DB textureconnection = utils.kodiSQL('texture') texturecursor = textureconnection.cursor() texturecursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"') rows = texturecursor.fetchall() for row in rows: tableName = row[0] if(tableName != "version"): texturecursor.execute("DELETE FROM " + tableName) textureconnection.commit() texturecursor.close() # Cache all entries in video DB connection = utils.kodiSQL('video') cursor = connection.cursor() cursor.execute("SELECT url FROM art WHERE media_type != 'actor'") # dont include actors result = cursor.fetchall() total = len(result) count = 1 percentage = 0 self.logMsg("Image cache sync about to process " + str(total) + " images", 1) for url in result: if dialog.iscanceled(): break percentage = int((float(count) / float(total))*100) textMessage = str(count) + " of " + str(total) + " (" + str(len(self.imageCacheThreads)) + ")" dialog.update(percentage, "Updating Image Cache: " + textMessage) self.CacheTexture(url[0]) count += 1 cursor.close() # Cache all entries in music DB connection = utils.kodiSQL('music') cursor = connection.cursor() cursor.execute("SELECT url FROM art") result = cursor.fetchall() total = len(result) count = 1 percentage = 0 self.logMsg("Image cache sync about to process " + str(total) + " images", 1) for url in result: if dialog.iscanceled(): break percentage = int((float(count) / float(total))*100) textMessage = str(count) + " of " + str(total) dialog.update(percentage, "Updating Image Cache: " + textMessage) self.CacheTexture(url[0]) count += 1 cursor.close() dialog.update(100, "Waiting for all threads to exit: " + str(len(self.imageCacheThreads))) self.logMsg("Waiting for all threads to exit", 1) while len(self.imageCacheThreads) > 0: for thread in self.imageCacheThreads: if thread.isFinished: self.imageCacheThreads.remove(thread) dialog.update(100, "Waiting for all threads to exit: " + str(len(self.imageCacheThreads))) self.logMsg("Waiting for all threads to exit: " + str(len(self.imageCacheThreads)), 1) xbmc.sleep(500) dialog.close()
def single_urlencode(self, text): text = urllib.urlencode({'blahblahblah': utils.tryEncode(text)}) #urlencode needs a utf- string text = text[13:] return utils.tryDecode(text) #return the result again as unicode
def audioSubsPref(self, listitem, url, part=None): dialog = xbmcgui.Dialog() # For transcoding only # Present the list of audio to select from audioStreamsList = [] audioStreams = [] # audioStreamsChannelsList = [] subtitleStreamsList = [] subtitleStreams = ['1 No subtitles'] downloadableStreams = [] # selectAudioIndex = "" selectSubsIndex = "" playurlprefs = {} # Set part where we're at self.API.setPartNumber(part) if part is None: part = 0 try: mediastreams = self.item[0][part] except (TypeError, KeyError, IndexError): return url audioNum = 0 # Remember 'no subtitles' subNum = 1 defaultSub = None for stream in mediastreams: # Since Plex returns all possible tracks together, have to sort # them. index = stream.attrib.get('id') type = stream.attrib.get('streamType') # Audio if type == "2": codec = stream.attrib.get('codec') channelLayout = stream.attrib.get('audioChannelLayout', "") try: track = "%s %s - %s %s" % (audioNum+1, stream.attrib['language'], codec, channelLayout) except: track = "%s 'unknown' - %s %s" % (audioNum+1, codec, channelLayout) audioStreamsList.append(index) audioStreams.append(tryEncode(track)) audioNum += 1 # Subtitles elif type == "3": try: track = "%s %s" % (subNum+1, stream.attrib['language']) except: track = "%s 'unknown' (%s)" % (subNum+1, stream.attrib.get('codec')) default = stream.attrib.get('default') forced = stream.attrib.get('forced') downloadable = stream.attrib.get('key') if default: track = "%s - Default" % track if forced: track = "%s - Forced" % track if downloadable: downloadableStreams.append(index) else: track = "%s (burn-in)" % track if stream.attrib.get('selected') == '1' and downloadable: # Only show subs without asking user if they can be # turned off defaultSub = index subtitleStreamsList.append(index) subtitleStreams.append(tryEncode(track)) subNum += 1 if audioNum > 1: resp = dialog.select(lang(33013), audioStreams) if resp > -1: # User selected audio playurlprefs['audioStreamID'] = audioStreamsList[resp] else: # User backed out of selection - let PMS decide pass else: # There's only one audiotrack. playurlprefs['audioStreamID'] = audioStreamsList[0] # Add audio boost playurlprefs['audioBoost'] = settings('audioBoost') selectSubsIndex = None if subNum > 1: if (settings('pickPlexSubtitles') == 'true' and defaultSub is not None): log.info('Using default Plex subtitle: %s' % defaultSub) selectSubsIndex = defaultSub else: resp = dialog.select(lang(33014), subtitleStreams) if resp > 0: selectSubsIndex = subtitleStreamsList[resp-1] else: # User selected no subtitles or backed out of dialog playurlprefs["skipSubtitles"] = 1 if selectSubsIndex is not None: # Load subtitles in the listitem if downloadable if selectSubsIndex in downloadableStreams: sub_url = self.API.addPlexHeadersToUrl( "%s/library/streams/%s" % (self.server, selectSubsIndex)) log.info("Downloadable sub: %s: %s" % (selectSubsIndex, sub_url)) listitem.setSubtitles([tryEncode(sub_url)]) # Don't additionally burn in subtitles playurlprefs["skipSubtitles"] = 1 else: log.info('Need to burn in subtitle %s' % selectSubsIndex) playurlprefs["subtitleStreamID"] = selectSubsIndex playurlprefs["subtitleSize"] = settings('subtitleSize') url += '&' + urlencode(playurlprefs) # Get number of channels for selected audio track # audioChannels = audioStreamsChannelsList.get(selectAudioIndex, 0) # if audioChannels > 2: # playurlprefs += "&AudioBitrate=384000" # else: # playurlprefs += "&AudioBitrate=192000" return url
def play(self, itemid, dbid=None): window = utils.window settings = utils.settings item = self.item # Hack to get only existing entry in PMS response for THIS instance of # playbackutils :-) self.API = PlexAPI.API(item[0]) API = self.API listitem = xbmcgui.ListItem() playutils = putils.PlayUtils(item[0]) self.logMsg("Play called.", 1) playurl = playutils.getPlayUrl() if not playurl: return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem) if dbid in (None, '999999999', 'plexnode'): # Item is not in Kodi database, is a trailer or plex redirect # e.g. plex.tv watch later API.CreateListItemFromPlexItem(listitem) self.setArtwork(listitem) if dbid == 'plexnode': # Need to get yet another xml to get final url window('emby_%s.playmethod' % playurl, clear=True) xml = downloadutils.DownloadUtils().downloadUrl( '{server}%s' % item[0][0][0].attrib.get('key')) if xml in (None, 401): self.logMsg('Could not download %s' % item[0][0][0].attrib.get('key'), -1) return xbmcplugin.setResolvedUrl( int(sys.argv[1]), False, listitem) playurl = utils.tryEncode(xml[0].attrib.get('key')) window('emby_%s.playmethod' % playurl, value='DirectStream') playmethod = window('emby_%s.playmethod' % playurl) if playmethod == "Transcode": window('emby_%s.playmethod' % playurl, clear=True) playurl = utils.tryEncode(playutils.audioSubsPref( listitem, utils.tryDecode(playurl))) window('emby_%s.playmethod' % playurl, "Transcode") listitem.setPath(playurl) self.setProperties(playurl, listitem) return xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem) ############### ORGANIZE CURRENT PLAYLIST ################ homeScreen = xbmc.getCondVisibility('Window.IsActive(home)') playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) startPos = max(playlist.getposition(), 0) # Can return -1 sizePlaylist = playlist.size() self.currentPosition = startPos propertiesPlayback = window('plex_playbackProps') == "true" introsPlaylist = False dummyPlaylist = False self.logMsg("Playlist start position: %s" % startPos, 2) self.logMsg("Playlist plugin position: %s" % self.currentPosition, 2) self.logMsg("Playlist size: %s" % sizePlaylist, 2) ############### RESUME POINT ################ seektime, runtime = API.getRuntime() # We need to ensure we add the intro and additional parts only once. # Otherwise we get a loop. if not propertiesPlayback: window('plex_playbackProps', value="true") self.logMsg("Setting up properties in playlist.", 1) if (not homeScreen and not seektime and window('plex_customplaylist') != "true"): self.logMsg("Adding dummy file to playlist.", 2) dummyPlaylist = True playlist.add(playurl, listitem, index=startPos) # Remove the original item from playlist self.pl.removefromPlaylist(startPos+1) # Readd the original item to playlist - via jsonrpc so we have full metadata self.pl.insertintoPlaylist( self.currentPosition+1, dbid, PF.GetKodiTypeFromPlex(API.getType())) self.currentPosition += 1 ############### -- CHECK FOR INTROS ################ if (settings('enableCinema') == "true" and not seektime): # if we have any play them when the movie/show is not being resumed xml = PF.GetPlexPlaylist( itemid, item.attrib.get('librarySectionUUID'), mediatype=API.getType()) introsPlaylist = self.AddTrailers(xml) ############### -- ADD MAIN ITEM ONLY FOR HOMESCREEN ############### if homeScreen and not seektime and not sizePlaylist: # Extend our current playlist with the actual item to play # only if there's no playlist first self.logMsg("Adding main item to playlist.", 1) self.pl.addtoPlaylist( dbid, PF.GetKodiTypeFromPlex(API.getType())) # Ensure that additional parts are played after the main item self.currentPosition += 1 ############### -- CHECK FOR ADDITIONAL PARTS ################ if len(item[0][0]) > 1: # Only add to the playlist after intros have played for counter, part in enumerate(item[0][0]): # Never add first part if counter == 0: continue # Set listitem and properties for each additional parts API.setPartNumber(counter) additionalListItem = xbmcgui.ListItem() additionalPlayurl = playutils.getPlayUrl( partNumber=counter) self.logMsg("Adding additional part: %s" % counter, 1) self.setProperties(additionalPlayurl, additionalListItem) self.setArtwork(additionalListItem) # NEW to Plex API.CreateListItemFromPlexItem(additionalListItem) playlist.add(additionalPlayurl, additionalListItem, index=self.currentPosition) self.pl.verifyPlaylist() self.currentPosition += 1 API.setPartNumber = 0 if dummyPlaylist: # Added a dummy file to the playlist, # because the first item is going to fail automatically. self.logMsg("Processed as a playlist. First item is skipped.", 1) return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem) # We just skipped adding properties. Reset flag for next time. elif propertiesPlayback: self.logMsg("Resetting properties playback flag.", 2) window('plex_playbackProps', clear=True) #self.pl.verifyPlaylist() ########## SETUP MAIN ITEM ########## # For transcoding only, ask for audio/subs pref if window('emby_%s.playmethod' % playurl) == "Transcode": window('emby_%s.playmethod' % playurl, clear=True) playurl = utils.tryEncode(playutils.audioSubsPref( listitem, utils.tryDecode(playurl))) window('emby_%s.playmethod' % playurl, value="Transcode") listitem.setPath(playurl) self.setProperties(playurl, listitem) ############### PLAYBACK ################ if homeScreen and seektime and window('plex_customplaylist') != "true": self.logMsg("Play as a widget item.", 1) API.CreateListItemFromPlexItem(listitem) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem) elif ((introsPlaylist and window('plex_customplaylist') == "true") or (homeScreen and not sizePlaylist)): # Playlist was created just now, play it. self.logMsg("Play playlist.", 1) xbmc.Player().play(playlist, startpos=startPos) else: self.logMsg("Play as a regular item.", 1) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
def PlayBackStart(self, data): """ Called whenever a playback is started """ log = self.logMsg window = utils.window # Get currently playing file - can take a while. Will be utf-8! try: currentFile = self.xbmcplayer.getPlayingFile() except: currentFile = None count = 0 while currentFile is None: xbmc.sleep(100) try: currentFile = self.xbmcplayer.getPlayingFile() except: pass if count == 50: log("No current File - Cancelling OnPlayBackStart...", -1) return else: count += 1 # Just to be on the safe side currentFile = utils.tryDecode(currentFile) log("Currently playing file is: %s" % currentFile, 1) # Get the type of media we're playing try: typus = data['item']['type'] except (TypeError, KeyError): log("Item is invalid for PMS playstate update.", 0) return log("Playing itemtype is (or appears to be): %s" % typus, 1) # Try to get a Kodi ID # If PKC was used - native paths, not direct paths plexid = utils.window('emby_%s.itemid' % utils.tryEncode(currentFile)) # Get rid of the '' if the window property was not set plexid = None if not plexid else plexid kodiid = None if plexid is None: log('Did not get Plex id from window properties', 1) try: kodiid = data['item']['id'] except (TypeError, KeyError): log('Did not get a Kodi id from Kodi, darn', 1) # For direct paths, if we're not streaming something # When using Widgets, Kodi doesn't tell us shit so we need this hack if (kodiid is None and plexid is None and typus != 'song' and not currentFile.startswith('http')): try: filename = currentFile.rsplit('/', 1)[1] path = currentFile.rsplit('/', 1)[0] + '/' except IndexError: filename = currentFile.rsplit('\\', 1)[1] path = currentFile.rsplit('\\', 1)[0] + '\\' log('Trying to figure out playing item from filename: %s and ' 'path: %s' % (filename, path), 1) with kodidb.GetKodiDB('video') as kodi_db: try: kodiid, typus = kodi_db.getIdFromFilename(filename, path) except TypeError: log('Aborting playback report', 1) return if plexid is None: # Get Plex' item id with embydb.GetEmbyDB() as emby_db: emby_dbitem = emby_db.getItem_byKodiId(kodiid, typus) try: plexid = emby_dbitem[0] except TypeError: log("No Plex id returned for kodiid %s" % kodiid, 1) log('Aborting playback report', 1) return log("Found Plex id %s for Kodi id %s for type %s" % (plexid, kodiid, typus), 1) # Set some stuff if Kodi initiated playback if ((utils.settings('useDirectPaths') == "1" and not typus == "song") or (typus == "song" and utils.settings('enableMusic') == "true")): if self.StartDirectPath(plexid, typus, utils.tryEncode(currentFile)) is False: log('Could not initiate monitoring; aborting', -1) return # Save currentFile for cleanup later and to be able to access refs window('plex_lastPlayedFiled', value=currentFile) window('Plex_currently_playing_itemid', value=plexid) window("emby_%s.itemid" % utils.tryEncode(currentFile), value=plexid) log('Finish playback startup', 1)
def PlayBackStart(self, data): """ Called whenever a playback is started """ # Get currently playing file - can take a while. Will be utf-8! try: currentFile = self.xbmcplayer.getPlayingFile() except: currentFile = None count = 0 while currentFile is None: sleep(100) try: currentFile = self.xbmcplayer.getPlayingFile() except: pass if count == 50: log.info("No current File, cancel OnPlayBackStart...") return else: count += 1 # Just to be on the safe side currentFile = tryDecode(currentFile) log.debug("Currently playing file is: %s" % currentFile) # Get the type of media we're playing try: typus = data['item']['type'] except (TypeError, KeyError): log.info("Item is invalid for PMS playstate update.") return log.debug("Playing itemtype is (or appears to be): %s" % typus) # Try to get a Kodi ID # If PKC was used - native paths, not direct paths plexid = window('plex_%s.itemid' % tryEncode(currentFile)) # Get rid of the '' if the window property was not set plexid = None if not plexid else plexid kodiid = None if plexid is None: log.debug('Did not get Plex id from window properties') try: kodiid = data['item']['id'] except (TypeError, KeyError): log.debug('Did not get a Kodi id from Kodi, darn') # For direct paths, if we're not streaming something # When using Widgets, Kodi doesn't tell us shit so we need this hack if (kodiid is None and plexid is None and typus != 'song' and not currentFile.startswith('http')): (kodiid, typus) = get_kodiid_from_filename(currentFile) if kodiid is None: return if plexid is None: # Get Plex' item id with plexdb.Get_Plex_DB() as plexcursor: plex_dbitem = plexcursor.getItem_byKodiId(kodiid, typus) try: plexid = plex_dbitem[0] except TypeError: log.info("No Plex id returned for kodiid %s. Aborting playback" " report" % kodiid) return log.debug("Found Plex id %s for Kodi id %s for type %s" % (plexid, kodiid, typus)) # Set some stuff if Kodi initiated playback if ((settings('useDirectPaths') == "1" and not typus == "song") or (typus == "song" and settings('enableMusic') == "true")): if self.StartDirectPath(plexid, typus, tryEncode(currentFile)) is False: log.error('Could not initiate monitoring; aborting') return # Save currentFile for cleanup later and to be able to access refs window('plex_lastPlayedFiled', value=currentFile) window('plex_currently_playing_itemid', value=plexid) window("plex_%s.itemid" % tryEncode(currentFile), value=plexid) log.info('Finish playback startup')
def play(self, plex_id, kodi_id=None, plex_lib_UUID=None): """ plex_lib_UUID: xml attribute 'librarySectionUUID', needed for posting to the PMS """ log.info("Playbackutils called") item = self.xml[0] api = API(item) playqueue = self.playqueue xml = None result = Playback_Successful() listitem = ListItem() playutils = putils.PlayUtils(item) playurl = playutils.getPlayUrl() if not playurl: log.error('No playurl found, aborting') return if kodi_id in (None, 'plextrailer', 'plexnode'): # Item is not in Kodi database, is a trailer/clip or plex redirect # e.g. plex.tv watch later api.CreateListItemFromPlexItem(listitem) api.set_listitem_artwork(listitem) if kodi_id == 'plexnode': # Need to get yet another xml to get final url window('plex_%s.playmethod' % playurl, clear=True) xml = downloadutils.DownloadUtils().downloadUrl( '{server}%s' % item[0][0].attrib.get('key')) try: xml[0].attrib except (TypeError, AttributeError): log.error('Could not download %s' % item[0][0].attrib.get('key')) return playurl = tryEncode(xml[0].attrib.get('key')) window('plex_%s.playmethod' % playurl, value='DirectStream') playmethod = window('plex_%s.playmethod' % playurl) if playmethod == "Transcode": window('plex_%s.playmethod' % playurl, clear=True) playurl = tryEncode(playutils.audioSubsPref( listitem, tryDecode(playurl))) window('plex_%s.playmethod' % playurl, "Transcode") listitem.setPath(playurl) api.set_playback_win_props(playurl, listitem) result.listitem = listitem return result kodi_type = v.KODITYPE_FROM_PLEXTYPE[api.getType()] kodi_id = int(kodi_id) # ORGANIZE CURRENT PLAYLIST ################ contextmenu_play = window('plex_contextplay') == 'true' window('plex_contextplay', clear=True) homeScreen = getCondVisibility('Window.IsActive(home)') sizePlaylist = len(playqueue.items) if contextmenu_play: # Need to start with the items we're inserting here startPos = sizePlaylist else: # Can return -1 startPos = max(playqueue.kodi_pl.getposition(), 0) self.currentPosition = startPos propertiesPlayback = window('plex_playbackProps') == "true" introsPlaylist = False dummyPlaylist = False log.info("Playing from contextmenu: %s" % contextmenu_play) log.info("Playlist start position: %s" % startPos) log.info("Playlist plugin position: %s" % self.currentPosition) log.info("Playlist size: %s" % sizePlaylist) # RESUME POINT ################ seektime, runtime = api.getRuntime() if window('plex_customplaylist.seektime'): # Already got seektime, e.g. from playqueue & Plex companion seektime = int(window('plex_customplaylist.seektime')) # We need to ensure we add the intro and additional parts only once. # Otherwise we get a loop. if not propertiesPlayback: window('plex_playbackProps', value="true") log.info("Setting up properties in playlist.") # Where will the player need to start? # Do we need to get trailers? trailers = False if (api.getType() == v.PLEX_TYPE_MOVIE and not seektime and sizePlaylist < 2 and settings('enableCinema') == "true"): if settings('askCinema') == "true": trailers = xbmcgui.Dialog().yesno( lang(29999), "Play trailers?") else: trailers = True # Post to the PMS. REUSE THE PLAYQUEUE! xml = init_plex_playqueue(plex_id, plex_lib_UUID, mediatype=api.getType(), trailers=trailers) try: get_playlist_details_from_xml(playqueue, xml=xml) except KeyError: return if (not homeScreen and not seektime and sizePlaylist < 2 and window('plex_customplaylist') != "true" and not contextmenu_play): # Need to add a dummy file because the first item will fail log.debug("Adding dummy file to playlist.") dummyPlaylist = True add_listitem_to_Kodi_playlist( playqueue, startPos, xbmcgui.ListItem(), playurl, xml[0]) # Remove the original item from playlist remove_from_Kodi_playlist( playqueue, startPos+1) # Readd the original item to playlist - via jsonrpc so we have # full metadata add_item_to_kodi_playlist( playqueue, self.currentPosition+1, kodi_id=kodi_id, kodi_type=kodi_type, file=playurl) self.currentPosition += 1 # -- ADD TRAILERS ################ if trailers: for i, item in enumerate(xml): if i == len(xml) - 1: # Don't add the main movie itself break self.add_trailer(item) introsPlaylist = True # -- ADD MAIN ITEM ONLY FOR HOMESCREEN ############## if homeScreen and not seektime and not sizePlaylist: # Extend our current playlist with the actual item to play # only if there's no playlist first log.info("Adding main item to playlist.") add_item_to_kodi_playlist( playqueue, self.currentPosition, kodi_id, kodi_type) elif contextmenu_play: if window('useDirectPaths') == 'true': # Cannot add via JSON with full metadata because then we # Would be using the direct path log.debug("Adding contextmenu item for direct paths") if window('plex_%s.playmethod' % playurl) == "Transcode": window('plex_%s.playmethod' % playurl, clear=True) playurl = tryEncode(playutils.audioSubsPref( listitem, tryDecode(playurl))) window('plex_%s.playmethod' % playurl, value="Transcode") api.CreateListItemFromPlexItem(listitem) api.set_playback_win_props(playurl, listitem) api.set_listitem_artwork(listitem) add_listitem_to_Kodi_playlist( playqueue, self.currentPosition+1, convert_PKC_to_listitem(listitem), playurl, kodi_item={'id': kodi_id, 'type': kodi_type}) else: # Full metadata$ add_item_to_kodi_playlist( playqueue, self.currentPosition+1, kodi_id, kodi_type) self.currentPosition += 1 if seektime: window('plex_customplaylist.seektime', value=str(seektime)) # Ensure that additional parts are played after the main item self.currentPosition += 1 # -- CHECK FOR ADDITIONAL PARTS ################ if len(item[0]) > 1: self.add_part(item, api, kodi_id, kodi_type) if dummyPlaylist: # Added a dummy file to the playlist, # because the first item is going to fail automatically. log.info("Processed as a playlist. First item is skipped.") # Delete the item that's gonna fail! del playqueue.items[startPos] # Don't attach listitem return result # We just skipped adding properties. Reset flag for next time. elif propertiesPlayback: log.debug("Resetting properties playback flag.") window('plex_playbackProps', clear=True) # SETUP MAIN ITEM ########## # For transcoding only, ask for audio/subs pref if (window('plex_%s.playmethod' % playurl) == "Transcode" and not contextmenu_play): window('plex_%s.playmethod' % playurl, clear=True) playurl = tryEncode(playutils.audioSubsPref( listitem, tryDecode(playurl))) window('plex_%s.playmethod' % playurl, value="Transcode") listitem.setPath(playurl) api.set_playback_win_props(playurl, listitem) api.set_listitem_artwork(listitem) # PLAYBACK ################ if (homeScreen and seektime and window('plex_customplaylist') != "true" and not contextmenu_play): log.info("Play as a widget item") api.CreateListItemFromPlexItem(listitem) result.listitem = listitem return result elif ((introsPlaylist and window('plex_customplaylist') == "true") or (homeScreen and not sizePlaylist) or contextmenu_play): # Playlist was created just now, play it. # Contextmenu plays always need this log.info("Play playlist from starting position %s" % startPos) # Need a separate thread because Player won't return in time thread = Thread(target=Player().play, args=(playqueue.kodi_pl, None, False, startPos)) thread.setDaemon(True) thread.start() # Don't attach listitem return result else: log.info("Play as a regular item") result.listitem = listitem return result
def _sanitizePath(self, name): name = "".join(i for i in name if i not in "\/:*?<>|,;$%\"\'.`") if len(name) > 50: name = name[:50] return utils.tryEncode(name).strip()