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(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 playback_init(plex_id, plex_type, playqueue): """ Playback setup if Kodi starts playing an item for the first time. """ LOG.info('Initializing PKC playback') xml = GetPlexMetadata(plex_id) try: xml[0].attrib except (IndexError, TypeError, AttributeError): LOG.error('Could not get a PMS xml for plex id %s', plex_id) # "Play error" dialog('notification', lang(29999), lang(30128), icon='{error}') return trailers = False api = API(xml[0]) if (plex_type == v.PLEX_TYPE_MOVIE and not api.resume_point() and settings('enableCinema') == "true"): if settings('askCinema') == "true": # "Play trailers?" trailers = dialog('yesno', lang(29999), lang(33016)) trailers = True if trailers else False else: trailers = True LOG.debug('Playing trailers: %s', trailers) playqueue.clear() if plex_type != v.PLEX_TYPE_CLIP: # Post to the PMS to create a playqueue - in any case due to Companion xml = init_plex_playqueue(plex_id, xml.attrib.get('librarySectionUUID'), mediatype=plex_type, trailers=trailers) if xml is None: LOG.error('Could not get a playqueue xml for plex id %s, UUID %s', plex_id, xml.attrib.get('librarySectionUUID')) # "Play error" dialog('notification', lang(29999), lang(30128), icon='{error}') return # Should already be empty, but just in case PL.get_playlist_details_from_xml(playqueue, xml) stack = _prep_playlist_stack(xml) # Sleep a bit to let setResolvedUrl do its thing - bit ugly sleep(200) _process_stack(playqueue, stack) # Reset some playback variables state.CONTEXT_MENU_PLAY = False state.FORCE_TRANSCODE = False # New thread to release this one sooner (e.g. harddisk spinning up) thread = Thread(target=Player().play, args=(playqueue.kodi_pl, )) thread.setDaemon(True) LOG.info('Done initializing PKC playback, starting Kodi player') # By design, PKC will start Kodi playback using Player().play(). Kodi # caches paths like our plugin://pkc. If we use Player().play() between # 2 consecutive startups of exactly the same Kodi library item, Kodi's # cache will have been flushed for some reason. Hence the 2nd call for # plugin://pkc will be lost; Kodi will try to startup playback for an empty # path: log entry is "CGUIWindowVideoBase::OnPlayMedia <missing path>" thread.start()
def _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 add_item_to_PMS_playlist(playlist, pos, plex_id=None, kodi_item=None): """ Adds a new item to the playlist at position pos [int] only on the Plex side of things (e.g. because the user changed the Kodi side) WILL ALSO UPDATE OUR PLAYLISTS Returns the PKC PlayList item or raises PlaylistError """ verify_kodi_item(plex_id, kodi_item) if plex_id: item = playlist_item_from_plex(plex_id) else: item = playlist_item_from_kodi(kodi_item) url = "{server}/%ss/%s?uri=%s" % (playlist.kind, playlist.id, item.uri) # Will always put the new item at the end of the Plex playlist xml = DU().downloadUrl(url, action_type="PUT") try: xml[-1].attrib except (TypeError, AttributeError, KeyError, IndexError): raise PlaylistError('Could not add item %s to playlist %s' % (kodi_item, playlist)) api = API(xml[-1]) item.xml = xml[-1] item.id = api.item_id() item.guid = api.guid_html_escaped() item.offset = api.resume_point() item.playcount = api.viewcount() playlist.items.append(item) if pos == len(playlist.items) - 1: # Item was added at the end _get_playListVersion_from_xml(playlist, xml) else: # Move the new item to the correct position move_playlist_item(playlist, len(playlist.items) - 1, pos) LOG.debug('Successfully added item on the Plex side: %s', playlist) return item
def _playback_init(plex_id, plex_type, playqueue, pos): """ Playback setup if Kodi starts playing an item for the first time. """ LOG.info('Initializing PKC playback') xml = GetPlexMetadata(plex_id) try: xml[0].attrib except (IndexError, TypeError, AttributeError): LOG.error('Could not get a PMS xml for plex id %s', plex_id) # "Play error" dialog('notification', lang(29999), lang(30128), icon='{error}') _ensure_resolve(abort=True) return if playqueue.kodi_pl.size() > 1: # Special case - we already got a filled Kodi playqueue try: _init_existing_kodi_playlist(playqueue) except PL.PlaylistError: LOG.error('Aborting playback_init for longer Kodi playlist') _ensure_resolve(abort=True) return # Now we need to use setResolvedUrl for the item at position pos _conclude_playback(playqueue, pos) return # "Usual" case - consider trailers and parts and build both Kodi and Plex # playqueues # Fail the item we're trying to play now so we can restart the player _ensure_resolve() api = API(xml[0]) trailers = False if (plex_type == v.PLEX_TYPE_MOVIE and not api.resume_point() and settings('enableCinema') == "true"): if settings('askCinema') == "true": # "Play trailers?" trailers = dialog('yesno', lang(29999), lang(33016)) trailers = True if trailers else False else: trailers = True LOG.debug('Playing trailers: %s', trailers) playqueue.clear() if plex_type != v.PLEX_TYPE_CLIP: # Post to the PMS to create a playqueue - in any case due to Companion xml = init_plex_playqueue(plex_id, xml.attrib.get('librarySectionUUID'), mediatype=plex_type, trailers=trailers) if xml is None: LOG.error('Could not get a playqueue xml for plex id %s, UUID %s', plex_id, xml.attrib.get('librarySectionUUID')) # "Play error" dialog('notification', lang(29999), lang(30128), icon='{error}') _ensure_resolve(abort=True) return # Should already be empty, but just in case PL.get_playlist_details_from_xml(playqueue, xml) stack = _prep_playlist_stack(xml) # Sleep a bit to let setResolvedUrl do its thing - bit ugly sleep(200) _process_stack(playqueue, stack) # Always resume if playback initiated via PMS and there IS a resume # point offset = api.resume_point() * 1000 if state.CONTEXT_MENU_PLAY else None # Reset some playback variables state.CONTEXT_MENU_PLAY = False state.FORCE_TRANSCODE = False # Do NOT set offset, because Kodi player will return here to resolveURL # New thread to release this one sooner (e.g. harddisk spinning up) thread = Thread(target=threaded_playback, args=(playqueue.kodi_pl, pos, offset)) thread.setDaemon(True) LOG.info( 'Done initializing playback, starting Kodi player at pos %s and ' 'resume point %s', pos, offset) # By design, PKC will start Kodi playback using Player().play(). Kodi # caches paths like our plugin://pkc. If we use Player().play() between # 2 consecutive startups of exactly the same Kodi library item, Kodi's # cache will have been flushed for some reason. Hence the 2nd call for # plugin://pkc will be lost; Kodi will try to startup playback for an empty # path: log entry is "CGUIWindowVideoBase::OnPlayMedia <missing path>" thread.start()