示例#1
0
    def singleNode(self, indexnumber, tagname, mediatype, itemtype):
        tagname = try_encode(tagname)
        cleantagname = try_decode(normalize_nodes(tagname))
        nodepath = try_decode(
            xbmc.translatePath("special://profile/library/video/"))
        nodeXML = "%splex_%s.xml" % (nodepath, cleantagname)
        path = "library://video/plex_%s.xml" % cleantagname
        if v.KODIVERSION >= 17:
            # Krypton
            windowpath = "ActivateWindow(Videos,%s,return)" % path
        else:
            windowpath = "ActivateWindow(Video,%s,return)" % path

        # Create the video node directory
        if not exists_dir(nodepath):
            # We need to copy over the default items
            copytree(
                src=try_decode(
                    xbmc.translatePath("special://xbmc/system/library/video")),
                dst=try_decode(
                    xbmc.translatePath("special://profile/library/video")))

        labels = {
            'Favorite movies': 30180,
            'Favorite tvshows': 30181,
            'channels': 30173
        }
        label = lang(labels[tagname])
        embynode = "Plex.nodes.%s" % indexnumber
        window('%s.title' % embynode, value=label)
        window('%s.path' % embynode, value=windowpath)
        window('%s.content' % embynode, value=path)
        window('%s.type' % embynode, value=itemtype)

        if exists(try_encode(nodeXML)):
            # Don't recreate xml if already exists
            return

        if itemtype == "channels":
            root = self.commonRoot(order=1,
                                   label=label,
                                   tagname=tagname,
                                   roottype=2)
            etree.SubElement(
                root, 'path'
            ).text = "plugin://plugin.video.plexkodiconnect/?id=0&mode=channels"
        else:
            root = self.commonRoot(order=1, label=label, tagname=tagname)
            etree.SubElement(root, 'order', {
                'direction': "ascending"
            }).text = "sorttitle"

        etree.SubElement(root, 'content').text = mediatype

        try:
            indent(root)
        except:
            pass
        etree.ElementTree(root).write(nodeXML, encoding="UTF-8")
示例#2
0
def getVideoFiles(plexId, params):
    """
    GET VIDEO EXTRAS FOR LISTITEM

    returns the video files for the item as plugin listing, can be used for
    browsing the actual files or videoextras etc.
    """
    if plexId is None:
        filename = params.get('filename')
        if filename is not None:
            filename = filename[0]
            import re
            regex = re.compile(r'''library/metadata/(\d+)''')
            filename = regex.findall(filename)
            try:
                plexId = filename[0]
            except IndexError:
                pass

    if plexId is None:
        log.info('No Plex ID found, abort getting Extras')
        return xbmcplugin.endOfDirectory(HANDLE)

    item = GetPlexMetadata(plexId)
    try:
        path = item[0][0][0].attrib['file']
    except:
        log.error('Could not get file path for item %s' % plexId)
        return xbmcplugin.endOfDirectory(HANDLE)
    # Assign network protocol
    if path.startswith('\\\\'):
        path = path.replace('\\\\', 'smb://')
        path = path.replace('\\', '/')
    # Plex returns Windows paths as e.g. 'c:\slfkjelf\slfje\file.mkv'
    elif '\\' in path:
        path = path.replace('\\', '\\\\')
    # Directory only, get rid of filename
    path = path.replace(basename(path), '')
    if exists_dir(path):
        for root, dirs, files in walk(path):
            for directory in dirs:
                item_path = try_encode(join(root, directory))
                li = ListItem(item_path, path=item_path)
                xbmcplugin.addDirectoryItem(handle=HANDLE,
                                            url=item_path,
                                            listitem=li,
                                            isFolder=True)
            for file in files:
                item_path = try_encode(join(root, file))
                li = ListItem(item_path, path=item_path)
                xbmcplugin.addDirectoryItem(handle=HANDLE,
                                            url=file,
                                            listitem=li)
            break
    else:
        log.error('Kodi cannot access folder %s' % path)
    xbmcplugin.endOfDirectory(HANDLE)
