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
Example #2
0
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)
Example #3
0
 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)
Example #4
0
 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)
Example #5
0
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
Example #6
0
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
Example #8
0
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
Example #9
0
 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'))
Example #10
0
 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)))
Example #11
0
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)
Example #12
0
    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
Example #13
0
    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'])
Example #15
0
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)
Example #16
0
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()