def process_play(self, plex_id, kodi_id=None): """ Processes Kodi playback init for ONE item """ log.info("Process_play called with plex_id %s, kodi_id %s" % (plex_id, kodi_id)) if window('plex_authenticated') != "true": log.error('Not yet authenticated for PMS, abort starting playback') # Todo: Warn user with dialog return xml = GetPlexMetadata(plex_id) try: xml[0].attrib except (IndexError, TypeError, AttributeError): log.error('Could not get a PMS xml for plex id %s' % plex_id) return api = API(xml[0]) if api.getType() == v.PLEX_TYPE_PHOTO: # Photo result = Playback_Successful() listitem = PKC_ListItem() listitem = api.CreateListItemFromPlexItem(listitem) result.listitem = listitem else: # Video and Music playqueue = self.playqueue.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()]) with lock: result = PlaybackUtils(xml, playqueue).play( plex_id, kodi_id, xml.attrib.get('librarySectionUUID')) log.info('Done process_play, playqueues: %s' % self.playqueue.playqueues) return result
def __build_item(xml_element): api = API(xml_element) listitem = api.CreateListItemFromPlexItem() resume = api.getResume() if resume: listitem.setProperty('resumetime', str(resume)) if (api.getKey().startswith('/system/services') or api.getKey().startswith('http')): params = { 'mode': 'plex_node', 'key': xml_element.attrib.get('key'), 'offset': xml_element.attrib.get('viewOffset', '0'), } url = "plugin://%s?%s" % (v.ADDON_ID, urlencode(params)) elif api.getType() == v.PLEX_TYPE_PHOTO: url = api.get_picture_path() else: params = { 'mode': 'play', 'plex_id': api.getRatingKey(), 'plex_type': api.getType(), } url = "plugin://%s?%s" % (v.ADDON_ID, urlencode(params)) xbmcplugin.addDirectoryItem(handle=HANDLE, url=url, listitem=listitem)
def play_all(self): """ Play all items contained in the xml passed in. Called by Plex Companion """ log.info("Playbackutils play_all called") window('plex_playbackProps', value="true") self.currentPosition = 0 for item in self.xml: api = API(item) successful = True if api.getType() == v.PLEX_TYPE_CLIP: self.add_trailer(item) else: with Get_Plex_DB() as plex_db: db_item = plex_db.getItem_byId(api.getRatingKey()) if db_item is not None: successful = add_item_to_kodi_playlist( self.playqueue, self.currentPosition, kodi_id=db_item[0], kodi_type=db_item[4]) if successful is True: self.currentPosition += 1 if len(item[0]) > 1: self.add_part(item, api, db_item[0], db_item[4]) else: # Item not in Kodi DB self.add_trailer(item) if successful is True: self.playqueue.items[self.currentPosition - 1].ID = item.get( '%sItemID' % self.playqueue.kind)
def playlist_item_from_xml(playlist, xml_video_element, kodi_id=None, kodi_type=None): """ Returns a playlist element for the playqueue using the Plex xml xml_video_element: etree xml piece 1 level underneath <MediaContainer> """ item = Playlist_Item() api = API(xml_video_element) item.plex_id = api.getRatingKey() item.plex_type = api.getType() try: item.id = xml_video_element.attrib['%sItemID' % playlist.kind] except KeyError: pass item.guid = xml_video_element.attrib.get('guid') if item.guid is not None: item.guid = escape_html(item.guid) if kodi_id is not None: item.kodi_id = kodi_id item.kodi_type = kodi_type elif item.plex_id is not None: with plexdb.Get_Plex_DB() as plex_db: db_element = plex_db.getItem_byId(item.plex_id) try: item.kodi_id, item.kodi_type = int(db_element[0]), db_element[4] except TypeError: pass item.xml = xml_video_element LOG.debug('Created new playlist item from xml: %s', item) return item
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 process_plex_node(self, url, viewOffset, directplay=False, node=True): """ Called for Plex directories or redirect for playback (e.g. trailers, clips, watchlater) """ log.info('process_plex_node called with url: %s, viewOffset: %s' % (url, viewOffset)) # Plex redirect, e.g. watch later. Need to get actual URLs if url.startswith('http') or url.startswith('{server}'): xml = DownloadUtils().downloadUrl(url) else: xml = DownloadUtils().downloadUrl('{server}%s' % url) try: xml[0].attrib except: log.error('Could not download PMS metadata') return if viewOffset != '0': try: viewOffset = int(v.PLEX_TO_KODI_TIMEFACTOR * float(viewOffset)) except: pass else: window('plex_customplaylist.seektime', value=str(viewOffset)) log.info('Set resume point to %s' % str(viewOffset)) api = API(xml[0]) typus = v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()] if node is True: plex_id = None kodi_id = 'plexnode' else: plex_id = api.getRatingKey() kodi_id = None with plexdb.Get_Plex_DB() as plex_db: plexdb_item = plex_db.getItem_byId(plex_id) try: kodi_id = plexdb_item[0] except TypeError: log.info('Couldnt find item %s in Kodi db' % api.getRatingKey()) playqueue = self.playqueue.get_playqueue_from_type(typus) with lock: result = PlaybackUtils(xml, playqueue).play( plex_id, kodi_id=kodi_id, plex_lib_UUID=xml.attrib.get('librarySectionUUID')) if directplay: if result.listitem: listitem = convert_PKC_to_listitem(result.listitem) Player().play(listitem.getfilename(), listitem) return Playback_Successful() else: return result
def playlist_item_from_xml(playlist, xml_video_element): """ Returns a playlist element for the playqueue using the Plex xml """ item = Playlist_Item() api = API(xml_video_element) item.plex_id = api.getRatingKey() item.plex_type = api.getType() item.ID = xml_video_element.attrib['%sItemID' % playlist.kind] item.guid = xml_video_element.attrib.get('guid') if item.guid is not None: item.guid = escape_html(item.guid) if item.plex_id: with plexdb.Get_Plex_DB() as plex_db: db_element = plex_db.getItem_byId(item.plex_id) try: item.kodi_id, item.kodi_type = int(db_element[0]), db_element[4] except TypeError: pass log.debug('Created new playlist item from xml: %s' % item) return item
def _process_playlist(self, data): # Get the playqueue ID _, container_key, query = ParseContainerKey(data['containerKey']) try: playqueue = PQ.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[data['type']]) except KeyError: # E.g. Plex web does not supply the media type # Still need to figure out the type (video vs. music vs. pix) xml = GetPlexMetadata(data['key']) try: xml[0].attrib except (AttributeError, IndexError, TypeError): LOG.error('Could not download Plex metadata') return api = API(xml[0]) playqueue = PQ.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()]) PQ.update_playqueue_from_PMS( playqueue, playqueue_id=container_key, repeat=query.get('repeat'), offset=data.get('offset'), transient_token=data.get('token'))
def _process_alexa(self, data): xml = GetPlexMetadata(data['key']) try: xml[0].attrib except (AttributeError, IndexError, TypeError): LOG.error('Could not download Plex metadata for: %s', data) return api = API(xml[0]) if api.getType() == v.PLEX_TYPE_ALBUM: LOG.debug('Plex music album detected') PQ.init_playqueue_from_plex_children( api.getRatingKey(), transient_token=data.get('token')) else: state.PLEX_TRANSIENT_TOKEN = data.get('token') params = { 'mode': 'plex_node', 'key': '{server}%s' % data.get('key'), 'view_offset': data.get('offset'), 'play_directly': 'true', 'node': 'false' } executebuiltin('RunPlugin(plugin://%s?%s)' % (v.ADDON_ID, urlencode(params)))
def __build_item(xml_element): api = API(xml_element) listitem = api.CreateListItemFromPlexItem() if (api.getKey().startswith('/system/services') or api.getKey().startswith('http')): params = { 'mode': 'plex_node', 'key': xml_element.attrib.get('key'), 'view_offset': xml_element.attrib.get('viewOffset', '0'), } url = "plugin://%s?%s" % (v.ADDON_ID, urlencode(params)) elif api.getType() == v.PLEX_TYPE_PHOTO: url = api.get_picture_path() else: params = { 'mode': 'play', 'filename': api.getKey(), 'id': api.getRatingKey(), 'dbid': listitem.getProperty('dbid') } url = "plugin://%s?%s" % (v.ADDON_ID, urlencode(params)) xbmcplugin.addDirectoryItem(handle=HANDLE, url=url, listitem=listitem)
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 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": playutils.audioSubsPref(listitem, tryDecode(playurl)) 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 state.DIRECT_PATHS: # 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": playutils.audioSubsPref(listitem, tryDecode(playurl)) 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), file=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): playutils.audioSubsPref(listitem, tryDecode(playurl)) 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 processTasks(self, task): """ Processes tasks picked up e.g. by Companion listener, e.g. {'action': 'playlist', 'data': {'address': 'xyz.plex.direct', 'commandID': '7', 'containerKey': '/playQueues/6669?own=1&repeat=0&window=200', 'key': '/library/metadata/220493', 'machineIdentifier': 'xyz', 'offset': '0', 'port': '32400', 'protocol': 'https', 'token': 'transient-cd2527d1-0484-48e0-a5f7-f5caa7d591bd', 'type': 'video'}} """ log.debug('Processing: %s' % task) data = task['data'] # Get the token of the user flinging media (might be different one) token = data.get('token') if task['action'] == 'alexa': # e.g. Alexa xml = GetPlexMetadata(data['key']) try: xml[0].attrib except (AttributeError, IndexError, TypeError): log.error('Could not download Plex metadata') return api = API(xml[0]) if api.getType() == v.PLEX_TYPE_ALBUM: log.debug('Plex music album detected') queue = self.mgr.playqueue.init_playqueue_from_plex_children( api.getRatingKey()) queue.plex_transient_token = token else: state.PLEX_TRANSIENT_TOKEN = token params = { 'mode': 'plex_node', 'key': '{server}%s' % data.get('key'), 'view_offset': data.get('offset'), 'play_directly': 'true', 'node': 'false' } executebuiltin('RunPlugin(plugin://%s?%s)' % (v.ADDON_ID, urlencode(params))) elif (task['action'] == 'playlist' and data.get('address') == 'node.plexapp.com'): # E.g. watch later initiated by Companion state.PLEX_TRANSIENT_TOKEN = token params = { 'mode': 'plex_node', 'key': '{server}%s' % data.get('key'), 'view_offset': data.get('offset'), 'play_directly': 'true' } executebuiltin('RunPlugin(plugin://%s?%s)' % (v.ADDON_ID, urlencode(params))) elif task['action'] == 'playlist': # Get the playqueue ID try: typus, ID, query = ParseContainerKey(data['containerKey']) except Exception as e: log.error('Exception while processing: %s' % e) import traceback log.error("Traceback:\n%s" % traceback.format_exc()) return try: playqueue = self.mgr.playqueue.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[data['type']]) except KeyError: # E.g. Plex web does not supply the media type # Still need to figure out the type (video vs. music vs. pix) xml = GetPlexMetadata(data['key']) try: xml[0].attrib except (AttributeError, IndexError, TypeError): log.error('Could not download Plex metadata') return api = API(xml[0]) playqueue = self.mgr.playqueue.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()]) self.mgr.playqueue.update_playqueue_from_PMS( playqueue, ID, repeat=query.get('repeat'), offset=data.get('offset')) playqueue.plex_transient_token = token elif task['action'] == 'refreshPlayQueue': # example data: {'playQueueID': '8475', 'commandID': '11'} xml = get_pms_playqueue(data['playQueueID']) if xml is None: return if len(xml) == 0: log.debug('Empty playqueue received - clearing playqueue') plex_type = get_plextype_from_xml(xml) if plex_type is None: return playqueue = self.mgr.playqueue.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[plex_type]) playqueue.clear() return playqueue = self.mgr.playqueue.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[xml[0].attrib['type']]) self.mgr.playqueue.update_playqueue_from_PMS( playqueue, data['playQueueID'])
def getOnDeck(viewid, mediatype, tagname, limit): """ Retrieves Plex On Deck items, currently only for TV shows Input: viewid: Plex id of the library section, e.g. '1' mediatype: Kodi mediatype, e.g. 'tvshows', 'movies', 'homevideos', 'photos' tagname: Name of the Plex library, e.g. "My Movies" limit: Max. number of items to retrieve, e.g. 50 """ xbmcplugin.setContent(HANDLE, 'episodes') appendShowTitle = settings('OnDeckTvAppendShow') == 'true' appendSxxExx = settings('OnDeckTvAppendSeason') == 'true' directpaths = settings('useDirectPaths') == 'true' if settings('OnDeckTVextended') == 'false': # Chances are that this view is used on Kodi startup # Wait till we've connected to a PMS. At most 30s counter = 0 while window('plex_authenticated') != 'true': counter += 1 if counter >= 300: log.error('Aborting On Deck view, we were not authenticated ' 'for the PMS') return xbmcplugin.endOfDirectory(HANDLE, False) sleep(100) xml = downloadutils.DownloadUtils().downloadUrl( '{server}/library/sections/%s/onDeck' % viewid) if xml in (None, 401): log.error('Could not download PMS xml for view %s' % viewid) return xbmcplugin.endOfDirectory(HANDLE) limitcounter = 0 for item in xml: api = API(item) listitem = api.CreateListItemFromPlexItem( appendShowTitle=appendShowTitle, appendSxxExx=appendSxxExx) if directpaths: url = api.getFilePath() else: params = { 'mode': "play", 'plex_id': api.getRatingKey(), 'plex_type': api.getType() } url = "plugin://plugin.video.plexkodiconnect/tvshows/?%s" \ % urlencode(params) xbmcplugin.addDirectoryItem( handle=HANDLE, url=url, listitem=listitem) limitcounter += 1 if limitcounter == limit: break return xbmcplugin.endOfDirectory( handle=HANDLE, cacheToDisc=settings('enableTextureCache') == 'true') # if the addon is called with nextup parameter, # we return the nextepisodes list of the given tagname # First we get a list of all the TV shows - filtered by tag params = { 'sort': {'order': "descending", 'method': "lastplayed"}, 'filter': { 'and': [ {'operator': "true", 'field': "inprogress", 'value': ""}, {'operator': "is", 'field': "tag", 'value': "%s" % tagname} ]} } items = js.get_tv_shows(params) if not items: # Now items retrieved - empty directory xbmcplugin.endOfDirectory(handle=HANDLE) return params = { 'sort': {'method': "episode"}, 'limits': {"end": 1}, 'properties': [ "title", "playcount", "season", "episode", "showtitle", "plot", "file", "rating", "resume", "tvshowid", "art", "streamdetails", "firstaired", "runtime", "cast", "writer", "dateadded", "lastplayed" ], } if settings('ignoreSpecialsNextEpisodes') == "true": params['filter'] = { 'and': [ {'operator': "lessthan", 'field': "playcount", 'value': "1"}, {'operator': "greaterthan", 'field': "season", 'value': "0"} ] } else: params['filter'] = { 'or': [ {'operator': "lessthan", 'field': "playcount", 'value': "1"}, {'operator': "true", 'field': "inprogress", 'value': ""} ] } # Are there any episodes still in progress/not yet finished watching?!? # Then we should show this episode, NOT the "next up" inprog_params = { 'sort': {'method': "episode"}, 'filter': {'operator': "true", 'field': "inprogress", 'value': ""}, 'properties': params['properties'] } count = 0 for item in items: inprog_params['tvshowid'] = item['tvshowid'] episodes = js.get_episodes(inprog_params) if not episodes: # No, there are no episodes not yet finished. Get "next up" params['tvshowid'] = item['tvshowid'] episodes = js.get_episodes(params) if not episodes: # Also no episodes currently coming up continue for episode in episodes: # There will always be only 1 episode ('limit=1') listitem = createListItem(episode, appendShowTitle=appendShowTitle, appendSxxExx=appendSxxExx) xbmcplugin.addDirectoryItem(handle=HANDLE, url=episode['file'], listitem=listitem, isFolder=False) count += 1 if count >= limit: break xbmcplugin.endOfDirectory(handle=HANDLE)
def process_indirect(key, offset, resolve=True): """ Called e.g. for Plex "Play later" - Plex items where we need to fetch an additional xml for the actual playurl. In the PMS metadata, indirect="1" is set. Will release default.py with setResolvedUrl Set resolve to False if playback should be kicked off directly, not via setResolvedUrl """ LOG.info('process_indirect called with key: %s, offset: %s', key, offset) result = Playback_Successful() if key.startswith('http') or key.startswith('{server}'): xml = DU().downloadUrl(key) elif key.startswith('/system/services'): xml = DU().downloadUrl('http://node.plexapp.com:32400%s' % key) else: xml = DU().downloadUrl('{server}%s' % key) try: xml[0].attrib except (TypeError, IndexError, AttributeError): LOG.error('Could not download PMS metadata') if resolve is True: # Release default.py pickle_me(result) return if offset != '0': offset = int(v.PLEX_TO_KODI_TIMEFACTOR * float(offset)) # Todo: implement offset api = API(xml[0]) listitem = PKC_ListItem() api.CreateListItemFromPlexItem(listitem) playqueue = PQ.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()]) playqueue.clear() item = PL.Playlist_Item() item.xml = xml[0] item.offset = int(offset) item.plex_type = v.PLEX_TYPE_CLIP item.playmethod = 'DirectStream' # Need to get yet another xml to get the final playback url xml = DU().downloadUrl('http://node.plexapp.com:32400%s' % xml[0][0][0].attrib['key']) try: xml[0].attrib except (TypeError, IndexError, AttributeError): LOG.error('Could not download last xml for playurl') if resolve is True: # Release default.py pickle_me(result) return playurl = xml[0].attrib['key'] item.file = playurl listitem.setPath(tryEncode(playurl)) playqueue.items.append(item) if resolve is True: result.listitem = listitem pickle_me(result) else: thread = Thread(target=Player().play, args={ 'item': tryEncode(playurl), 'listitem': listitem }) thread.setDaemon(True) LOG.info('Done initializing PKC playback, starting Kodi player') thread.start()