示例#3
0
def getExtraFanArt(plexid, plexPath):
    """
    Get extrafanart for listitem
    will be called by skinhelper script to get the extrafanart
    for tvshows we get the plexid just from the path
    """
    log.debug('Called with plexid: %s, plexPath: %s' % (plexid, plexPath))
    if not plexid:
        if "plugin.video.plexkodiconnect" in plexPath:
            plexid = plexPath.split("/")[-2]
    if not plexid:
        log.error('Could not get a plexid, aborting')
        return xbmcplugin.endOfDirectory(HANDLE)

    # We need to store the images locally for this to work
    # because of the caching system in xbmc
    fanartDir = try_decode(translatePath(
        "special://thumbnails/plex/%s/" % plexid))
    if not exists_dir(fanartDir):
        # Download the images to the cache directory
        makedirs(fanartDir)
        xml = GetPlexMetadata(plexid)
        if xml is None:
            log.error('Could not download metadata for %s' % plexid)
            return xbmcplugin.endOfDirectory(HANDLE)

        api = API(xml[0])
        backdrops = api.artwork()['Backdrop']
        for count, backdrop in enumerate(backdrops):
            # Same ordering as in artwork
            fanartFile = try_encode(join(fanartDir, "fanart%.3d.jpg" % count))
            li = ListItem("%.3d" % count, path=fanartFile)
            xbmcplugin.addDirectoryItem(
                handle=HANDLE,
                url=fanartFile,
                listitem=li)
            copyfile(backdrop, try_decode(fanartFile))
    else:
        log.info("Found cached backdrop.")
        # Use existing cached images
        for root, dirs, files in walk(fanartDir):
            for file in files:
                fanartFile = try_encode(join(root, file))
                li = ListItem(file, path=fanartFile)
                xbmcplugin.addDirectoryItem(handle=HANDLE,
                                            url=fanartFile,
                                            listitem=li)
    xbmcplugin.endOfDirectory(HANDLE)
示例#4
0
def transcode_image_path(key, AuthToken, path, width, height):
    """
    Transcode Image support

    parameters:
        key
        AuthToken
        path - source path of current XML: path[srcXML]
        width
        height
    result:
        final path to image file
    """
    # external address - can we get a transcoding request for external images?
    if key.startswith('http://') or key.startswith('https://'):  
        path = key
    elif key.startswith('/'):  # internal full path.
        path = 'http://127.0.0.1:32400' + key
    else:  # internal path, add-on
        path = 'http://127.0.0.1:32400' + path + '/' + key
    path = try_encode(path)
    # This is bogus (note the extra path component) but ATV is stupid when it
    # comes to caching images, it doesn't use querystrings. Fortunately PMS is
    # lenient...
    transcode_path = ('/photo/:/transcode/%sx%s/%s'
                      % (width, height, quote_plus(path)))
    args = {
        'width': width,
        'height': height,
        'url': path
    }
    if AuthToken:
        args['X-Plex-Token'] = AuthToken
    return transcode_path + '?' + urlencode(args)
示例#5
0
    def _auto_pick_pms(self):
        """
        Will try to pick PMS based on machineIdentifier saved in file settings
        but only once

        Returns server or None if unsuccessful
        """
        https_updated = False
        checked_plex_tv = False
        server = None
        while True:
            if https_updated is False:
                serverlist = PF.discover_pms(self.plex_token)
                for item in serverlist:
                    if item.get('machineIdentifier') == self.serverid:
                        server = item
                if server is None:
                    name = settings('plex_servername')
                    LOG.warn(
                        'The PMS you have used before with a unique '
                        'machineIdentifier of %s and name %s is '
                        'offline', self.serverid, name)
                    return
            chk = self._check_pms_connectivity(server)
            if chk == 504 and https_updated is False:
                # switch HTTPS to HTTP or vice-versa
                if server['scheme'] == 'https':
                    server['scheme'] = 'http'
                else:
                    server['scheme'] = 'https'
                https_updated = True
                continue
            if chk == 401:
                LOG.warn('Not yet authorized for Plex server %s',
                         server['name'])
                if self.check_plex_tv_sign_in() is True:
                    if checked_plex_tv is False:
                        # Try again
                        checked_plex_tv = True
                        https_updated = False
                        continue
                    else:
                        LOG.warn('Not authorized even though we are signed '
                                 ' in to plex.tv correctly')
                        dialog(
                            'ok', lang(29999), '%s %s' %
                            (lang(39214), try_encode(server['name'])))
                        return
                else:
                    return
            # Problems connecting
            elif chk >= 400 or chk is False:
                LOG.warn('Problems connecting to server %s. chk is %s',
                         server['name'], chk)
                return
            LOG.info('We found a server to automatically connect to: %s',
                     server['name'])
            return server
示例#6
0
文件: kodidb.py 项目: camster1/RTOTV
 def set_json(jsonmethod, params):
     '''method to set info in the kodi json api'''
     kodi_json = {}
     kodi_json["jsonrpc"] = "2.0"
     kodi_json["method"] = jsonmethod
     kodi_json["params"] = params
     kodi_json["id"] = 1
     json_response = xbmc.executeJSONRPC(try_encode(json.dumps(kodi_json)))
     return json.loads(json_response.decode('utf-8', 'replace'))
