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 (!! exists() needs /  or \ at end)
    path = path.replace(os_path.basename(path), '')
    # Only proceed if we can access this folder
    import xbmcvfs
    if xbmcvfs.exists(path):
        # Careful, returns encoded strings!
        dirs, files = xbmcvfs.listdir(path)
        for file in files:
            file = path + tryDecode(file)
            li = ListItem(file, path=file)
            xbmcplugin.addDirectoryItem(handle=HANDLE,
                                        url=tryEncode(file),
                                        listitem=li)
        for dir in dirs:
            dir = path + tryDecode(dir)
            li = ListItem(dir, path=dir)
            xbmcplugin.addDirectoryItem(handle=HANDLE,
                                        url=tryEncode(dir),
                                        listitem=li,
                                        isFolder=True)
    else:
        log.warn('Kodi cannot access folder %s' % path)
    xbmcplugin.endOfDirectory(HANDLE)
Exemple #2
0
    def singleNode(self, indexnumber, tagname, mediatype, itemtype):

        window = utils.window

        tagname = utils.tryEncode(tagname)
        cleantagname = utils.normalize_nodes(tagname)
        nodepath = utils.tryDecode(
            xbmc.translatePath("special://profile/library/video/"))
        nodeXML = "%splex_%s.xml" % (nodepath, cleantagname)
        path = "library://video/plex_%s.xml" % cleantagname
        windowpath = "ActivateWindow(Video,%s,return)" % path

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

        labels = {
            'Favorite movies': 30180,
            'Favorite tvshows': 30181,
            'channels': 30173
        }
        label = utils.language(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 xbmcvfs.exists(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:
            utils.indent(root)
        except:
            pass
        etree.ElementTree(root).write(nodeXML)
Exemple #3
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 (!! exists() needs /  or \ at end)
    path = path.replace(os_path.basename(path), '')
    # Only proceed if we can access this folder
    import xbmcvfs
    if xbmcvfs.exists(path):
        # Careful, returns encoded strings!
        dirs, files = xbmcvfs.listdir(path)
        for file in files:
            file = path + tryDecode(file)
            li = ListItem(file, path=file)
            xbmcplugin.addDirectoryItem(handle=HANDLE,
                                        url=tryEncode(file),
                                        listitem=li)
        for dir in dirs:
            dir = path + tryDecode(dir)
            li = ListItem(dir, path=dir)
            xbmcplugin.addDirectoryItem(handle=HANDLE,
                                        url=tryEncode(dir),
                                        listitem=li,
                                        isFolder=True)
    else:
        log.warn('Kodi cannot access folder %s' % path)
    xbmcplugin.endOfDirectory(HANDLE)
Exemple #4
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
    """
    import xbmcvfs
    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 = tryDecode(translatePath(
        "special://thumbnails/plex/%s/" % plexid))
    if not xbmcvfs.exists(fanartDir):
        # Download the images to the cache directory
        xbmcvfs.mkdirs(tryEncode(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.getAllArtwork()['Backdrop']
        for count, backdrop in enumerate(backdrops):
            # Same ordering as in artwork
            if os_path.supports_unicode_filenames:
                fanartFile = os_path.join(fanartDir,
                                          "fanart%.3d.jpg" % count)
            else:
                fanartFile = os_path.join(
                    tryEncode(fanartDir),
                    tryEncode("fanart%.3d.jpg" % count))
            li = ListItem("%.3d" % count, path=fanartFile)
            xbmcplugin.addDirectoryItem(
                handle=HANDLE,
                url=fanartFile,
                listitem=li)
            xbmcvfs.copy(backdrop, fanartFile)
    else:
        log.info("Found cached backdrop.")
        # Use existing cached images
        dirs, files = xbmcvfs.listdir(fanartDir)
        for file in files:
            fanartFile = os_path.join(fanartDir, tryDecode(file))
            li = ListItem(file, path=fanartFile)
            xbmcplugin.addDirectoryItem(handle=HANDLE,
                                        url=fanartFile,
                                        listitem=li)
    xbmcplugin.endOfDirectory(HANDLE)
    def singleNode(self, indexnumber, tagname, mediatype, itemtype):

        tagname = tryEncode(tagname)
        cleantagname = normalize_nodes(tagname)
        nodepath = tryDecode(xbmc.translatePath(
            "special://profile/library/video/"))
        nodeXML = "%splex_%s.xml" % (nodepath, cleantagname)
        path = "library://video/plex_%s.xml" % cleantagname
        if self.kodiversion >= 17:
            # Krypton
            windowpath = "ActivateWindow(Videos,%s,return)" % path
        else:
            windowpath = "ActivateWindow(Video,%s,return)" % path

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

        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 xbmcvfs.exists(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)
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
    """
    import xbmcvfs
    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 = tryDecode(
        translatePath("special://thumbnails/plex/%s/" % plexid))
    if not xbmcvfs.exists(fanartDir):
        # Download the images to the cache directory
        xbmcvfs.mkdirs(tryEncode(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.getAllArtwork()['Backdrop']
        for count, backdrop in enumerate(backdrops):
            # Same ordering as in artwork
            if os_path.supports_unicode_filenames:
                fanartFile = os_path.join(fanartDir, "fanart%.3d.jpg" % count)
            else:
                fanartFile = os_path.join(tryEncode(fanartDir),
                                          tryEncode("fanart%.3d.jpg" % count))
            li = ListItem("%.3d" % count, path=fanartFile)
            xbmcplugin.addDirectoryItem(handle=HANDLE,
                                        url=fanartFile,
                                        listitem=li)
            xbmcvfs.copy(backdrop, fanartFile)
    else:
        log.info("Found cached backdrop.")
        # Use existing cached images
        dirs, files = xbmcvfs.listdir(fanartDir)
        for file in files:
            fanartFile = os_path.join(fanartDir, tryDecode(file))
            li = ListItem(file, path=fanartFile)
            xbmcplugin.addDirectoryItem(handle=HANDLE,
                                        url=fanartFile,
                                        listitem=li)
    xbmcplugin.endOfDirectory(HANDLE)
Exemple #7
0
    def deleteCachedArtwork(self, url):
        # Only necessary to remove and apply a new backdrop or poster
        connection = utils.kodiSQL('texture')
        cursor = connection.cursor()

        try:
            cursor.execute("SELECT cachedurl FROM texture WHERE url = ?", (url,))
            cachedurl = cursor.fetchone()[0]

        except TypeError:
            self.logMsg("Could not find cached url.", 1)

        except OperationalError:
            self.logMsg("Database is locked. Skip deletion process.", 1)

        else: # Delete thumbnail as well as the entry
            thumbnails = utils.tryDecode(
                xbmc.translatePath("special://thumbnails/%s" % cachedurl))
            self.logMsg("Deleting cached thumbnail: %s" % thumbnails, 1)
            xbmcvfs.delete(thumbnails)

            try:
                cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
                connection.commit()
            except OperationalError:
                self.logMsg("Issue deleting url from cache. Skipping.", 2)

        finally:
            cursor.close()
Exemple #8
0
    def deleteCachedArtwork(self, url):
        # Only necessary to remove and apply a new backdrop or poster
        connection = utils.kodiSQL('texture')
        cursor = connection.cursor()

        try:
            cursor.execute("SELECT cachedurl FROM texture WHERE url = ?",
                           (url, ))
            cachedurl = cursor.fetchone()[0]

        except TypeError:
            self.logMsg("Could not find cached url.", 1)

        except OperationalError:
            self.logMsg("Database is locked. Skip deletion process.", 1)

        else:  # Delete thumbnail as well as the entry
            thumbnails = utils.tryDecode(
                xbmc.translatePath("special://thumbnails/%s" % cachedurl))
            self.logMsg("Deleting cached thumbnail: %s" % thumbnails, 1)
            xbmcvfs.delete(thumbnails)

            try:
                cursor.execute("DELETE FROM texture WHERE url = ?", (url, ))
                connection.commit()
            except OperationalError:
                self.logMsg("Issue deleting url from cache. Skipping.", 2)

        finally:
            cursor.close()
Exemple #9
0
    def single_urlencode(self, text):

        text = urllib.urlencode({'blahblahblah': utils.tryEncode(text)
                                 })  #urlencode needs a utf- string
        text = text[13:]

        return utils.tryDecode(text)  #return the result again as unicode
Exemple #10
0
def getRealFileName(filename, isTemp=False):
    #get the filename path accessible by python if possible...

    if not xbmcvfs.exists(filename):
        logMsg("File does not exist! %s" % (filename), 0)
        return (False, "")

    #if we use os.path method on older python versions (sunch as some android builds), we need to pass arguments as string
    if os.path.supports_unicode_filenames:
        checkfile = filename
    else:
        checkfile = utils.tryEncode(filename)

    # determine if our python module is able to access the file directly...
    if os.path.exists(checkfile):
        filename = filename
    elif os.path.exists(
            checkfile.replace("smb://", "\\\\").replace("/", "\\")):
        filename = filename.replace("smb://", "\\\\").replace("/", "\\")
    else:
        #file can not be accessed by python directly, we copy it for processing...
        isTemp = True
        if "/" in filename: filepart = filename.split("/")[-1]
        else: filepart = filename.split("\\")[-1]
        tempfile = "special://temp/" + filepart
        xbmcvfs.copy(filename, tempfile)
        filename = utils.tryDecode(xbmc.translatePath(tempfile))

    return (isTemp, filename)
Exemple #11
0
def getRealFileName(filename, isTemp=False):
    #get the filename path accessible by python if possible...
    
    if not xbmcvfs.exists(filename):
        logMsg( "File does not exist! %s" %(filename), 0)
        return (False, "")
    
    #if we use os.path method on older python versions (sunch as some android builds), we need to pass arguments as string
    if os.path.supports_unicode_filenames:
        checkfile = filename
    else:
        checkfile = utils.tryEncode(filename)
    
    # determine if our python module is able to access the file directly...
    if os.path.exists(checkfile):
        filename = filename
    elif os.path.exists(checkfile.replace("smb://","\\\\").replace("/","\\")):
        filename = filename.replace("smb://","\\\\").replace("/","\\")
    else:
        #file can not be accessed by python directly, we copy it for processing...
        isTemp = True
        if "/" in filename: filepart = filename.split("/")[-1]
        else: filepart = filename.split("\\")[-1]
        tempfile = "special://temp/"+filepart
        xbmcvfs.copy(filename, tempfile)
        filename = utils.tryDecode(xbmc.translatePath(tempfile))
        
    return (isTemp,filename)
Exemple #12
0
    def onPlayBackResumed(self):

        currentFile = self.currentFile
        self.logMsg("PLAYBACK_RESUMED: %s" % utils.tryDecode(currentFile), 2)

        if self.played_info.get(currentFile):
            self.played_info[currentFile]['paused'] = False
            self.reportPlayback()
Exemple #13
0
    def onPlayBackResumed(self):

        currentFile = self.currentFile
        self.logMsg("PLAYBACK_RESUMED: %s" % utils.tryDecode(currentFile), 2)

        if self.played_info.get(currentFile):
            self.played_info[currentFile]['paused'] = False
            self.reportPlayback()
Exemple #14
0
    def onPlayBackPaused(self):

        currentFile = self.currentFile
        self.logMsg("PLAYBACK_PAUSED: %s" % utils.tryDecode(currentFile), 2)

        if self.played_info.get(currentFile):
            self.played_info[currentFile]['paused'] = True
            self.reportPlayback()
Exemple #15
0
    def onPlayBackPaused(self):

        currentFile = self.currentFile
        self.logMsg("PLAYBACK_PAUSED: %s" % utils.tryDecode(currentFile), 2)

        if self.played_info.get(currentFile):
            self.played_info[currentFile]['paused'] = True
            self.reportPlayback()
Exemple #16
0
 def getDeviceName(self):
     if settings('deviceNameOpt') == "false":
         # Use Kodi's deviceName
         deviceName = tryDecode(xbmc.getInfoLabel('System.FriendlyName'))
     else:
         deviceName = settings('deviceName')
         deviceName = deviceName.replace("\"", "_")
         deviceName = deviceName.replace("/", "_")
     return deviceName
 def getDeviceName(self):
     if settings('deviceNameOpt') == "false":
         # Use Kodi's deviceName
         deviceName = tryDecode(xbmc.getInfoLabel('System.FriendlyName'))
     else:
         deviceName = settings('deviceName')
         deviceName = deviceName.replace("\"", "_")
         deviceName = deviceName.replace("/", "_")
     return deviceName
Exemple #18
0
def updateRatingToFile(rating, file):
    #update the rating from Emby to the file

    f = xbmcvfs.File(file)
    org_size = f.size()
    f.close()

    #create tempfile
    if "/" in file: filepart = file.split("/")[-1]
    else: filepart = file.split("\\")[-1]
    tempfile = "special://temp/" + filepart
    xbmcvfs.copy(file, tempfile)
    tempfile = utils.tryDecode(xbmc.translatePath(tempfile))

    logMsg("setting song rating: %s for filename: %s - using tempfile: %s" %
           (rating, file, tempfile))

    if not tempfile:
        return

    try:
        if tempfile.lower().endswith(".flac"):
            audio = FLAC(tempfile)
            calcrating = int(round((float(rating) / 5) * 100, 0))
            audio["rating"] = str(calcrating)
            audio.save()
        elif tempfile.lower().endswith(".mp3"):
            audio = ID3(tempfile)
            calcrating = int(round((float(rating) / 5) * 255, 0))
            audio.add(
                id3.POPM(email="Windows Media Player 9 Series",
                         rating=calcrating,
                         count=1))
            audio.save()
        else:
            logMsg("Not supported fileformat: %s" % (tempfile))

        #once we have succesfully written the flags we move the temp file to destination, otherwise not proceeding and just delete the temp
        #safety check: we check the file size of the temp file before proceeding with overwite of original file
        f = xbmcvfs.File(tempfile)
        checksum_size = f.size()
        f.close()
        if checksum_size >= org_size:
            xbmcvfs.delete(file)
            xbmcvfs.copy(tempfile, file)
        else:
            logMsg(
                "Checksum mismatch for filename: %s - using tempfile: %s  -  not proceeding with file overwite!"
                % (rating, file, tempfile))

        #always delete the tempfile
        xbmcvfs.delete(tempfile)

    except Exception as e:
        #file in use ?
        logMsg("Exception in updateRatingToFile %s" % e, 0)
Exemple #19
0
    def onPlayBackSeek(self, time, seekOffset):
        # Make position when seeking a bit more accurate
        currentFile = self.currentFile
        self.logMsg("PLAYBACK_SEEK: %s" % utils.tryDecode(currentFile), 2)

        if self.played_info.get(currentFile):
            position = self.xbmcplayer.getTime()
            self.played_info[currentFile]['currentPosition'] = position

            self.reportPlayback()
Exemple #20
0
    def fullTextureCacheSync(self):
        """
        This method will sync all Kodi artwork to textures13.db
        and cache them locally. This takes diskspace!
        """
        if not dialog('yesno', "Image Texture Cache", lang(39250)):
            return

        LOG.info("Doing Image Cache Sync")

        # ask to rest all existing or not
        if dialog('yesno', "Image Texture Cache", lang(39251)):
            LOG.info("Resetting all cache data first")
            # Remove all existing textures first
            path = tryDecode(translatePath("special://thumbnails/"))
            if exists_dir(path):
                rmtree(path, ignore_errors=True)
                self.restoreCacheDirectories()

            # remove all existing data from texture DB
            connection = kodiSQL('texture')
            cursor = connection.cursor()
            query = 'SELECT tbl_name FROM sqlite_master WHERE type=?'
            cursor.execute(query, ('table', ))
            rows = cursor.fetchall()
            for row in rows:
                tableName = row[0]
                if tableName != "version":
                    cursor.execute("DELETE FROM %s" % tableName)
            connection.commit()
            connection.close()

        # Cache all entries in video DB
        connection = kodiSQL('video')
        cursor = connection.cursor()
        # dont include actors
        query = "SELECT url FROM art WHERE media_type != ?"
        cursor.execute(query, ('actor', ))
        result = cursor.fetchall()
        total = len(result)
        LOG.info("Image cache sync about to process %s video images" % total)
        connection.close()

        for url in result:
            self.cacheTexture(url[0])
        # Cache all entries in music DB
        connection = kodiSQL('music')
        cursor = connection.cursor()
        cursor.execute("SELECT url FROM art")
        result = cursor.fetchall()
        total = len(result)
        LOG.info("Image cache sync about to process %s music images" % total)
        connection.close()
        for url in result:
            self.cacheTexture(url[0])
Exemple #21
0
    def __init__(self):
        # Initial logging
        LOG.info("======== START %s ========", v.ADDON_NAME)
        LOG.info("Platform: %s", v.PLATFORM)
        LOG.info("KODI Version: %s", v.KODILONGVERSION)
        LOG.info("%s Version: %s", v.ADDON_NAME, v.ADDON_VERSION)
        LOG.info("PKC Direct Paths: %s", settings('useDirectPaths') == "true")
        LOG.info("Number of sync threads: %s", settings('syncThreadNumber'))
        LOG.info("Full sys.argv received: %s", argv)

        # Reset window props for profile switch
        properties = [
            "plex_online", "plex_serverStatus", "plex_onWake",
            "plex_kodiScan",
            "plex_shouldStop", "plex_dbScan",
            "plex_initialScan", "plex_customplayqueue", "plex_playbackProps",
            "pms_token", "plex_token",
            "pms_server", "plex_machineIdentifier", "plex_servername",
            "plex_authenticated", "PlexUserImage", "useDirectPaths",
            "countError", "countUnauthorized",
            "plex_restricteduser", "plex_allows_mediaDeletion",
            "plex_command", "plex_result", "plex_force_transcode_pix"
        ]
        for prop in properties:
            window(prop, clear=True)

        # Clear video nodes properties
        videonodes.VideoNodes().clearProperties()

        # Init some stuff
        state.VERIFY_SSL_CERT = settings('sslverify') == 'true'
        state.SSL_CERT_PATH = settings('sslcert') \
            if settings('sslcert') != 'None' else None
        state.FULL_SYNC_INTERVALL = int(settings('fullSyncInterval')) * 60
        state.SYNC_THREAD_NUMBER = int(settings('syncThreadNumber'))
        state.SYNC_DIALOG = settings('dbSyncIndicator') == 'true'
        state.ENABLE_MUSIC = settings('enableMusic') == 'true'
        state.BACKGROUND_SYNC = settings(
            'enableBackgroundSync') == 'true'
        state.BACKGROUNDSYNC_SAFTYMARGIN = int(
            settings('backgroundsync_saftyMargin'))
        state.REPLACE_SMB_PATH = settings('replaceSMB') == 'true'
        state.REMAP_PATH = settings('remapSMB') == 'true'
        set_replace_paths()
        state.KODI_PLEX_TIME_OFFSET = float(settings('kodiplextimeoffset'))

        window('plex_minDBVersion', value="2.0.0")
        set_webserver()
        self.monitor = Monitor()
        window('plex_kodiProfile',
               value=tryDecode(translatePath("special://profile")))
        window('fetch_pms_item_number',
               value=settings('fetch_pms_item_number'))
        clientinfo.getDeviceId()
Exemple #22
0
    def onPlayBackSeek(self, time, seekOffset):
        # Make position when seeking a bit more accurate
        currentFile = self.currentFile
        self.logMsg("PLAYBACK_SEEK: %s" % utils.tryDecode(currentFile), 2)

        if self.played_info.get(currentFile):
            try:
                position = self.xbmcplayer.getTime()
            except RuntimeError:
                # When Kodi is not playing
                return
            self.played_info[currentFile]['currentPosition'] = position * 1000
            self.reportPlayback()
Exemple #23
0
    def onPlayBackSeek(self, time, seekOffset):
        # Make position when seeking a bit more accurate
        currentFile = self.currentFile
        self.logMsg("PLAYBACK_SEEK: %s" % utils.tryDecode(currentFile), 2)

        if self.played_info.get(currentFile):
            try:
                position = self.xbmcplayer.getTime()
            except RuntimeError:
                # When Kodi is not playing
                return
            self.played_info[currentFile]['currentPosition'] = position * 1000
            self.reportPlayback()
Exemple #24
0
def updateRatingToFile(rating, file):
    #update the rating from Emby to the file
    
    f = xbmcvfs.File(file)
    org_size = f.size()
    f.close()
    
    #create tempfile
    if "/" in file: filepart = file.split("/")[-1]
    else: filepart = file.split("\\")[-1]
    tempfile = "special://temp/"+filepart
    xbmcvfs.copy(file, tempfile)
    tempfile = utils.tryDecode(xbmc.translatePath(tempfile))
    
    logMsg( "setting song rating: %s for filename: %s - using tempfile: %s" %(rating,file,tempfile))
    
    if not tempfile:
        return
    
    try:
        if tempfile.lower().endswith(".flac"):
            audio = FLAC(tempfile)
            calcrating = int(round((float(rating) / 5) * 100, 0))
            audio["rating"] = str(calcrating)
            audio.save()
        elif tempfile.lower().endswith(".mp3"):
            audio = ID3(tempfile)
            calcrating = int(round((float(rating) / 5) * 255, 0))
            audio.add(id3.POPM(email="Windows Media Player 9 Series", rating=calcrating, count=1))
            audio.save()
        else:
            logMsg( "Not supported fileformat: %s" %(tempfile))
            
        #once we have succesfully written the flags we move the temp file to destination, otherwise not proceeding and just delete the temp
        #safety check: we check the file size of the temp file before proceeding with overwite of original file
        f = xbmcvfs.File(tempfile)
        checksum_size = f.size()
        f.close()
        if checksum_size >= org_size:
            xbmcvfs.delete(file)
            xbmcvfs.copy(tempfile,file)
        else:
            logMsg( "Checksum mismatch for filename: %s - using tempfile: %s  -  not proceeding with file overwite!" %(rating,file,tempfile))
        
        #always delete the tempfile
        xbmcvfs.delete(tempfile)
            
    except Exception as e:
        #file in use ?
        logMsg("Exception in updateRatingToFile %s" %e,0)
Exemple #25
0
    def __init__(self):

        logLevel = self.getLogLevel()
        self.monitor = Monitor()

        window('plex_logLevel', value=str(logLevel))
        window('plex_kodiProfile',
               value=tryDecode(translatePath("special://profile")))
        window('plex_context',
               value='true' if settings('enableContext') == "true" else "")
        window('fetch_pms_item_number',
               value=settings('fetch_pms_item_number'))

        # Initial logging
        log.warn("======== START %s ========" % v.ADDON_NAME)
        log.warn("Platform: %s" % v.PLATFORM)
        log.warn("KODI Version: %s" % v.KODILONGVERSION)
        log.warn("%s Version: %s" % (v.ADDON_NAME, v.ADDON_VERSION))
        log.warn("Using plugin paths: %s"
                 % (settings('useDirectPaths') != "true"))
        log.warn("Number of sync threads: %s"
                 % settings('syncThreadNumber'))
        log.warn("Log Level: %s" % logLevel)
        log.warn("Full sys.argv received: %s" % argv)

        # Reset window props for profile switch
        properties = [
            "plex_online", "plex_serverStatus", "plex_onWake",
            "plex_dbCheck", "plex_kodiScan",
            "plex_shouldStop", "plex_dbScan",
            "plex_initialScan", "plex_customplayqueue", "plex_playbackProps",
            "plex_runLibScan", "pms_token", "plex_token",
            "pms_server", "plex_machineIdentifier", "plex_servername",
            "plex_authenticated", "PlexUserImage", "useDirectPaths",
            "kodiplextimeoffset", "countError", "countUnauthorized",
            "plex_restricteduser", "plex_allows_mediaDeletion",
            "plex_command", "plex_result", "plex_force_transcode_pix"
        ]
        for prop in properties:
            window(prop, clear=True)

        # Clear video nodes properties
        videonodes.VideoNodes().clearProperties()

        # Set the minimum database version
        window('plex_minDBVersion', value="1.5.10")
Exemple #26
0
 def deleteCachedArtwork(self, url):
     # Only necessary to remove and apply a new backdrop or poster
     connection = kodiSQL('texture')
     cursor = connection.cursor()
     try:
         cursor.execute("SELECT cachedurl FROM texture WHERE url = ?",
                        (url,))
         cachedurl = cursor.fetchone()[0]
     except TypeError:
         log.info("Could not find cached url.")
     else:
         # Delete thumbnail as well as the entry
         path = translatePath("special://thumbnails/%s" % cachedurl)
         log.debug("Deleting cached thumbnail: %s" % path)
         if exists(path):
             rmtree(tryDecode(path), ignore_errors=True)
         cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
         connection.commit()
     finally:
         connection.close()
Exemple #27
0
 def deleteCachedArtwork(self, url):
     # Only necessary to remove and apply a new backdrop or poster
     connection = kodiSQL('texture')
     cursor = connection.cursor()
     try:
         cursor.execute("SELECT cachedurl FROM texture WHERE url = ?",
                        (url,))
         cachedurl = cursor.fetchone()[0]
     except TypeError:
         log.info("Could not find cached url.")
     else:
         # Delete thumbnail as well as the entry
         thumbnails = tryDecode(
             translatePath("special://thumbnails/%s" % cachedurl))
         log.debug("Deleting cached thumbnail: %s" % thumbnails)
         try:
             delete(thumbnails)
         except Exception as e:
             log.error('Could not delete cached artwork %s. Error: %s'
                       % (thumbnails, e))
         cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
         connection.commit()
     finally:
         connection.close()
Exemple #28
0
 def deleteCachedArtwork(self, url):
     # Only necessary to remove and apply a new backdrop or poster
     connection = kodiSQL('texture')
     cursor = connection.cursor()
     try:
         cursor.execute("SELECT cachedurl FROM texture WHERE url = ?",
                        (url, ))
         cachedurl = cursor.fetchone()[0]
     except TypeError:
         log.info("Could not find cached url.")
     else:
         # Delete thumbnail as well as the entry
         thumbnails = tryDecode(
             xbmc.translatePath("special://thumbnails/%s" % cachedurl))
         log.debug("Deleting cached thumbnail: %s" % thumbnails)
         try:
             xbmcvfs.delete(thumbnails)
         except Exception as e:
             log.error('Could not delete cached artwork %s. Error: %s' %
                       (thumbnails, e))
         cursor.execute("DELETE FROM texture WHERE url = ?", (url, ))
         connection.commit()
     finally:
         connection.close()
    def PlayBackStart(self, data):
        """
        Called whenever a playback is started
        """
        log = self.logMsg
        window = utils.window

        # Get currently playing file - can take a while. Will be utf-8!
        try:
            currentFile = self.xbmcplayer.getPlayingFile()
        except:
            currentFile = None
            count = 0
            while currentFile is None:
                xbmc.sleep(100)
                try:
                    currentFile = self.xbmcplayer.getPlayingFile()
                except:
                    pass
                if count == 50:
                    log("No current File - Cancelling OnPlayBackStart...", -1)
                    return
                else:
                    count += 1
        log("Currently playing file is: %s" % utils.tryDecode(currentFile), 1)

        # Try to get a Kodi ID
        item = data.get('item')
        try:
            type = item['type']
        except:
            log("Item is invalid for PMS playstate update.", 0)
            return
        try:
            kodiid = item['id']
        except (KeyError, TypeError):
            itemType = window("emby_%s.type" % currentFile)
            log("No kodi id passed. Playing itemtype is: %s" % itemType, 1)
            if itemType in ('movie', 'episode'):
                # Window was setup by PKC and is NOT a trailer ('clip')
                with kodidb.GetKodiDB('video') as kodi_db:
                    kodiid = kodi_db.getIdFromTitle(data.get('item'))
                    if kodiid is None:
                        log(
                            "Skip playstate update. No unique Kodi title found"
                            " for %s" % data.get('item'), 0)
                        return
            else:
                log("Item is invalid for PMS playstate update.", 0)
                return

        # Get Plex' item id
        with embydb.GetEmbyDB() as emby_db:
            emby_dbitem = emby_db.getItem_byKodiId(kodiid, type)
        try:
            plexid = emby_dbitem[0]
        except TypeError:
            log("No Plex id returned for kodiid %s" % kodiid, 0)
            return
        log("Found Plex id %s for Kodi id %s" % (plexid, kodiid), 1)

        # Set some stuff if Kodi initiated playback
        if ((utils.settings('useDirectPaths') == "1" and not type == "song") or
            (type == "song" and utils.settings('enableMusic') == "true")):
            if self.StartDirectPath(plexid, type, currentFile) is False:
                log('Could not initiate monitoring; aborting', -1)
                return

        # Save currentFile for cleanup later and to be able to access refs
        window('plex_lastPlayedFiled', value=utils.tryDecode(currentFile))
        window('Plex_currently_playing_itemid', value=plexid)
        window("emby_%s.itemid" % currentFile, value=plexid)
        log('Finish playback startup', 1)
Exemple #30
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
Exemple #31
0
    def authenticate(self):
        log.info('Authenticating user')
        dialog = xbmcgui.Dialog()

        # Give attempts at entering password / selecting user
        if self.retry >= 2:
            log.error("Too many retries to login.")
            window('plex_serverStatus', value="Stop")
            dialog.ok(lang(33001), lang(39023))
            xbmc.executebuiltin(
                'Addon.OpenSettings(plugin.video.plexkodiconnect)')
            return False

        # Get /profile/addon_data
        addondir = tryDecode(
            xbmc.translatePath(self.addon.getAddonInfo('profile')))
        hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)

        # If there's no settings.xml
        if not hasSettings:
            log.error("Error, no settings.xml found.")
            self.auth = False
            return False
        server = self.getServer()
        # If there is no server we can connect to
        if not server:
            log.info("Missing server information.")
            self.auth = False
            return False

        # If there is a username in the settings, try authenticating
        username = settings('username')
        userId = settings('userid')
        usertoken = settings('accessToken')
        enforceLogin = settings('enforceUserLogin')
        # Found a user in the settings, try to authenticate
        if username and enforceLogin == 'false':
            log.info('Trying to authenticate with old settings')
            if self.loadCurrUser(username,
                                 userId,
                                 usertoken,
                                 authenticated=False):
                # SUCCESS: loaded a user from the settings
                return True
            else:
                # Failed to use the settings - delete them!
                log.info("Failed to use settings credentials. Deleting them")
                settings('username', value='')
                settings('userid', value='')
                settings('accessToken', value='')

        plx = PlexAPI.PlexAPI()

        # Could not use settings - try to get Plex user list from plex.tv
        plextoken = settings('plexToken')
        if plextoken:
            log.info("Trying to connect to plex.tv to get a user list")
            userInfo = plx.ChoosePlexHomeUser(plextoken)
            if userInfo is False:
                # FAILURE: Something went wrong, try again
                self.auth = True
                self.retry += 1
                return False
            username = userInfo['username']
            userId = userInfo['userid']
            usertoken = userInfo['token']
        else:
            log.info("Trying to authenticate without a token")
            username = ''
            userId = ''
            usertoken = ''

        if self.loadCurrUser(username, userId, usertoken, authenticated=False):
            # SUCCESS: loaded a user from the settings
            return True
        else:
            # FAILUR: Something went wrong, try again
            self.auth = True
            self.retry += 1
            return False
    def play(self, itemid, dbid=None):

        window = utils.window
        settings = utils.settings

        item = self.item
        # Hack to get only existing entry in PMS response for THIS instance of
        # playbackutils :-)
        self.API = PlexAPI.API(item[0])
        API = self.API
        listitem = xbmcgui.ListItem()
        playutils = putils.PlayUtils(item[0])

        self.logMsg("Play called.", 1)
        playurl = playutils.getPlayUrl()
        if not playurl:
            return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)

        if dbid in (None, '999999999', 'plexnode'):
            # Item is not in Kodi database, is a trailer or plex redirect
            # e.g. plex.tv watch later
            API.CreateListItemFromPlexItem(listitem)
            self.setArtwork(listitem)
            if dbid == 'plexnode':
                # Need to get yet another xml to get final url
                window('emby_%s.playmethod' % playurl, clear=True)
                xml = downloadutils.DownloadUtils().downloadUrl(
                    '{server}%s' % item[0][0][0].attrib.get('key'))
                if xml in (None, 401):
                    self.logMsg('Could not download %s'
                        % item[0][0][0].attrib.get('key'), -1)
                    return xbmcplugin.setResolvedUrl(
                        int(sys.argv[1]), False, listitem)
                playurl = utils.tryEncode(xml[0].attrib.get('key'))
                window('emby_%s.playmethod' % playurl, value='DirectStream')

            playmethod = window('emby_%s.playmethod' % playurl)
            if playmethod == "Transcode":
                window('emby_%s.playmethod' % playurl, clear=True)
                playurl = utils.tryEncode(playutils.audioSubsPref(
                    listitem, utils.tryDecode(playurl)))
                window('emby_%s.playmethod' % playurl, "Transcode")
            listitem.setPath(playurl)
            self.setProperties(playurl, listitem)
            return xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)

        ############### ORGANIZE CURRENT PLAYLIST ################
        
        homeScreen = xbmc.getCondVisibility('Window.IsActive(home)')
        playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
        startPos = max(playlist.getposition(), 0) # Can return -1
        sizePlaylist = playlist.size()
        self.currentPosition = startPos

        propertiesPlayback = window('plex_playbackProps') == "true"
        introsPlaylist = False
        dummyPlaylist = False

        self.logMsg("Playlist start position: %s" % startPos, 2)
        self.logMsg("Playlist plugin position: %s" % self.currentPosition, 2)
        self.logMsg("Playlist size: %s" % sizePlaylist, 2)

        ############### RESUME POINT ################
        
        seektime, runtime = API.getRuntime()

        # 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")
            self.logMsg("Setting up properties in playlist.", 1)

            if (not homeScreen and not seektime and
                    window('plex_customplaylist') != "true"):
                self.logMsg("Adding dummy file to playlist.", 2)
                dummyPlaylist = True
                playlist.add(playurl, listitem, index=startPos)
                # Remove the original item from playlist 
                self.pl.removefromPlaylist(startPos+1)
                # Readd the original item to playlist - via jsonrpc so we have full metadata
                self.pl.insertintoPlaylist(
                    self.currentPosition+1,
                    dbid,
                    PF.GetKodiTypeFromPlex(API.getType()))
                self.currentPosition += 1
            
            ############### -- CHECK FOR INTROS ################

            if (settings('enableCinema') == "true" and not seektime):
                # if we have any play them when the movie/show is not being resumed
                xml = PF.GetPlexPlaylist(
                    itemid,
                    item.attrib.get('librarySectionUUID'),
                    mediatype=API.getType())
                introsPlaylist = self.AddTrailers(xml)

            ############### -- 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
                self.logMsg("Adding main item to playlist.", 1)
                self.pl.addtoPlaylist(
                    dbid,
                    PF.GetKodiTypeFromPlex(API.getType()))

            # Ensure that additional parts are played after the main item
            self.currentPosition += 1

            ############### -- CHECK FOR ADDITIONAL PARTS ################
            
            if len(item[0][0]) > 1:
                # Only add to the playlist after intros have played
                for counter, part in enumerate(item[0][0]):
                    # Never add first part
                    if counter == 0:
                        continue
                    # Set listitem and properties for each additional parts
                    API.setPartNumber(counter)
                    additionalListItem = xbmcgui.ListItem()
                    additionalPlayurl = playutils.getPlayUrl(
                        partNumber=counter)
                    self.logMsg("Adding additional part: %s" % counter, 1)

                    self.setProperties(additionalPlayurl, additionalListItem)
                    self.setArtwork(additionalListItem)
                    # NEW to Plex
                    API.CreateListItemFromPlexItem(additionalListItem)

                    playlist.add(additionalPlayurl, additionalListItem, index=self.currentPosition)
                    self.pl.verifyPlaylist()
                    self.currentPosition += 1
                API.setPartNumber = 0

            if dummyPlaylist:
                # Added a dummy file to the playlist,
                # because the first item is going to fail automatically.
                self.logMsg("Processed as a playlist. First item is skipped.", 1)
                return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
                

        # We just skipped adding properties. Reset flag for next time.
        elif propertiesPlayback:
            self.logMsg("Resetting properties playback flag.", 2)
            window('plex_playbackProps', clear=True)

        #self.pl.verifyPlaylist()
        ########## SETUP MAIN ITEM ##########

        # For transcoding only, ask for audio/subs pref
        if window('emby_%s.playmethod' % playurl) == "Transcode":
            window('emby_%s.playmethod' % playurl, clear=True)
            playurl = utils.tryEncode(playutils.audioSubsPref(
                listitem, utils.tryDecode(playurl)))
            window('emby_%s.playmethod' % playurl, value="Transcode")

        listitem.setPath(playurl)
        self.setProperties(playurl, listitem)

        ############### PLAYBACK ################

        if homeScreen and seektime and window('plex_customplaylist') != "true":
            self.logMsg("Play as a widget item.", 1)
            API.CreateListItemFromPlexItem(listitem)
            xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)

        elif ((introsPlaylist and window('plex_customplaylist') == "true") or
                (homeScreen and not sizePlaylist)):
            # Playlist was created just now, play it.
            self.logMsg("Play playlist.", 1)
            xbmc.Player().play(playlist, startpos=startPos)

        else:
            self.logMsg("Play as a regular item.", 1)
            xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
