def __build_item(xml_element): api = API(xml_element) listitem = api.create_listitem() resume = api.resume_point() if resume: listitem.setProperty('resumetime', str(resume)) if (api.path_and_plex_id().startswith('/system/services') or api.path_and_plex_id().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.plex_type() == v.PLEX_TYPE_PHOTO: url = api.get_picture_path() else: params = { 'mode': 'play', 'plex_id': api.plex_id(), 'plex_type': api.plex_type(), } url = "plugin://%s?%s" % (v.ADDON_ID, urlencode(params)) xbmcplugin.addDirectoryItem(handle=HANDLE, url=url, listitem=listitem)
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.plex_id() item.plex_type = api.plex_type() 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 playlist_item_from_xml(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.plex_id() item.plex_type = api.plex_type() # item.id will only be set if you passed in an xml_video_element from e.g. # a playQueue item.id = api.item_id() 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 = db_element[0], db_element[4] except TypeError: pass item.guid = api.guid_html_escaped() item.playcount = api.viewcount() item.offset = api.resume_point() 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.plex_type() != 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.plex_id()) 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. # Also set kodi_id to None for playback via PMS, so that we're # using add-on paths. kodi_id = None kodi_type = None for part, _ in enumerate(item[0]): api.set_part_number(part) if kodi_id is None: # Need to redirect again to PKC to conclude playback params = { 'mode': 'play', 'plex_id': api.plex_id(), 'plex_type': api.plex_type() } path = ('plugin://%s/?%s' % (v.ADDON_TYPE[api.plex_type()], urlencode(params))) listitem = api.create_listitem() listitem.setPath(try_encode(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.viewcount(), 'offset': api.resume_point(), 'id': api.item_id() }) return stack
def _process_alexa(self, data): xml = GetPlexMetadata(data['key']) try: xml[0].attrib except (AttributeError, IndexError, TypeError): LOG.error('Could not download Plex metadata for: %s', data) return api = API(xml[0]) if api.plex_type() == v.PLEX_TYPE_ALBUM: LOG.debug('Plex music album detected') PQ.init_playqueue_from_plex_children( api.plex_id(), transient_token=data.get('token')) elif data['containerKey'].startswith('/playQueues/'): _, container_key, _ = ParseContainerKey(data['containerKey']) xml = DownloadChunks('{server}/playQueues/%s?' % container_key) if xml is None: # "Play error" dialog('notification', lang(29999), lang(30128), icon='{error}') return playqueue = PQ.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.plex_type()]) playqueue.clear() get_playlist_details_from_xml(playqueue, xml) playqueue.plex_transient_token = data.get('token') if data.get('offset') != '0': offset = float(data['offset']) / 1000.0 else: offset = None play_xml(playqueue, xml, offset) else: state.PLEX_TRANSIENT_TOKEN = data.get('token') if data.get('offset') != '0': state.RESUMABLE = True state.RESUME_PLAYBACK = True playback_triage(api.plex_id(), api.plex_type(), resolve=False)
def _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.plex_type()]) PQ.update_playqueue_from_PMS(playqueue, playqueue_id=container_key, repeat=query.get('repeat'), offset=data.get('offset'), transient_token=data.get('token'))
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') append_show_title = settings('OnDeckTvAppendShow') == 'true' append_sxxexx = 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.create_listitem( append_show_title=append_show_title, append_sxxexx=append_sxxexx) if directpaths: url = api.file_path() else: params = { 'mode': "play", 'plex_id': api.plex_id(), 'plex_type': api.plex_type() } 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, append_show_title=append_show_title, append_sxxexx=append_sxxexx) 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) global RESOLVE RESOLVE = resolve 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') _ensure_resolve(abort=True) return if offset != '0': offset = int(v.PLEX_TO_KODI_TIMEFACTOR * float(offset)) # Todo: implement offset api = API(xml[0]) listitem = PKC_ListItem() api.create_listitem(listitem) playqueue = PQ.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.plex_type()]) 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') _ensure_resolve(abort=True) return playurl = xml[0].attrib['key'] item.file = playurl listitem.setPath(try_encode(playurl)) playqueue.items.append(item) if resolve is True: result.listitem = listitem pickle_me(result) else: thread = Thread(target=Player().play, args={ 'item': try_encode(playurl), 'listitem': listitem }) thread.setDaemon(True) LOG.info('Done initializing PKC playback, starting Kodi player') thread.start()