示例#7
0
 def get_json(jsonmethod,
              sort=None,
              filters=None,
              fields=None,
              limits=None,
              returntype=None,
              optparam=None,
              filtertype=None):
     '''method to get details from the kodi json api'''
     kodi_json = {}
     kodi_json["jsonrpc"] = "2.0"
     kodi_json["method"] = jsonmethod
     kodi_json["params"] = {}
     if optparam:
         if isinstance(optparam, list):
             for param in optparam:
                 kodi_json["params"][param[0]] = param[1]
         else:
             kodi_json["params"][optparam[0]] = optparam[1]
     kodi_json["id"] = 1
     if sort:
         kodi_json["params"]["sort"] = sort
     if filters:
         if not filtertype:
             filtertype = "and"
         if len(filters) > 1:
             kodi_json["params"]["filter"] = {filtertype: filters}
         else:
             kodi_json["params"]["filter"] = filters[0]
     if fields:
         kodi_json["params"]["properties"] = fields
     if limits:
         kodi_json["params"]["limits"] = {
             "start": limits[0],
             "end": limits[1]
         }
     json_response = xbmc.executeJSONRPC(try_encode(json.dumps(kodi_json)))
     json_object = json.loads(json_response.decode('utf-8', 'replace'))
     # set the default returntype to prevent errors
     if "details" in jsonmethod.lower():
         result = {}
     else:
         result = []
     if 'result' in json_object:
         if returntype and returntype in json_object['result']:
             # returntype specified, return immediately
             result = json_object['result'][returntype]
         else:
             # no returntype specified, we'll have to look for it
             for key, value in json_object['result'].iteritems():
                 if not key == "limits" and (isinstance(value, list)
                                             or isinstance(value, dict)):
                     result = value
     else:
         log_msg(json_response)
         log_msg(kodi_json)
     return result
示例#8
0
 def set_json(jsonmethod, params):
     """method to set info in the kodi json api"""
     kodi_json = {}
     kodi_json["jsonrpc"] = "2.0"
     kodi_json["method"] = jsonmethod
     kodi_json["params"] = params
     kodi_json["id"] = 1
     json_response = xbmc.executeJSONRPC(try_encode(json.dumps(kodi_json)))
     if sys.version_info.major == 3:
         return json.loads(json_response)
     else:
         return json.loads(json_response.decode('utf-8', 'replace'))
示例#9
0
def _conclude_playback(playqueue, pos):
    """
    ONLY if actually being played (e.g. at 5th position of a playqueue).

        Decide on direct play, direct stream, transcoding
        path to
            direct paths: file itself
            PMS URL
            Web URL
        audiostream (e.g. let user choose)
        subtitle stream (e.g. let user choose)
        Init Kodi Playback (depending on situation):
            start playback
            return PKC listitem attached to result
    """
    LOG.info('Concluding playback for playqueue position %s', pos)
    result = Playback_Successful()
    listitem = PKC_ListItem()
    item = playqueue.items[pos]
    if item.xml is not None:
        # Got a Plex element
        api = API(item.xml)
        api.set_part_number(item.part)
        api.create_listitem(listitem)
        playutils = PlayUtils(api, item)
        playurl = playutils.getPlayUrl()
    else:
        playurl = item.file
    listitem.setPath(try_encode(playurl))
    if item.playmethod == 'DirectStream':
        listitem.setSubtitles(api.cache_external_subs())
    elif item.playmethod == 'Transcode':
        playutils.audio_subtitle_prefs(listitem)

    if state.RESUME_PLAYBACK is True:
        state.RESUME_PLAYBACK = False
        if (item.offset is None and item.plex_type
                not in (v.PLEX_TYPE_SONG, v.PLEX_TYPE_CLIP)):
            with plexdb.Get_Plex_DB() as plex_db:
                plex_dbitem = plex_db.getItem_byId(item.plex_id)
                file_id = plex_dbitem[1] if plex_dbitem else None
            with kodidb.GetKodiDB('video') as kodi_db:
                item.offset = kodi_db.get_resume(file_id)
        LOG.info('Resuming playback at %s', item.offset)
        listitem.setProperty('StartOffset', str(item.offset))
        listitem.setProperty('resumetime', str(item.offset))
    # Reset the resumable flag
    result.listitem = listitem
    pickle_me(result)
    LOG.info('Done concluding playback')