Exemple #33
0
    def PlayBackStart(self, data):
        """
        Called whenever a playback is started
        """
        # Get currently playing file - can take a while. Will be utf-8!
        try:
            currentFile = self.xbmcplayer.getPlayingFile()
        except:
            currentFile = None
            count = 0
            while currentFile is None:
                sleep(100)
                try:
                    currentFile = self.xbmcplayer.getPlayingFile()
                except:
                    pass
                if count == 50:
                    log.info("No current File, cancel OnPlayBackStart...")
                    return
                else:
                    count += 1
        # Just to be on the safe side
        currentFile = tryDecode(currentFile)
        log.debug("Currently playing file is: %s" % currentFile)

        # Get the type of media we're playing
        try:
            typus = data['item']['type']
        except (TypeError, KeyError):
            log.info("Item is invalid for PMS playstate update.")
            return
        log.debug("Playing itemtype is (or appears to be): %s" % typus)

        # Try to get a Kodi ID
        # If PKC was used - native paths, not direct paths
        plex_id = window('plex_%s.itemid' % tryEncode(currentFile))
        # Get rid of the '' if the window property was not set
        plex_id = None if not plex_id else plex_id
        kodiid = None
        if plex_id is None:
            log.debug('Did not get Plex id from window properties')
            try:
                kodiid = data['item']['id']
            except (TypeError, KeyError):
                log.debug('Did not get a Kodi id from Kodi, darn')
        # For direct paths, if we're not streaming something
        # When using Widgets, Kodi doesn't tell us shit so we need this hack
        if (kodiid is None and plex_id is None and typus != 'song'
                and not currentFile.startswith('http')):
            (kodiid, typus) = get_kodiid_from_filename(currentFile)
            if kodiid is None:
                return

        if plex_id is None:
            # Get Plex' item id
            with plexdb.Get_Plex_DB() as plexcursor:
                plex_dbitem = plexcursor.getItem_byKodiId(kodiid, typus)
            try:
                plex_id = plex_dbitem[0]
            except TypeError:
                log.info("No Plex id returned for kodiid %s. Aborting playback"
                         " report" % kodiid)
                return
        log.debug("Found Plex id %s for Kodi id %s for type %s" %
                  (plex_id, kodiid, typus))

        # Switch subtitle tracks if applicable
        subtitle = window('plex_%s.subtitle' % tryEncode(currentFile))
        if window(tryEncode('plex_%s.playmethod' % currentFile)) \
                == 'Transcode' and subtitle:
            if window('plex_%s.subtitle' % currentFile) == 'None':
                self.xbmcplayer.showSubtitles(False)
            else:
                self.xbmcplayer.setSubtitleStream(int(subtitle))

        # Set some stuff if Kodi initiated playback
        if ((settings('useDirectPaths') == "1" and not typus == "song")
                or (typus == "song" and settings('enableMusic') == "true")):
            if self.StartDirectPath(plex_id, typus,
                                    tryEncode(currentFile)) is False:
                log.error('Could not initiate monitoring; aborting')
                return

        # Save currentFile for cleanup later and to be able to access refs
        window('plex_lastPlayedFiled', value=currentFile)
        window('plex_currently_playing_itemid', value=plex_id)
        window("plex_%s.itemid" % tryEncode(currentFile), value=plex_id)
        log.info('Finish playback startup')