示例#10
0
    def create_frame(data, opcode):
        """
        create frame to send text, binary and other data.

        data: data to send. This is string value(byte array).
            if opcode is OPCODE_TEXT and this value is uniocde,
            data value is conveted into unicode string, automatically.

        opcode: operation code. please see OPCODE_XXX.
        """
        if opcode == ABNF.OPCODE_TEXT and isinstance(data, unicode):
            data = utils.try_encode(data)
        # mask must be set if send data from client
        return ABNF(1, 0, 0, 0, opcode, 1, data)
示例#11
0
 def __repr__(self):
     """
     Print the playlist, e.g. to log. Returns utf-8 encoded string
     """
     answ = u'{\'%s\': {\'id\': %s, ' % (self.__class__.__name__, self.id)
     # For some reason, can't use dir directly
     for key in self.__dict__:
         if key in ('id', 'items', 'kodi_pl'):
             continue
         if isinstance(getattr(self, key), str):
             answ += '\'%s\': \'%s\', ' % (key,
                                           try_decode(getattr(self, key)))
         else:
             # e.g. int
             answ += '\'%s\': %s, ' % (key, unicode(getattr(self, key)))
     return try_encode(answ + '\'items\': %s}}') % self.items
示例#12
0
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
示例#13
0
 def __repr__(self):
     """
     Print the playlist, e.g. to log
     """
     answ = '{\'%s\': {' % (self.__class__.__name__)
     # For some reason, can't use dir directly
     answ += '\'id\': %s, ' % self.id
     for key in self.__dict__:
         if key in ('id', 'items', 'kodi_pl'):
             continue
         if isinstance(getattr(self, key), (str, unicode)):
             answ += '\'%s\': \'%s\', ' % (key,
                                           try_encode(getattr(self, key)))
         else:
             # e.g. int
             answ += '\'%s\': %s, ' % (key, str(getattr(self, key)))
     return answ + '\'items\': %s}}' % self.items
示例#14
0
 def __repr__(self):
     """
     Print the playlist item, e.g. to log. Returns utf-8 encoded string
     """
     answ = (u'{\'%s\': {\'id\': \'%s\', \'plex_id\': \'%s\', ' %
             (self.__class__.__name__, self.id, self.plex_id))
     for key in self.__dict__:
         if key in ('id', 'plex_id', 'xml'):
             continue
         if isinstance(getattr(self, key), str):
             answ += '\'%s\': \'%s\', ' % (key,
                                           try_decode(getattr(self, key)))
         else:
             # e.g. int
             answ += '\'%s\': %s, ' % (key, unicode(getattr(self, key)))
     if self.xml is None:
         answ += '\'xml\': None}}'
     else:
         answ += '\'xml\': \'%s\'}}' % self.xml.tag
     return try_encode(answ)
示例#15
0
 def __repr__(self):
     """
     Print the playlist item, e.g. to log
     """
     answ = '{\'%s\': {' % (self.__class__.__name__)
     answ += '\'id\': \'%s\', ' % self.id
     answ += '\'plex_id\': \'%s\', ' % self.plex_id
     for key in self.__dict__:
         if key in ('id', 'plex_id', 'xml'):
             continue
         if isinstance(getattr(self, key), (str, unicode)):
             answ += '\'%s\': \'%s\', ' % (key,
                                           try_encode(getattr(self, key)))
         else:
             # e.g. int
             answ += '\'%s\': %s, ' % (key, str(getattr(self, key)))
     if self.xml is None:
         answ += '\'xml\': None}}'
     else:
         answ += '\'xml\': \'%s\'}}' % self.xml.tag
     return answ
示例#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)
    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()
示例#17
0
 def cacheTexture(self, url):
     # Cache a single image url to the texture cache
     if url and self.enableTextureCache:
         self.queue.put(double_urlencode(try_encode(url)))
示例#18
0
def choose_home_user(token):
    """
    Let's user choose from a list of Plex home users. Will switch to that
    user accordingly.

    Returns a dict:
    {
        'username':             Unicode
        'userid': ''            Plex ID of the user
        'token': ''             User's token
        'protected':            True if PIN is needed, else False
    }

    Will return False if something went wrong (wrong PIN, no connection)
    """
    # Get list of Plex home users
    users = list_home_users(token)
    if not users:
        LOG.error("User download failed.")
        return False
    userlist = []
    userlist_coded = []
    for user in users:
        username = user['title']
        userlist.append(username)
        # To take care of non-ASCII usernames
        userlist_coded.append(try_encode(username))
    usernumber = len(userlist)
    username = ''
    usertoken = ''
    trials = 0
    while trials < 3:
        if usernumber > 1:
            # Select user
            user_select = dialog('select',
                                 lang(29999) + lang(39306), userlist_coded)
            if user_select == -1:
                LOG.info("No user selected.")
                settings('username', value='')
                executebuiltin('Addon.OpenSettings(%s)' % v.ADDON_ID)
                return False
        # Only 1 user received, choose that one
        else:
            user_select = 0
        selected_user = userlist[user_select]
        LOG.info("Selected user: %s", selected_user)
        user = users[user_select]
        # Ask for PIN, if protected:
        pin = None
        if user['protected'] == '1':
            LOG.debug('Asking for users PIN')
            pin = dialog('input',
                         lang(39307) + selected_user,
                         '',
                         type='{numeric}',
                         option='{hide}')
            # User chose to cancel
            # Plex bug: don't call url for protected user with empty PIN
            if not pin:
                trials += 1
                continue
        # Switch to this Plex Home user, if applicable
        result = switch_home_user(user['id'], pin, token,
                                  settings('plex_machineIdentifier'))
        if result:
            # Successfully retrieved username: break out of while loop
            username = result['username']
            usertoken = result['usertoken']
            break
        # Couldn't get user auth
        else:
            trials += 1
            # Could not login user, please try again
            if not dialog('yesno',
                          heading='{plex}',
                          line1=lang(39308) + selected_user,
                          line2=lang(39309)):
                # User chose to cancel
                break
    if not username:
        LOG.error('Failed signing in a user to plex.tv')
        executebuiltin('Addon.OpenSettings(%s)' % v.ADDON_ID)
        return False
    return {
        'username': username,
        'userid': user['id'],
        'protected': True if user['protected'] == '1' else False,
        'token': usertoken
    }