Exemple #34
0
    def fullTextureCacheSync(self):
        """
        This method will sync all Kodi artwork to textures13.db
        and cache them locally. This takes diskspace!
        """
        if not xbmcgui.Dialog().yesno("Image Texture Cache", lang(39250)):
            return

        log.info("Doing Image Cache Sync")

        # ask to rest all existing or not
        if xbmcgui.Dialog().yesno("Image Texture Cache", lang(39251), ""):
            log.info("Resetting all cache data first")
            # Remove all existing textures first
            path = tryDecode(xbmc.translatePath("special://thumbnails/"))
            if IfExists(path):
                allDirs, allFiles = xbmcvfs.listdir(path)
                for dir in allDirs:
                    allDirs, allFiles = xbmcvfs.listdir(path + dir)
                    for file in allFiles:
                        if os.path.supports_unicode_filenames:
                            xbmcvfs.delete(
                                os.path.join(path + tryDecode(dir),
                                             tryDecode(file)))
                        else:
                            xbmcvfs.delete(
                                os.path.join(tryEncode(path) + dir, file))

            # remove all existing data from texture DB
            connection = kodiSQL('texture')
            cursor = connection.cursor()
            cursor.execute(
                'SELECT tbl_name FROM sqlite_master WHERE type="table"')
            rows = cursor.fetchall()
            for row in rows:
                tableName = row[0]
                if tableName != "version":
                    cursor.execute("DELETE FROM " + tableName)
            connection.commit()
            connection.close()

        # Cache all entries in video DB
        connection = kodiSQL('video')
        cursor = connection.cursor()
        # dont include actors
        cursor.execute("SELECT url FROM art WHERE media_type != 'actor'")
        result = cursor.fetchall()
        total = len(result)
        log.info("Image cache sync about to process %s video images" % total)
        connection.close()

        for url in result:
            self.cacheTexture(url[0])
        # Cache all entries in music DB
        connection = kodiSQL('music')
        cursor = connection.cursor()
        cursor.execute("SELECT url FROM art")
        result = cursor.fetchall()
        total = len(result)
        log.info("Image cache sync about to process %s music images" % total)
        connection.close()
        for url in result:
            self.cacheTexture(url[0])