示例#19
0
    def viewNode(self,
                 indexnumber,
                 tagname,
                 mediatype,
                 viewtype,
                 viewid,
                 delete=False):
        # Plex: reassign mediatype due to Kodi inner workings
        # How many items do we get at most?
        limit = state.FETCH_PMS_ITEM_NUMBER
        mediatypes = {
            'movie': 'movies',
            'show': 'tvshows',
            'photo': 'photos',
            'homevideo': 'homevideos',
            'musicvideos': 'musicvideos'
        }
        mediatype = mediatypes[mediatype]

        if viewtype == "mixed":
            dirname = "%s-%s" % (viewid, mediatype)
        else:
            dirname = viewid

        # Returns strings
        path = try_decode(
            xbmc.translatePath("special://profile/library/video/"))
        nodepath = try_decode(
            xbmc.translatePath("special://profile/library/video/Plex-%s/" %
                               dirname))

        if delete:
            if exists_dir(nodepath):
                from shutil import rmtree
                rmtree(nodepath)
                log.info("Sucessfully removed videonode: %s." % tagname)
            return

        # Verify the video directory
        if not exists_dir(path):
            copytree(
                src=try_decode(
                    xbmc.translatePath("special://xbmc/system/library/video")),
                dst=try_decode(
                    xbmc.translatePath("special://profile/library/video")))

        # Create the node directory
        if mediatype != "photos":
            if not exists_dir(nodepath):
                # folder does not exist yet
                log.debug('Creating folder %s' % nodepath)
                makedirs(nodepath)

        # Create index entry
        nodeXML = "%sindex.xml" % nodepath
        # Set windows property
        path = "library://video/Plex-%s/" % dirname
        for i in range(1, indexnumber):
            # Verify to make sure we don't create duplicates
            if window('Plex.nodes.%s.index' % i) == path:
                return

        if mediatype == "photos":
            path = "plugin://plugin.video.plexkodiconnect?mode=browseplex&key=/library/sections/%s&id=%s" % (
                viewid, viewid)

        window('Plex.nodes.%s.index' % indexnumber, value=path)

        # Root
        if not mediatype == "photos":
            if viewtype == "mixed":
                specialtag = "%s-%s" % (tagname, mediatype)
                root = self.commonRoot(order=0,
                                       label=specialtag,
                                       tagname=tagname,
                                       roottype=0)
            else:
                root = self.commonRoot(order=0,
                                       label=tagname,
                                       tagname=tagname,
                                       roottype=0)
            try:
                indent(root)
            except:
                pass
            etree.ElementTree(root).write(nodeXML, encoding="UTF-8")

        nodetypes = {
            '1': "all",
            '2': "recent",
            '3': "recentepisodes",
            '4': "inprogress",
            '5': "inprogressepisodes",
            '6': "unwatched",
            '7': "nextepisodes",
            '8': "sets",
            '9': "genres",
            '10': "random",
            '11': "recommended",
            '12': "ondeck",
            '13': 'browsefiles'
        }
        mediatypes = {
            # label according to nodetype per mediatype
            'movies': {
                '1': tagname,
                '2': 30174,
                # '4': 30177,
                # '6': 30189,
                '8': 39501,
                '9': 135,
                '10': 30227,
                '11': 30230,
                '12': 39500,
                '13': 39702
            },
            'tvshows': {
                '1': tagname,
                # '2': 30170,
                '3': 30174,
                # '4': 30171,
                # '5': 30178,
                # '7': 30179,
                '9': 135,
                '10': 30227,
                # '11': 30230,
                '12': 39500,
                '13': 39702
            },
            'homevideos': {
                '1': tagname,
                '2': 30251,
                '11': 30253,
                '13': 39702
            },
            'photos': {
                '1': tagname,
                '2': 30252,
                '8': 30255,
                '11': 30254,
                '13': 39702
            },
            'musicvideos': {
                '1': tagname,
                '2': 30256,
                '4': 30257,
                '6': 30258,
                '13': 39702
            }
        }

        # Key: nodetypes, value: sort order in Kodi
        sortorder = {
            '1': '3',  # "all",
            '2': '2',  # "recent",
            '3': '2',  # "recentepisodes",
            # '4': # "inprogress",
            # '5': # "inprogressepisodes",
            # '6': # "unwatched",
            # '7': # "nextepisodes",
            '8': '7',  # "sets",
            '9': '6',  # "genres",
            '10': '8',  # "random",
            '11': '5',  # "recommended",
            '12': '1',  # "ondeck"
            '13': '9'  # browse by folder
        }

        nodes = mediatypes[mediatype]
        for node in nodes:

            nodetype = nodetypes[node]
            nodeXML = "%s%s_%s.xml" % (nodepath, viewid, nodetype)
            # Get label
            stringid = nodes[node]
            if node != "1":
                label = lang(stringid)
                if not label:
                    label = xbmc.getLocalizedString(stringid)
            else:
                label = stringid

            # Set window properties
            if (mediatype == "homevideos"
                    or mediatype == "photos") and nodetype == "all":
                # Custom query
                path = (
                    "plugin://plugin.video.plexkodiconnect/?id=%s&mode=browseplex&type=%s"
                    % (viewid, mediatype))
            elif (mediatype == "homevideos" or mediatype == "photos"):
                # Custom query
                path = (
                    "plugin://plugin.video.plexkodiconnect/?id=%s&mode=browseplex&type=%s&folderid=%s"
                    % (viewid, mediatype, nodetype))
            elif nodetype == "nextepisodes":
                # Custom query
                path = "plugin://plugin.video.plexkodiconnect/?id=%s&mode=nextup&limit=%s" % (
                    tagname, limit)
            # elif v.KODIVERSION == 14 and nodetype == "recentepisodes":
            elif nodetype == "recentepisodes":
                # Custom query
                path = (
                    "plugin://plugin.video.plexkodiconnect/?id=%s&mode=recentepisodes&type=%s&tagname=%s&limit=%s"
                    % (viewid, mediatype, tagname, limit))
            elif v.KODIVERSION == 14 and nodetype == "inprogressepisodes":
                # Custom query
                path = "plugin://plugin.video.plexkodiconnect/?id=%s&mode=inprogressepisodes&limit=%s" % (
                    tagname, limit)
            elif nodetype == 'ondeck':
                # PLEX custom query
                if mediatype == "tvshows":
                    path = (
                        "plugin://plugin.video.plexkodiconnect/?id=%s&mode=ondeck&type=%s&tagname=%s&limit=%s"
                        % (viewid, mediatype, tagname, limit))
                elif mediatype == "movies":
                    # Reset nodetype; we got the label
                    nodetype = 'inprogress'
            elif nodetype == 'browsefiles':
                path = 'plugin://plugin.video.plexkodiconnect?mode=browseplex&key=/library/sections/%s/folder' % viewid
            else:
                path = "library://video/Plex-%s/%s_%s.xml" % (dirname, viewid,
                                                              nodetype)

            if mediatype == "photos":
                windowpath = "ActivateWindow(Pictures,%s,return)" % path
            else:
                if v.KODIVERSION >= 17:
                    # Krypton
                    windowpath = "ActivateWindow(Videos,%s,return)" % path
                else:
                    windowpath = "ActivateWindow(Video,%s,return)" % path

            if nodetype == "all":

                if viewtype == "mixed":
                    templabel = "%s-%s" % (tagname, mediatype)
                else:
                    templabel = label

                embynode = "Plex.nodes.%s" % indexnumber
                window('%s.title' % embynode, value=templabel)
                window('%s.path' % embynode, value=windowpath)
                window('%s.content' % embynode, value=path)
                window('%s.type' % embynode, value=mediatype)
            else:
                embynode = "Plex.nodes.%s.%s" % (indexnumber, nodetype)
                window('%s.title' % embynode, value=label)
                window('%s.path' % embynode, value=windowpath)
                window('%s.content' % embynode, value=path)

            if mediatype == "photos":
                # For photos, we do not create a node in videos but we do want the window props
                # to be created.
                # To do: add our photos nodes to kodi picture sources somehow
                continue

            if exists(try_encode(nodeXML)):
                # Don't recreate xml if already exists
                continue

            # Create the root
            if (nodetype in ("nextepisodes", "ondeck", 'recentepisodes',
                             'browsefiles') or mediatype == "homevideos"):
                # Folder type with plugin path
                root = self.commonRoot(order=sortorder[node],
                                       label=label,
                                       tagname=tagname,
                                       roottype=2)
                etree.SubElement(root, 'path').text = path
                etree.SubElement(root, 'content').text = "episodes"
            else:
                root = self.commonRoot(order=sortorder[node],
                                       label=label,
                                       tagname=tagname)
                if nodetype in ('recentepisodes', 'inprogressepisodes'):
                    etree.SubElement(root, 'content').text = "episodes"
                else:
                    etree.SubElement(root, 'content').text = mediatype

                # Elements per nodetype
                if nodetype == "all":
                    etree.SubElement(root, 'order', {
                        'direction': "ascending"
                    }).text = "sorttitle"

                elif nodetype == "recent":
                    etree.SubElement(root, 'order', {
                        'direction': "descending"
                    }).text = "dateadded"
                    etree.SubElement(root, 'limit').text = limit
                    if settings('MovieShowWatched') == 'false':
                        rule = etree.SubElement(root, 'rule', {
                            'field': "playcount",
                            'operator': "is"
                        })
                        etree.SubElement(rule, 'value').text = "0"

                elif nodetype == "inprogress":
                    etree.SubElement(root, 'rule', {
                        'field': "inprogress",
                        'operator': "true"
                    })
                    etree.SubElement(root, 'limit').text = limit
                    etree.SubElement(root, 'order', {
                        'direction': 'descending'
                    }).text = 'lastplayed'

                elif nodetype == "genres":
                    etree.SubElement(root, 'order', {
                        'direction': "ascending"
                    }).text = "sorttitle"
                    etree.SubElement(root, 'group').text = "genres"

                elif nodetype == "unwatched":
                    etree.SubElement(root, 'order', {
                        'direction': "ascending"
                    }).text = "sorttitle"
                    rule = etree.SubElement(root, "rule", {
                        'field': "playcount",
                        'operator': "is"
                    })
                    etree.SubElement(rule, 'value').text = "0"

                elif nodetype == "sets":
                    etree.SubElement(root, 'order', {
                        'direction': "ascending"
                    }).text = "sorttitle"
                    etree.SubElement(root, 'group').text = "tags"

                elif nodetype == "random":
                    etree.SubElement(root, 'order', {
                        'direction': "ascending"
                    }).text = "random"
                    etree.SubElement(root, 'limit').text = limit

                elif nodetype == "recommended":
                    etree.SubElement(root, 'order', {
                        'direction': "descending"
                    }).text = "rating"
                    etree.SubElement(root, 'limit').text = limit
                    rule = etree.SubElement(root, 'rule', {
                        'field': "playcount",
                        'operator': "is"
                    })
                    etree.SubElement(rule, 'value').text = "0"
                    rule2 = etree.SubElement(root,
                                             'rule',
                                             attrib={
                                                 'field': "rating",
                                                 'operator': "greaterthan"
                                             })
                    etree.SubElement(rule2, 'value').text = "7"

                elif nodetype == "recentepisodes":
                    # Kodi Isengard, Jarvis
                    etree.SubElement(root, 'order', {
                        'direction': "descending"
                    }).text = "dateadded"
                    etree.SubElement(root, 'limit').text = limit
                    rule = etree.SubElement(root, 'rule', {
                        'field': "playcount",
                        'operator': "is"
                    })
                    etree.SubElement(rule, 'value').text = "0"

                elif nodetype == "inprogressepisodes":
                    # Kodi Isengard, Jarvis
                    etree.SubElement(root, 'limit').text = limit
                    rule = etree.SubElement(root,
                                            'rule',
                                            attrib={
                                                'field': "inprogress",
                                                'operator': "true"
                                            })

            try:
                indent(root)
            except:
                pass
            etree.ElementTree(root).write(nodeXML, encoding="UTF-8")
示例#20
0
    def audio_subtitle_prefs(self, listitem):
        """
        For transcoding only

        Called at the very beginning of play; used to change audio and subtitle
        stream by a PUT request to the PMS
        """
        # Set media and part where we're at
        if self.api.mediastream is None:
            self.api.mediastream_number()
        try:
            mediastreams = self.api.plex_media_streams()
        except (TypeError, IndexError):
            LOG.error('Could not get media %s, part %s',
                      self.api.mediastream, self.api.part)
            return
        part_id = mediastreams.attrib['id']
        audio_streams_list = []
        audio_streams = []
        subtitle_streams_list = []
        # No subtitles as an option
        subtitle_streams = [lang(39706)]
        downloadable_streams = []
        download_subs = []
        # selectAudioIndex = ""
        select_subs_index = ""
        audio_numb = 0
        # Remember 'no subtitles'
        sub_num = 1
        default_sub = None

        for stream in mediastreams:
            # Since Plex returns all possible tracks together, have to sort
            # them.
            index = stream.attrib.get('id')
            typus = stream.attrib.get('streamType')
            # Audio
            if typus == "2":
                codec = stream.attrib.get('codec')
                channellayout = stream.attrib.get('audioChannelLayout', "")
                try:
                    track = "%s %s - %s %s" % (audio_numb+1,
                                               stream.attrib['language'],
                                               codec,
                                               channellayout)
                except KeyError:
                    track = "%s %s - %s %s" % (audio_numb+1,
                                               lang(39707),  # unknown
                                               codec,
                                               channellayout)
                audio_streams_list.append(index)
                audio_streams.append(try_encode(track))
                audio_numb += 1

            # Subtitles
            elif typus == "3":
                try:
                    track = "%s %s" % (sub_num+1, stream.attrib['language'])
                except KeyError:
                    track = "%s %s (%s)" % (sub_num+1,
                                            lang(39707),  # unknown
                                            stream.attrib.get('codec'))
                default = stream.attrib.get('default')
                forced = stream.attrib.get('forced')
                downloadable = stream.attrib.get('key')

                if default:
                    track = "%s - %s" % (track, lang(39708))  # Default
                if forced:
                    track = "%s - %s" % (track, lang(39709))  # Forced
                if downloadable:
                    # We do know the language - temporarily download
                    if 'language' in stream.attrib:
                        path = self.api.download_external_subtitles(
                            '{server}%s' % stream.attrib['key'],
                            "subtitle.%s.%s" % (stream.attrib['languageCode'],
                                                stream.attrib['codec']))
                    # We don't know the language - no need to download
                    else:
                        path = self.api.attach_plex_token_to_url(
                            "%s%s" % (window('pms_server'),
                                      stream.attrib['key']))
                    downloadable_streams.append(index)
                    download_subs.append(try_encode(path))
                else:
                    track = "%s (%s)" % (track, lang(39710))  # burn-in
                if stream.attrib.get('selected') == '1' and downloadable:
                    # Only show subs without asking user if they can be
                    # turned off
                    default_sub = index

                subtitle_streams_list.append(index)
                subtitle_streams.append(try_encode(track))
                sub_num += 1

        if audio_numb > 1:
            resp = dialog('select', lang(33013), audio_streams)
            if resp > -1:
                # User selected some audio track
                args = {
                    'audioStreamID': audio_streams_list[resp],
                    'allParts': 1
                }
                DU().downloadUrl('{server}/library/parts/%s' % part_id,
                                 action_type='PUT',
                                 parameters=args)

        if sub_num == 1:
            # No subtitles
            return

        select_subs_index = None
        if (settings('pickPlexSubtitles') == 'true' and
                default_sub is not None):
            LOG.info('Using default Plex subtitle: %s', default_sub)
            select_subs_index = default_sub
        else:
            resp = dialog('select', lang(33014), subtitle_streams)
            if resp > 0:
                select_subs_index = subtitle_streams_list[resp-1]
            else:
                # User selected no subtitles or backed out of dialog
                select_subs_index = ''

        LOG.debug('Adding external subtitles: %s', download_subs)
        # Enable Kodi to switch autonomously to downloadable subtitles
        if download_subs:
            listitem.setSubtitles(download_subs)
        # Don't additionally burn in subtitles
        if select_subs_index in downloadable_streams:
            select_subs_index = ''
        # Now prep the PMS for our choice
        args = {
            'subtitleStreamID': select_subs_index,
            'allParts': 1
        }
        DU().downloadUrl('{server}/library/parts/%s' % part_id,
                         action_type='PUT',
                         parameters=args)