Exemple #35
0
    def onPlayBackStarted(self):
        """
        Will be called when xbmc starts playing a file.
        Window values need to have been set in Kodimonitor.py
        """
        self.stopAll()

        # Get current file (in utf-8!)
        try:
            currentFile = tryDecode(self.getPlayingFile())
            xbmc.sleep(300)
        except:
            currentFile = ""
            count = 0
            while not currentFile:
                xbmc.sleep(100)
                try:
                    currentFile = tryDecode(self.getPlayingFile())
                except:
                    pass
                if count == 20:
                    break
                else:
                    count += 1
        if not currentFile:
            log.warn('Error getting currently playing file; abort reporting')
            return

        # Save currentFile for cleanup later and for references
        self.currentFile = currentFile
        window('plex_lastPlayedFiled', value=currentFile)
        # We may need to wait for info to be set in kodi monitor
        itemId = window("plex_%s.itemid" % tryEncode(currentFile))
        count = 0
        while not itemId:
            xbmc.sleep(200)
            itemId = window("plex_%s.itemid" % tryEncode(currentFile))
            if count == 5:
                log.warn("Could not find itemId, cancelling playback report!")
                return
            count += 1

        log.info("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId))

        plexitem = "plex_%s" % tryEncode(currentFile)
        runtime = window("%s.runtime" % plexitem)
        refresh_id = window("%s.refreshid" % plexitem)
        playMethod = window("%s.playmethod" % plexitem)
        itemType = window("%s.type" % plexitem)
        try:
            playcount = int(window("%s.playcount" % plexitem))
        except ValueError:
            playcount = 0
        window('plex_skipWatched%s' % itemId, value="true")

        log.debug("Playing itemtype is: %s" % itemType)

        customseek = window('plex_customplaylist.seektime')
        if customseek:
            # Start at, when using custom playlist (play to Kodi from
            # webclient)
            log.info("Seeking to: %s" % customseek)
            try:
                self.seekTime(int(customseek))
            except:
                log.error('Could not seek!')
            window('plex_customplaylist.seektime', clear=True)

        try:
            seekTime = self.getTime()
        except RuntimeError:
            log.error('Could not get current seektime from xbmc player')
            seekTime = 0

        # Get playback volume
        volume_query = {
            "jsonrpc": "2.0",
            "id": 1,
            "method": "Application.GetProperties",
            "params": {
                "properties": ["volume", "muted"]
            }
        }
        result = xbmc.executeJSONRPC(json.dumps(volume_query))
        result = json.loads(result)
        result = result.get('result')

        volume = result.get('volume')
        muted = result.get('muted')

        # Postdata structure to send to plex server
        url = "{server}/:/timeline?"
        postdata = {
            'QueueableMediaTypes': "Video",
            'CanSeek': True,
            'ItemId': itemId,
            'MediaSourceId': itemId,
            'PlayMethod': playMethod,
            'VolumeLevel': volume,
            'PositionTicks': int(seekTime * 10000000),
            'IsMuted': muted
        }

        # Get the current audio track and subtitles
        if playMethod == "Transcode":
            # property set in PlayUtils.py
            postdata['AudioStreamIndex'] = window("%sAudioStreamIndex" %
                                                  tryEncode(currentFile))
            postdata['SubtitleStreamIndex'] = window("%sSubtitleStreamIndex" %
                                                     tryEncode(currentFile))
        else:
            # Get the current kodi audio and subtitles and convert to plex equivalent
            tracks_query = {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "Player.GetProperties",
                "params": {
                    "playerid":
                    1,
                    "properties": [
                        "currentsubtitle", "currentaudiostream",
                        "subtitleenabled"
                    ]
                }
            }
            result = xbmc.executeJSONRPC(json.dumps(tracks_query))
            result = json.loads(result)
            result = result.get('result')

            try:  # Audio tracks
                indexAudio = result['currentaudiostream']['index']
            except (KeyError, TypeError):
                indexAudio = 0

            try:  # Subtitles tracks
                indexSubs = result['currentsubtitle']['index']
            except (KeyError, TypeError):
                indexSubs = 0

            try:  # If subtitles are enabled
                subsEnabled = result['subtitleenabled']
            except (KeyError, TypeError):
                subsEnabled = ""

            # Postdata for the audio
            postdata['AudioStreamIndex'] = indexAudio + 1

            # Postdata for the subtitles
            if subsEnabled and len(
                    xbmc.Player().getAvailableSubtitleStreams()) > 0:

                # Number of audiotracks to help get plex Index
                audioTracks = len(xbmc.Player().getAvailableAudioStreams())
                mapping = window("%s.indexMapping" % plexitem)

                if mapping:  # Set in playbackutils.py

                    log.debug("Mapping for external subtitles index: %s" %
                              mapping)
                    externalIndex = json.loads(mapping)

                    if externalIndex.get(str(indexSubs)):
                        # If the current subtitle is in the mapping
                        postdata['SubtitleStreamIndex'] = externalIndex[str(
                            indexSubs)]
                    else:
                        # Internal subtitle currently selected
                        subindex = indexSubs - len(
                            externalIndex) + audioTracks + 1
                        postdata['SubtitleStreamIndex'] = subindex

                else:  # Direct paths enabled scenario or no external subtitles set
                    postdata[
                        'SubtitleStreamIndex'] = indexSubs + audioTracks + 1
            else:
                postdata['SubtitleStreamIndex'] = ""

        # Post playback to server
        # log("Sending POST play started: %s." % postdata, 2)
        # self.doUtils(url, postBody=postdata, type="POST")

        # Ensure we do have a runtime
        try:
            runtime = int(runtime)
        except ValueError:
            try:
                runtime = self.getTotalTime()
                log.error("Runtime is missing, Kodi runtime: %s" % runtime)
            except:
                log.error('Could not get kodi runtime, setting to zero')
                runtime = 0

        with plexdb.Get_Plex_DB() as plex_db:
            plex_dbitem = plex_db.getItem_byId(itemId)
        try:
            fileid = plex_dbitem[1]
        except TypeError:
            log.info("Could not find fileid in plex db.")
            fileid = None
        # Save data map for updates and position calls
        data = {
            'runtime': runtime,
            'item_id': itemId,
            'refresh_id': refresh_id,
            'currentfile': currentFile,
            'AudioStreamIndex': postdata['AudioStreamIndex'],
            'SubtitleStreamIndex': postdata['SubtitleStreamIndex'],
            'playmethod': playMethod,
            'Type': itemType,
            'currentPosition': int(seekTime),
            'fileid': fileid,
            'itemType': itemType,
            'playcount': playcount
        }

        self.played_info[currentFile] = data
        log.info("ADDING_FILE: %s" % data)

        # log some playback stats
        '''if(itemType != None):
    def PlayBackStart(self, data):
        """
        Called whenever a playback is started
        """
        log = self.logMsg
        window = utils.window
        # Get currently playing file - can take a while. Will be utf-8!
        try:
            currentFile = self.xbmcplayer.getPlayingFile()
        except:
            currentFile = None
            count = 0
            while currentFile is None:
                xbmc.sleep(100)
                try:
                    currentFile = self.xbmcplayer.getPlayingFile()
                except:
                    pass
                if count == 50:
                    log("No current File - Cancelling OnPlayBackStart...", -1)
                    return
                else:
                    count += 1
        # Just to be on the safe side
        currentFile = utils.tryDecode(currentFile)
        log("Currently playing file is: %s" % currentFile, 1)

        # Get the type of media we're playing
        try:
            typus = data['item']['type']
        except (TypeError, KeyError):
            log("Item is invalid for PMS playstate update.", 0)
            return
        log("Playing itemtype is (or appears to be): %s" % typus, 1)

        # Try to get a Kodi ID
        # If PKC was used - native paths, not direct paths
        plexid = utils.window('emby_%s.itemid'
                              % utils.tryEncode(currentFile))
        # Get rid of the '' if the window property was not set
        plexid = None if not plexid else plexid
        kodiid = None
        if plexid is None:
            log('Did not get Plex id from window properties', 1)
            try:
                kodiid = data['item']['id']
            except (TypeError, KeyError):
                log('Did not get a Kodi id from Kodi, darn', 1)
        # For direct paths, if we're not streaming something
        # When using Widgets, Kodi doesn't tell us shit so we need this hack
        if (kodiid is None and plexid is None and typus != 'song'
                and not currentFile.startswith('http')):
            try:
                filename = currentFile.rsplit('/', 1)[1]
                path = currentFile.rsplit('/', 1)[0] + '/'
            except IndexError:
                filename = currentFile.rsplit('\\', 1)[1]
                path = currentFile.rsplit('\\', 1)[0] + '\\'
            log('Trying to figure out playing item from filename: %s and '
                'path: %s' % (filename, path), 1)
            with kodidb.GetKodiDB('video') as kodi_db:
                try:
                    kodiid, typus = kodi_db.getIdFromFilename(filename, path)
                except TypeError:
                    log('Aborting playback report', 1)
                    return

        if plexid is None:
            # Get Plex' item id
            with embydb.GetEmbyDB() as emby_db:
                emby_dbitem = emby_db.getItem_byKodiId(kodiid, typus)
            try:
                plexid = emby_dbitem[0]
            except TypeError:
                log("No Plex id returned for kodiid %s" % kodiid, 1)
                log('Aborting playback report', 1)
                return
        log("Found Plex id %s for Kodi id %s for type %s"
            % (plexid, kodiid, typus), 1)

        # Set some stuff if Kodi initiated playback
        if ((utils.settings('useDirectPaths') == "1" and not typus == "song")
                or
                (typus == "song" and utils.settings('enableMusic') == "true")):
            if self.StartDirectPath(plexid,
                                    typus,
                                    utils.tryEncode(currentFile)) is False:
                log('Could not initiate monitoring; aborting', -1)
                return

        # Save currentFile for cleanup later and to be able to access refs
        window('plex_lastPlayedFiled', value=currentFile)
        window('Plex_currently_playing_itemid', value=plexid)
        window("emby_%s.itemid" % utils.tryEncode(currentFile), value=plexid)
        log('Finish playback startup', 1)
Exemple #37
0
    def single_urlencode(self, text):

        text = urllib.urlencode({'blahblahblah': utils.tryEncode(text)}) #urlencode needs a utf- string
        text = text[13:]

        return utils.tryDecode(text) #return the result again as unicode
Exemple #38
0
import embydb_functions as embydb
import kodidb_functions as kodidb
import musicutils as musicutils

import PlexFunctions as PF
import PlexAPI


def logMsg(msg, lvl=1):
    utils.logMsg("%s %s" % ("PlexKodiConnect", "Contextmenu"), msg, lvl)


#Kodi contextmenu item to configure the emby settings
#for now used to set ratings but can later be used to sync individual items etc.
if __name__ == '__main__':
    itemid = utils.tryDecode(xbmc.getInfoLabel("ListItem.DBID"))
    itemtype = utils.tryDecode(xbmc.getInfoLabel("ListItem.DBTYPE"))

    emby = embyserver.Read_EmbyServer()

    plexid = ""
    if not itemtype and xbmc.getCondVisibility("Container.Content(albums)"):
        itemtype = "album"
    if not itemtype and xbmc.getCondVisibility("Container.Content(artists)"):
        itemtype = "artist"
    if not itemtype and xbmc.getCondVisibility("Container.Content(songs)"):
        itemtype = "song"
    if not itemtype and xbmc.getCondVisibility("Container.Content(pictures)"):
        itemtype = "picture"

    if (not itemid or itemid
    def play(self, itemid, dbid=None):

        item = self.item
        # Hack to get only existing entry in PMS response for THIS instance of
        # playbackutils :-)
        self.API = PlexAPI.API(item[0])
        API = self.API
        listitem = xbmcgui.ListItem()
        playutils = putils.PlayUtils(item[0])

        log.info("Play called.")
        playurl = playutils.getPlayUrl()
        if not playurl:
            return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)

        if dbid in (None, '999999999', 'plexnode'):
            # Item is not in Kodi database, is a trailer or plex redirect
            # e.g. plex.tv watch later
            API.CreateListItemFromPlexItem(listitem)
            self.setArtwork(listitem)
            if dbid == 'plexnode':
                # Need to get yet another xml to get final url
                window('emby_%s.playmethod' % playurl, clear=True)
                xml = downloadutils.DownloadUtils().downloadUrl(
                    '{server}%s' % item[0][0][0].attrib.get('key'))
                if xml in (None, 401):
                    log.error('Could not download %s' %
                              item[0][0][0].attrib.get('key'))
                    return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False,
                                                     listitem)
                playurl = tryEncode(xml[0].attrib.get('key'))
                window('emby_%s.playmethod' % playurl, value='DirectStream')

            playmethod = window('emby_%s.playmethod' % playurl)
            if playmethod == "Transcode":
                window('emby_%s.playmethod' % playurl, clear=True)
                playurl = tryEncode(
                    playutils.audioSubsPref(listitem, tryDecode(playurl)))
                window('emby_%s.playmethod' % playurl, "Transcode")
            listitem.setPath(playurl)
            self.setProperties(playurl, listitem)
            return xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)

        ############### ORGANIZE CURRENT PLAYLIST ################
        homeScreen = xbmc.getCondVisibility('Window.IsActive(home)')
        kodiPl = self.pl.playlist
        # Can return -1
        startPos = max(kodiPl.getposition(), 0)
        sizePlaylist = kodiPl.size()
        self.currentPosition = startPos

        propertiesPlayback = window('plex_playbackProps') == "true"
        introsPlaylist = False
        dummyPlaylist = False

        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()

        # 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.")

            if (not homeScreen and not seektime
                    and window('plex_customplaylist') != "true"):
                log.debug("Adding dummy file to playlist.")
                dummyPlaylist = True
                kodiPl.add(playurl, listitem, index=startPos)
                # Remove the original item from playlist
                self.pl.removefromPlaylist(startPos + 1)
                # Readd the original item to playlist - via jsonrpc so we have full metadata
                self.pl.insertintoPlaylist(
                    self.currentPosition + 1, dbid,
                    PF.GetKodiTypeFromPlex(API.getType()))
                self.currentPosition += 1

            ############### -- CHECK FOR INTROS ################
            if (settings('enableCinema') == "true" and not seektime):
                # if we have any play them when the movie/show is not being resumed
                xml = PF.GetPlexPlaylist(itemid,
                                         item.attrib.get('librarySectionUUID'),
                                         mediatype=API.getType())
                introsPlaylist = self.AddTrailers(xml)

            ############### -- 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.")
                self.pl.addtoPlaylist(dbid,
                                      PF.GetKodiTypeFromPlex(API.getType()))

            # Ensure that additional parts are played after the main item
            self.currentPosition += 1

            ############### -- CHECK FOR ADDITIONAL PARTS ################
            if len(item[0][0]) > 1:
                # Only add to the playlist after intros have played
                for counter, part in enumerate(item[0][0]):
                    # Never add first part
                    if counter == 0:
                        continue
                    # Set listitem and properties for each additional parts
                    API.setPartNumber(counter)
                    additionalListItem = xbmcgui.ListItem()
                    additionalPlayurl = playutils.getPlayUrl(
                        partNumber=counter)
                    log.debug("Adding additional part: %s" % counter)

                    self.setProperties(additionalPlayurl, additionalListItem)
                    self.setArtwork(additionalListItem)
                    # NEW to Plex
                    API.CreateListItemFromPlexItem(additionalListItem)

                    kodiPl.add(additionalPlayurl,
                               additionalListItem,
                               index=self.currentPosition)
                    self.pl.verifyPlaylist()
                    self.currentPosition += 1
                API.setPartNumber = 0

            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.")
                return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False,
                                                 listitem)

        # We just skipped adding properties. Reset flag for next time.
        elif propertiesPlayback:
            log.debug("Resetting properties playback flag.")
            window('plex_playbackProps', clear=True)

        #self.pl.verifyPlaylist()
        ########## SETUP MAIN ITEM ##########
        # For transcoding only, ask for audio/subs pref
        if window('emby_%s.playmethod' % playurl) == "Transcode":
            window('emby_%s.playmethod' % playurl, clear=True)
            playurl = tryEncode(
                playutils.audioSubsPref(listitem, tryDecode(playurl)))
            window('emby_%s.playmethod' % playurl, value="Transcode")

        listitem.setPath(playurl)
        self.setProperties(playurl, listitem)

        ############### PLAYBACK ################
        if homeScreen and seektime and window('plex_customplaylist') != "true":
            log.info("Play as a widget item.")
            API.CreateListItemFromPlexItem(listitem)
            xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)

        elif ((introsPlaylist and window('plex_customplaylist') == "true")
              or (homeScreen and not sizePlaylist)):
            # Playlist was created just now, play it.
            log.info("Play playlist.")
            xbmcplugin.endOfDirectory(int(sys.argv[1]), True, False, False)
            xbmc.Player().play(kodiPl, startpos=startPos)

        else:
            log.info("Play as a regular item.")
            xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
Exemple #40
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 = "100"
        mediatypes = {
            'movie': 'movies',
            'show': 'tvshows',
            'photo': 'photos',
            'homevideo': 'homevideos',
            'musicvideos': 'musicvideos'
        }
        mediatype = mediatypes[mediatype]

        window = utils.window

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

        path = utils.tryDecode(
            xbmc.translatePath("special://profile/library/video/"))
        nodepath = utils.tryDecode(
            xbmc.translatePath("special://profile/library/video/Plex-%s/" %
                               dirname))

        # Verify the video directory
        # KODI BUG
        # Kodi caches the result of exists for directories
        #  so try creating a file
        if utils.IfExists(path) is False:
            shutil.copytree(
                src=utils.tryDecode(
                    xbmc.translatePath("special://xbmc/system/library/video")),
                dst=utils.tryDecode(
                    xbmc.translatePath("special://profile/library/video")))

        # Create the node directory
        if mediatype != "photos":
            if utils.IfExists(nodepath) is False:
                # folder does not exist yet
                self.logMsg('Creating folder %s' % nodepath, 1)
                xbmcvfs.mkdirs(utils.tryEncode(nodepath))
                if delete:
                    dirs, files = xbmcvfs.listdir(utils.tryEncode(nodepath))
                    for file in files:
                        xbmcvfs.delete(
                            utils.tryEncode(
                                (nodepath + utils.tryDecode(file))))

                    self.logMsg("Sucessfully removed videonode: %s." % tagname,
                                1)
                    return

        # 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/?id=%s&mode=getsubfolders" % indexnumber

        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:
                utils.indent(root)
            except:
                pass
            etree.ElementTree(root).write(nodeXML)

        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"
        }
        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,
            },
            'tvshows': {
                '1': tagname,
                # '2': 30170,
                '3': 30174,
                # '4': 30171,
                # '5': 30178,
                # '7': 30179,
                '9': 135,
                '10': 30227,
                # '11': 30230,
                '12': 39500,
            },
            'homevideos': {
                '1': tagname,
                '2': 30251,
                '11': 30253
            },
            'photos': {
                '1': tagname,
                '2': 30252,
                '8': 30255,
                '11': 30254
            },
            'musicvideos': {
                '1': tagname,
                '2': 30256,
                '4': 30257,
                '6': 30258
            }
        }

        # 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"
        }

        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 = utils.language(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 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 self.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'
            else:
                path = "library://video/Plex-%s/%s_%s.xml" % (dirname, viewid,
                                                              nodetype)

            if mediatype == "photos":
                windowpath = "ActivateWindow(Pictures,%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 xbmcvfs.exists(utils.tryEncode(nodeXML)):
                # Don't recreate xml if already exists
                continue

            # Create the root
            if (nodetype in ("nextepisodes", "ondeck", 'recentepisodes')
                    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 utils.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:
                utils.indent(root)
            except:
                pass
            etree.ElementTree(root).write(nodeXML)
Exemple #41
0
    def PlayBackStart(self, data):
        """
        Called whenever a playback is started
        """
        # Get currently playing file - can take a while. Will be utf-8!
        try:
            currentFile = self.xbmcplayer.getPlayingFile()
        except:
            currentFile = None
            count = 0
            while currentFile is None:
                xbmc.sleep(100)
                try:
                    currentFile = self.xbmcplayer.getPlayingFile()
                except:
                    pass
                if count == 50:
                    log.info("No current File, cancel OnPlayBackStart...")
                    return
                else:
                    count += 1
        # Just to be on the safe side
        currentFile = tryDecode(currentFile)
        log.debug("Currently playing file is: %s" % currentFile)

        # Get the type of media we're playing
        try:
            typus = data['item']['type']
        except (TypeError, KeyError):
            log.info("Item is invalid for PMS playstate update.")
            return
        log.debug("Playing itemtype is (or appears to be): %s" % typus)

        # Try to get a Kodi ID
        # If PKC was used - native paths, not direct paths
        plexid = window('emby_%s.itemid' % tryEncode(currentFile))
        # Get rid of the '' if the window property was not set
        plexid = None if not plexid else plexid
        kodiid = None
        if plexid is None:
            log.debug('Did not get Plex id from window properties')
            try:
                kodiid = data['item']['id']
            except (TypeError, KeyError):
                log.debug('Did not get a Kodi id from Kodi, darn')
        # For direct paths, if we're not streaming something
        # When using Widgets, Kodi doesn't tell us shit so we need this hack
        if (kodiid is None and plexid is None and typus != 'song'
                and not currentFile.startswith('http')):
            try:
                filename = currentFile.rsplit('/', 1)[1]
                path = currentFile.rsplit('/', 1)[0] + '/'
            except IndexError:
                filename = currentFile.rsplit('\\', 1)[1]
                path = currentFile.rsplit('\\', 1)[0] + '\\'
            log.debug('Trying to figure out playing item from filename: %s '
                      'and path: %s' % (filename, path))
            with kodidb.GetKodiDB('video') as kodi_db:
                try:
                    kodiid, typus = kodi_db.getIdFromFilename(filename, path)
                except TypeError:
                    log.info('Abort playback report, could not id kodi item')
                    return

        if plexid is None:
            # Get Plex' item id
            with embydb.GetEmbyDB() as emby_db:
                emby_dbitem = emby_db.getItem_byKodiId(kodiid, typus)
            try:
                plexid = emby_dbitem[0]
            except TypeError:
                log.info("No Plex id returned for kodiid %s. Aborting playback"
                         " report" % kodiid)
                return
        log.debug("Found Plex id %s for Kodi id %s for type %s" %
                  (plexid, kodiid, typus))

        # Set some stuff if Kodi initiated playback
        if ((settings('useDirectPaths') == "1" and not typus == "song")
                or (typus == "song" and settings('enableMusic') == "true")):
            if self.StartDirectPath(plexid, typus,
                                    tryEncode(currentFile)) is False:
                log.error('Could not initiate monitoring; aborting')
                return

        # Save currentFile for cleanup later and to be able to access refs
        window('plex_lastPlayedFiled', value=currentFile)
        window('plex_currently_playing_itemid', value=plexid)
        window("emby_%s.itemid" % tryEncode(currentFile), value=plexid)
        log.info('Finish playback startup')
    def PlayBackStart(self, data):
        """
        Called whenever a playback is started
        """
        log = self.logMsg
        window = utils.window

        # Get currently playing file - can take a while. Will be utf-8!
        try:
            currentFile = self.xbmcplayer.getPlayingFile()
        except:
            currentFile = None
            count = 0
            while currentFile is None:
                xbmc.sleep(100)
                try:
                    currentFile = self.xbmcplayer.getPlayingFile()
                except:
                    pass
                if count == 50:
                    log("No current File - Cancelling OnPlayBackStart...", -1)
                    return
                else:
                    count += 1
        log("Currently playing file is: %s" % utils.tryDecode(currentFile), 1)

        # Try to get a Kodi ID
        item = data.get('item')
        try:
            type = item['type']
        except:
            log("Item is invalid for PMS playstate update.", 0)
            return
        try:
            kodiid = item['id']
        except (KeyError, TypeError):
            itemType = window("emby_%s.type" % currentFile)
            log("No kodi id passed. Playing itemtype is: %s" % itemType, 1)
            if itemType in ('movie', 'episode'):
                # Window was setup by PKC and is NOT a trailer ('clip')
                with kodidb.GetKodiDB('video') as kodi_db:
                    kodiid = kodi_db.getIdFromTitle(data.get('item'))
                    if kodiid is None:
                        log("Skip playstate update. No unique Kodi title found"
                            " for %s" % data.get('item'), 0)
                        return
            else:
                log("Item is invalid for PMS playstate update.", 0)
                return

        # Get Plex' item id
        with embydb.GetEmbyDB() as emby_db:
            emby_dbitem = emby_db.getItem_byKodiId(kodiid, type)
        try:
            plexid = emby_dbitem[0]
        except TypeError:
            log("No Plex id returned for kodiid %s" % kodiid, 0)
            return
        log("Found Plex id %s for Kodi id %s" % (plexid, kodiid), 1)

        # Set some stuff if Kodi initiated playback
        if ((utils.settings('useDirectPaths') == "1" and not type == "song") or
                (type == "song" and utils.settings('enableMusic') == "true")):
            if self.StartDirectPath(plexid, type, currentFile) is False:
                log('Could not initiate monitoring; aborting', -1)
                return

        # Save currentFile for cleanup later and to be able to access refs
        window('plex_lastPlayedFiled', value=utils.tryDecode(currentFile))
        window('Plex_currently_playing_itemid', value=plexid)
        window("emby_%s.itemid" % currentFile, value=plexid)
        log('Finish playback startup', 1)
Exemple #43
0
    def onPlayBackStarted(self):
        """
        Window values need to have been set in Kodimonitor.py
        """
        window = utils.window
        # Will be called when xbmc starts playing a file
        self.stopAll()

        # Get current file (in utf-8!)
        try:
            currentFile = self.xbmcplayer.getPlayingFile()
            xbmc.sleep(300)
        except:
            currentFile = ""
            count = 0
            while not currentFile:
                xbmc.sleep(100)
                try:
                    currentFile = self.xbmcplayer.getPlayingFile()
                except:
                    pass
                if count == 20:
                    self.logMsg("Cancelling playback report...", 1)
                    break
                else:
                    count += 1
        if not currentFile:
            self.logMsg('Error getting a currently playing file; abort '
                        'reporting', -1)
            return

        # Save currentFile for cleanup later and for references
        self.currentFile = currentFile
        window('plex_lastPlayedFiled', value=utils.tryDecode(currentFile))
        # We may need to wait for info to be set in kodi monitor
        itemId = window("emby_%s.itemid" % currentFile)
        count = 0
        while not itemId:
            xbmc.sleep(200)
            itemId = window("emby_%s.itemid" % currentFile)
            # try 20 times or about 10 seconds
            if count == 20:
                self.logMsg("Could not find itemId, cancelling playback "
                            "report!", -1)
                return
            count += 1

        self.logMsg("ONPLAYBACK_STARTED: %s itemid: %s"
                    % (utils.tryDecode(currentFile), itemId), 0)

        embyitem = "emby_%s" % currentFile
        runtime = window("%s.runtime" % embyitem)
        refresh_id = window("%s.refreshid" % embyitem)
        playMethod = window("%s.playmethod" % embyitem)
        itemType = window("%s.type" % embyitem)
        try:
            playcount = int(window("%s.playcount" % embyitem))
        except ValueError:
            playcount = 0
        window('emby_skipWatched%s' % itemId, value="true")

        self.logMsg("Playing itemtype is: %s" % itemType, 1)

        customseek = window('plex_customplaylist.seektime')
        if (window('plex_customplaylist') == "true" and customseek):
            # Start at, when using custom playlist (play to Kodi from webclient)
            self.logMsg("Seeking to: %s" % customseek, 1)
            try:
                self.xbmcplayer.seekTime(int(customseek))
            except:
                self.logMsg('Could not seek!', -1)
            window('plex_customplaylist.seektime', clear=True)

        try:
            seekTime = self.xbmcplayer.getTime()
        except RuntimeError:
            self.logMsg('Could not get current seektime from xbmc player', -1)
            seekTime = 0

        # Get playback volume
        volume_query = {
            "jsonrpc": "2.0",
            "id": 1,
            "method": "Application.GetProperties",
            "params": {
                "properties": ["volume", "muted"]
            }
        }
        result = xbmc.executeJSONRPC(json.dumps(volume_query))
        result = json.loads(result)
        result = result.get('result')
        
        volume = result.get('volume')
        muted = result.get('muted')

        # Postdata structure to send to Emby server
        url = "{server}/:/timeline?"
        postdata = {

            'QueueableMediaTypes': "Video",
            'CanSeek': True,
            'ItemId': itemId,
            'MediaSourceId': itemId,
            'PlayMethod': playMethod,
            'VolumeLevel': volume,
            'PositionTicks': int(seekTime * 10000000),
            'IsMuted': muted
        }

        # Get the current audio track and subtitles
        if playMethod == "Transcode":
            # property set in PlayUtils.py
            postdata['AudioStreamIndex'] = window("%sAudioStreamIndex" % currentFile)
            postdata['SubtitleStreamIndex'] = window("%sSubtitleStreamIndex" % currentFile)
        else:
            # Get the current kodi audio and subtitles and convert to Emby equivalent
            tracks_query = {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "Player.GetProperties",
                "params": {

                    "playerid": 1,
                    "properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]
                }
            }
            result = xbmc.executeJSONRPC(json.dumps(tracks_query))
            result = json.loads(result)
            result = result.get('result')

            try: # Audio tracks
                indexAudio = result['currentaudiostream']['index']
            except (KeyError, TypeError):
                indexAudio = 0
            
            try: # Subtitles tracks
                indexSubs = result['currentsubtitle']['index']
            except (KeyError, TypeError):
                indexSubs = 0

            try: # If subtitles are enabled
                subsEnabled = result['subtitleenabled']
            except (KeyError, TypeError):
                subsEnabled = ""

            # Postdata for the audio
            postdata['AudioStreamIndex'] = indexAudio + 1
            
            # Postdata for the subtitles
            if subsEnabled and len(xbmc.Player().getAvailableSubtitleStreams()) > 0:
                
                # Number of audiotracks to help get Emby Index
                audioTracks = len(xbmc.Player().getAvailableAudioStreams())
                mapping = window("%s.indexMapping" % embyitem)

                if mapping: # Set in playbackutils.py
                    
                    self.logMsg("Mapping for external subtitles index: %s"
                                % mapping, 2)
                    externalIndex = json.loads(mapping)

                    if externalIndex.get(str(indexSubs)):
                        # If the current subtitle is in the mapping
                        postdata['SubtitleStreamIndex'] = externalIndex[str(indexSubs)]
                    else:
                        # Internal subtitle currently selected
                        subindex = indexSubs - len(externalIndex) + audioTracks + 1
                        postdata['SubtitleStreamIndex'] = subindex
                
                else: # Direct paths enabled scenario or no external subtitles set
                    postdata['SubtitleStreamIndex'] = indexSubs + audioTracks + 1
            else:
                postdata['SubtitleStreamIndex'] = ""
        

        # Post playback to server
        # log("Sending POST play started: %s." % postdata, 2)
        # self.doUtils(url, postBody=postdata, type="POST")
        
        # Ensure we do have a runtime
        try:
            runtime = int(runtime)
        except ValueError:
            try:
                runtime = self.xbmcplayer.getTotalTime()
                self.logMsg("Runtime is missing, Kodi runtime: %s"
                            % runtime, 1)
            except:
                self.logMsg('Could not get kodi runtime, setting to zero', -1)
                runtime = 0

        playQueueVersion = window('playQueueVersion')
        playQueueID = window('playQueueID')
        playQueueItemID = window('plex_%s.playQueueItemID' % currentFile)
        with embydb.GetEmbyDB() as emby_db:
            emby_dbitem = emby_db.getItem_byId(itemId)
        try:
            fileid = emby_dbitem[1]
        except TypeError:
            self.logMsg("Could not find fileid in plex db.", 1)
            fileid = None
        # Save data map for updates and position calls
        data = {
            'playQueueVersion': playQueueVersion,
            'playQueueID': playQueueID,
            'playQueueItemID': playQueueItemID,
            'runtime': runtime,
            'item_id': itemId,
            'refresh_id': refresh_id,
            'currentfile': currentFile,
            'AudioStreamIndex': postdata['AudioStreamIndex'],
            'SubtitleStreamIndex': postdata['SubtitleStreamIndex'],
            'playmethod': playMethod,
            'Type': itemType,
            'currentPosition': int(seekTime) * 1000,
            'fileid': fileid,
            'itemType': itemType,
            'playcount': playcount
        }
        
        self.played_info[currentFile] = data
        self.logMsg("ADDING_FILE: %s" % data, 1)

        # log some playback stats
        '''if(itemType != None):
Exemple #44
0
                    
            elif mode == "texturecache":
                import artwork
                artwork.Artwork().FullTextureCacheSync()
            else:
                entrypoint.doMainListing()

                      
if ( __name__ == "__main__" ):
    xbmc.log('plugin.video.plexkodiconnect started')

    if enableProfiling:
        import cProfile
        import pstats
        import random
        from time import gmtime, strftime
        addonid      = utils.tryDecode(addon_.getAddonInfo('id'))
        datapath     = os.path.join(utils.tryDecode(xbmc.translatePath( "special://profile/" )), "addon_data", addonid )
        
        filename = os.path.join( datapath, strftime( "%Y%m%d%H%M%S",gmtime() ) + "-" + str( random.randrange(0,100000) ) + ".log" )
        cProfile.run( 'Main()', filename )
        
        stream = open( filename + ".txt", 'w')
        p = pstats.Stats( filename, stream = stream )
        p.sort_stats( "cumulative" )
        p.print_stats()
    
    else:
        Main()
    
    xbmc.log('plugin.video.plexkodiconnect stopped')
import embydb_functions as embydb
import kodidb_functions as kodidb
import musicutils as musicutils

import PlexFunctions as PF
import PlexAPI


def logMsg(msg, lvl=1):
    utils.logMsg("%s %s" % ("PlexKodiConnect", "Contextmenu"), msg, lvl)


#Kodi contextmenu item to configure the emby settings
#for now used to set ratings but can later be used to sync individual items etc.
if __name__ == '__main__':
    itemid = utils.tryDecode(xbmc.getInfoLabel("ListItem.DBID"))
    itemtype = utils.tryDecode(xbmc.getInfoLabel("ListItem.DBTYPE"))
    
    emby = embyserver.Read_EmbyServer()
    
    plexid = ""
    if not itemtype and xbmc.getCondVisibility("Container.Content(albums)"): itemtype = "album"
    if not itemtype and xbmc.getCondVisibility("Container.Content(artists)"): itemtype = "artist"
    if not itemtype and xbmc.getCondVisibility("Container.Content(songs)"): itemtype = "song"
    if not itemtype and xbmc.getCondVisibility("Container.Content(pictures)"): itemtype = "picture"
    
    if (not itemid or itemid == "-1") and xbmc.getInfoLabel("ListItem.Property(plexid)"):
        plexid = xbmc.getInfoLabel("ListItem.Property(plexid)")
    else:
        with embydb.GetEmbyDB() as emby_db:
            item = emby_db.getItem_byKodiId(itemid, itemtype)
Exemple #46
0
    def FullTextureCacheSync(self):
        # This method will sync all Kodi artwork to textures13.db
        # and cache them locally. This takes diskspace!
        import xbmcaddon
        string = xbmcaddon.Addon().getLocalizedString

        if not xbmcgui.Dialog().yesno(
                "Image Texture Cache", string(39250)):
            return

        self.logMsg("Doing Image Cache Sync", 1)

        dialog = xbmcgui.DialogProgress()
        dialog.create("Emby for Kodi", "Image Cache Sync")

        # ask to rest all existing or not
        if xbmcgui.Dialog().yesno(
                "Image Texture Cache", string(39251), ""):
            self.logMsg("Resetting all cache data first", 1)
            # Remove all existing textures first
            path = utils.tryDecode(xbmc.translatePath("special://thumbnails/"))
            if utils.IfExists(path):
                allDirs, allFiles = xbmcvfs.listdir(path)
                for dir in allDirs:
                    allDirs, allFiles = xbmcvfs.listdir(path+dir)
                    for file in allFiles:
                        if os.path.supports_unicode_filenames:
                            xbmcvfs.delete(os.path.join(
                                path + utils.tryDecode(dir),
                                utils.tryDecode(file)))
                        else:
                            xbmcvfs.delete(os.path.join(
                                utils.tryEncode(path) + dir,
                                file))

            # remove all existing data from texture DB
            textureconnection = utils.kodiSQL('texture')
            texturecursor = textureconnection.cursor()
            texturecursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
            rows = texturecursor.fetchall()
            for row in rows:
                tableName = row[0]
                if(tableName != "version"):
                    texturecursor.execute("DELETE FROM " + tableName)
            textureconnection.commit()
            texturecursor.close()

        # Cache all entries in video DB
        connection = utils.kodiSQL('video')
        cursor = connection.cursor()
        cursor.execute("SELECT url FROM art WHERE media_type != 'actor'") # dont include actors
        result = cursor.fetchall()
        total = len(result)
        count = 1
        percentage = 0
        self.logMsg("Image cache sync about to process " + str(total) + " images", 1)
        for url in result:
            if dialog.iscanceled():
                break
            percentage = int((float(count) / float(total))*100)
            textMessage = str(count) + " of " + str(total) + " (" + str(len(self.imageCacheThreads)) + ")"
            dialog.update(percentage, "Updating Image Cache: " + textMessage)
            self.CacheTexture(url[0])
            count += 1
        cursor.close()

        # Cache all entries in music DB
        connection = utils.kodiSQL('music')
        cursor = connection.cursor()
        cursor.execute("SELECT url FROM art")
        result = cursor.fetchall()
        total = len(result)
        count = 1
        percentage = 0
        self.logMsg("Image cache sync about to process " + str(total) + " images", 1)
        for url in result:
            if dialog.iscanceled():
                break
            percentage = int((float(count) / float(total))*100)
            textMessage = str(count) + " of " + str(total)
            dialog.update(percentage, "Updating Image Cache: " + textMessage)
            self.CacheTexture(url[0])
            count += 1
        cursor.close()

        dialog.update(100, "Waiting for all threads to exit: " + str(len(self.imageCacheThreads)))
        self.logMsg("Waiting for all threads to exit", 1)
        while len(self.imageCacheThreads) > 0:
            for thread in self.imageCacheThreads:
                if thread.isFinished:
                    self.imageCacheThreads.remove(thread)
            dialog.update(100, "Waiting for all threads to exit: " + str(len(self.imageCacheThreads)))
            self.logMsg("Waiting for all threads to exit: " + str(len(self.imageCacheThreads)), 1)
            xbmc.sleep(500)

        dialog.close()
Exemple #47
0
    def PlayBackStart(self, data):
        """
        Called whenever a playback is started
        """
        # Get currently playing file - can take a while. Will be utf-8!
        try:
            currentFile = self.xbmcplayer.getPlayingFile()
        except:
            currentFile = None
            count = 0
            while currentFile is None:
                sleep(100)
                try:
                    currentFile = self.xbmcplayer.getPlayingFile()
                except:
                    pass
                if count == 50:
                    log.info("No current File, cancel OnPlayBackStart...")
                    return
                else:
                    count += 1
        # Just to be on the safe side
        currentFile = tryDecode(currentFile)
        log.debug("Currently playing file is: %s" % currentFile)

        # Get the type of media we're playing
        try:
            typus = data['item']['type']
        except (TypeError, KeyError):
            log.info("Item is invalid for PMS playstate update.")
            return
        log.debug("Playing itemtype is (or appears to be): %s" % typus)

        # Try to get a Kodi ID
        # If PKC was used - native paths, not direct paths
        plexid = window('plex_%s.itemid' % tryEncode(currentFile))
        # Get rid of the '' if the window property was not set
        plexid = None if not plexid else plexid
        kodiid = None
        if plexid is None:
            log.debug('Did not get Plex id from window properties')
            try:
                kodiid = data['item']['id']
            except (TypeError, KeyError):
                log.debug('Did not get a Kodi id from Kodi, darn')
        # For direct paths, if we're not streaming something
        # When using Widgets, Kodi doesn't tell us shit so we need this hack
        if (kodiid is None and plexid is None and typus != 'song'
                and not currentFile.startswith('http')):
            (kodiid, typus) = get_kodiid_from_filename(currentFile)
            if kodiid is None:
                return

        if plexid is None:
            # Get Plex' item id
            with plexdb.Get_Plex_DB() as plexcursor:
                plex_dbitem = plexcursor.getItem_byKodiId(kodiid, typus)
            try:
                plexid = plex_dbitem[0]
            except TypeError:
                log.info("No Plex id returned for kodiid %s. Aborting playback"
                         " report" % kodiid)
                return
        log.debug("Found Plex id %s for Kodi id %s for type %s"
                  % (plexid, kodiid, typus))

        # Set some stuff if Kodi initiated playback
        if ((settings('useDirectPaths') == "1" and not typus == "song")
                or
                (typus == "song" and settings('enableMusic') == "true")):
            if self.StartDirectPath(plexid,
                                    typus,
                                    tryEncode(currentFile)) is False:
                log.error('Could not initiate monitoring; aborting')
                return

        # Save currentFile for cleanup later and to be able to access refs
        window('plex_lastPlayedFiled', value=currentFile)
        window('plex_currently_playing_itemid', value=plexid)
        window("plex_%s.itemid" % tryEncode(currentFile), value=plexid)
        log.info('Finish playback startup')
Exemple #48
0
 def restoreCacheDirectories():
     LOG.info("Restoring cache directories...")
     paths = ("","0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","Video","plex")
     for p in paths:
         makedirs(tryDecode(translatePath("special://thumbnails/%s" % p)))
Exemple #49
0
    def FullTextureCacheSync(self):
        # This method will sync all Kodi artwork to textures13.db
        # and cache them locally. This takes diskspace!
        import xbmcaddon
        string = xbmcaddon.Addon().getLocalizedString

        if not xbmcgui.Dialog().yesno("Image Texture Cache", string(39250)):
            return

        self.logMsg("Doing Image Cache Sync", 1)

        dialog = xbmcgui.DialogProgress()
        dialog.create("PlexKodiConnect", "Image Cache Sync")

        # ask to rest all existing or not
        if xbmcgui.Dialog().yesno("Image Texture Cache", string(39251), ""):
            self.logMsg("Resetting all cache data first", 1)
            # Remove all existing textures first
            path = utils.tryDecode(xbmc.translatePath("special://thumbnails/"))
            if utils.IfExists(path):
                allDirs, allFiles = xbmcvfs.listdir(path)
                for dir in allDirs:
                    allDirs, allFiles = xbmcvfs.listdir(path + dir)
                    for file in allFiles:
                        if os.path.supports_unicode_filenames:
                            xbmcvfs.delete(
                                os.path.join(path + utils.tryDecode(dir),
                                             utils.tryDecode(file)))
                        else:
                            xbmcvfs.delete(
                                os.path.join(
                                    utils.tryEncode(path) + dir, file))

            # remove all existing data from texture DB
            textureconnection = utils.kodiSQL('texture')
            texturecursor = textureconnection.cursor()
            texturecursor.execute(
                'SELECT tbl_name FROM sqlite_master WHERE type="table"')
            rows = texturecursor.fetchall()
            for row in rows:
                tableName = row[0]
                if (tableName != "version"):
                    texturecursor.execute("DELETE FROM " + tableName)
            textureconnection.commit()
            texturecursor.close()

        # Cache all entries in video DB
        connection = utils.kodiSQL('video')
        cursor = connection.cursor()
        cursor.execute("SELECT url FROM art WHERE media_type != 'actor'"
                       )  # dont include actors
        result = cursor.fetchall()
        total = len(result)
        count = 1
        percentage = 0
        self.logMsg(
            "Image cache sync about to process " + str(total) + " images", 1)
        for url in result:
            if dialog.iscanceled():
                break
            percentage = int((float(count) / float(total)) * 100)
            textMessage = str(count) + " of " + str(total) + " (" + str(
                len(self.imageCacheThreads)) + ")"
            dialog.update(percentage, "Updating Image Cache: " + textMessage)
            self.CacheTexture(url[0])
            count += 1
        cursor.close()

        # Cache all entries in music DB
        connection = utils.kodiSQL('music')
        cursor = connection.cursor()
        cursor.execute("SELECT url FROM art")
        result = cursor.fetchall()
        total = len(result)
        count = 1
        percentage = 0
        self.logMsg(
            "Image cache sync about to process " + str(total) + " images", 1)
        for url in result:
            if dialog.iscanceled():
                break
            percentage = int((float(count) / float(total)) * 100)
            textMessage = str(count) + " of " + str(total)
            dialog.update(percentage, "Updating Image Cache: " + textMessage)
            self.CacheTexture(url[0])
            count += 1
        cursor.close()

        dialog.update(
            100, "Waiting for all threads to exit: " +
            str(len(self.imageCacheThreads)))
        self.logMsg("Waiting for all threads to exit", 1)
        while len(self.imageCacheThreads) > 0:
            for thread in self.imageCacheThreads:
                if thread.isFinished:
                    self.imageCacheThreads.remove(thread)
            dialog.update(
                100, "Waiting for all threads to exit: " +
                str(len(self.imageCacheThreads)))
            self.logMsg(
                "Waiting for all threads to exit: " +
                str(len(self.imageCacheThreads)), 1)
            xbmc.sleep(500)

        dialog.close()
Exemple #50
0
    def fullTextureCacheSync(self):
        """
        This method will sync all Kodi artwork to textures13.db
        and cache them locally. This takes diskspace!
        """
        if not dialog('yesno', "Image Texture Cache", lang(39250)):
            return

        log.info("Doing Image Cache Sync")

        # ask to rest all existing or not
        if dialog('yesno', "Image Texture Cache", lang(39251)):
            log.info("Resetting all cache data first")
            # Remove all existing textures first
            path = tryDecode(translatePath("special://thumbnails/"))
            if IfExists(path):
                allDirs, allFiles = listdir(path)
                for dir in allDirs:
                    allDirs, allFiles = listdir(path+dir)
                    for file in allFiles:
                        if os_path.supports_unicode_filenames:
                            delete(os_path.join(
                                path + tryDecode(dir),
                                tryDecode(file)))
                        else:
                            delete(os_path.join(
                                tryEncode(path) + dir,
                                file))

            # remove all existing data from texture DB
            connection = kodiSQL('texture')
            cursor = connection.cursor()
            cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
            rows = cursor.fetchall()
            for row in rows:
                tableName = row[0]
                if tableName != "version":
                    cursor.execute("DELETE FROM " + tableName)
            connection.commit()
            connection.close()

        # Cache all entries in video DB
        connection = kodiSQL('video')
        cursor = connection.cursor()
        # dont include actors
        cursor.execute("SELECT url FROM art WHERE media_type != 'actor'")
        result = cursor.fetchall()
        total = len(result)
        log.info("Image cache sync about to process %s video images" % total)
        connection.close()

        for url in result:
            self.cacheTexture(url[0])
        # Cache all entries in music DB
        connection = kodiSQL('music')
        cursor = connection.cursor()
        cursor.execute("SELECT url FROM art")
        result = cursor.fetchall()
        total = len(result)
        log.info("Image cache sync about to process %s music images" % total)
        connection.close()
        for url in result:
            self.cacheTexture(url[0])
    def authenticate(self):
        log = self.logMsg
        log('Authenticating user', 1)
        lang = utils.language
        window = utils.window
        settings = utils.settings
        dialog = xbmcgui.Dialog()

        # Give attempts at entering password / selecting user
        if self.retry >= 2:
            log("Too many retries to login.", -1)
            window('plex_serverStatus', value="Stop")
            dialog.ok(lang(33001),
                      lang(39023))
            xbmc.executebuiltin(
                'Addon.OpenSettings(plugin.video.plexkodiconnect)')
            return False

        # Get /profile/addon_data
        addondir = utils.tryDecode(xbmc.translatePath(
            self.addon.getAddonInfo('profile')))
        hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)

        # If there's no settings.xml
        if not hasSettings:
            log("Error, no settings.xml found.", -1)
            self.auth = False
            return False
        server = self.getServer()
        # If there is no server we can connect to
        if not server:
            log("Missing server information.", 0)
            self.auth = False
            return False

        # If there is a username in the settings, try authenticating
        username = settings('username')
        userId = settings('userid')
        usertoken = settings('accessToken')
        enforceLogin = settings('enforceUserLogin')
        # Found a user in the settings, try to authenticate
        if username and enforceLogin == 'false':
            log('Trying to authenticate with old settings', 0)
            if self.loadCurrUser(username,
                                 userId,
                                 usertoken,
                                 authenticated=False):
                # SUCCESS: loaded a user from the settings
                return True
            else:
                # Failed to use the settings - delete them!
                log("Failed to use the settings credentials. Deleting them", 1)
                settings('username', value='')
                settings('userid', value='')
                settings('accessToken', value='')

        plx = PlexAPI.PlexAPI()

        # Could not use settings - try to get Plex user list from plex.tv
        plextoken = settings('plexToken')
        if plextoken:
            log("Trying to connect to plex.tv to get a user list", 0)
            userInfo = plx.ChoosePlexHomeUser(plextoken)
            if userInfo is False:
                # FAILURE: Something went wrong, try again
                self.auth = True
                self.retry += 1
                return False
            username = userInfo['username']
            userId = userInfo['userid']
            usertoken = userInfo['token']
        else:
            log("Trying to authenticate without a token", 0)
            username = ''
            userId = ''
            usertoken = ''

        if self.loadCurrUser(username, userId, usertoken, authenticated=False):
            # SUCCESS: loaded a user from the settings
            return True
        else:
            # FAILUR: Something went wrong, try again
            self.auth = True
            self.retry += 1
            return False
    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 = "100"
        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
        
        path = tryDecode(xbmc.translatePath(
            "special://profile/library/video/"))
        nodepath = tryDecode(xbmc.translatePath(
            "special://profile/library/video/Plex-%s/" % dirname))

        # Verify the video directory
        # KODI BUG
        # Kodi caches the result of exists for directories
        #  so try creating a file
        if IfExists(path) is False:
            shutil.copytree(
                src=tryDecode(xbmc.translatePath(
                    "special://xbmc/system/library/video")),
                dst=tryDecode(xbmc.translatePath(
                    "special://profile/library/video")))

        # Create the node directory
        if mediatype != "photos":
            if IfExists(nodepath) is False:
                # folder does not exist yet
                log.debug('Creating folder %s' % nodepath)
                xbmcvfs.mkdirs(tryEncode(nodepath))
                if delete:
                    dirs, files = xbmcvfs.listdir(tryEncode(nodepath))
                    for file in files:
                        xbmcvfs.delete(tryEncode(
                            (nodepath + tryDecode(file))))

                log.info("Sucessfully removed videonode: %s." % tagname)
                return

        # 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/?id=%s&mode=getsubfolders" % indexnumber
            
        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)

        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"
        }
        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,
                },

            'tvshows': 
                {
                '1': tagname,
                # '2': 30170,
                '3': 30174,
                # '4': 30171,
                # '5': 30178,
                # '7': 30179,
                '9': 135,
                '10': 30227,
                # '11': 30230,
                '12': 39500,
                },
                
            'homevideos': 
                {
                '1': tagname,
                '2': 30251,
                '11': 30253
                },
                
            'photos': 
                {
                '1': tagname,
                '2': 30252,
                '8': 30255,
                '11': 30254
                },

            'musicvideos': 
                {
                '1': tagname,
                '2': 30256,
                '4': 30257,
                '6': 30258
                }
        }

        # 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"
        }

        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 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 self.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'
            else:
                path = "library://video/Plex-%s/%s_%s.xml" % (dirname, viewid, nodetype)
            
            if mediatype == "photos":
                windowpath = "ActivateWindow(Pictures,%s,return)" % path
            else:
                if self.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 xbmcvfs.exists(tryEncode(nodeXML)):
                # Don't recreate xml if already exists
                continue

            # Create the root
            if (nodetype in ("nextepisodes", "ondeck", 'recentepisodes') 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)
    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
Exemple #54
0
            elif mode == "texturecache":
                import artwork
                artwork.Artwork().FullTextureCacheSync()
            else:
                entrypoint.doMainListing()


if (__name__ == "__main__"):
    xbmc.log('plugin.video.plexkodiconnect started')

    if enableProfiling:
        import cProfile
        import pstats
        import random
        from time import gmtime, strftime
        addonid = utils.tryDecode(addon_.getAddonInfo('id'))
        datapath = os.path.join(
            utils.tryDecode(xbmc.translatePath("special://profile/")),
            "addon_data", addonid)

        filename = os.path.join(
            datapath,
            strftime("%Y%m%d%H%M%S", gmtime()) + "-" +
            str(random.randrange(0, 100000)) + ".log")
        cProfile.run('Main()', filename)

        stream = open(filename + ".txt", 'w')
        p = pstats.Stats(filename, stream=stream)
        p.sort_stats("cumulative")
        p.print_stats()