コード例 #1
0
ファイル: vault.py プロジェクト: normico21/repository.xvbmc
    def __GetApplicationKey(self):
        """ Gets the decrypted application key that is used for all the encryption

        @return: the decrypted application key that is used for all the encryption
        """

        applicationKeyEncrypted = AddonSettings.GetSetting(Vault.__APPLICATION_KEY_SETTING)
        if not applicationKeyEncrypted:
            return None

        vaultIncorrectPin = LanguageHelper.GetLocalizedString(LanguageHelper.VaultIncorrectPin)
        pin = XbmcWrapper.ShowKeyBoard(
            heading=LanguageHelper.GetLocalizedString(LanguageHelper.VaultInputPin),
            hidden=True)
        if not pin:
            XbmcWrapper.ShowNotification("", vaultIncorrectPin, XbmcWrapper.Error)
            raise RuntimeError("Incorrect Retrospect PIN specified")
        pinKey = self.__GetPBK(pin)
        applicationKey = self.__Decrypt(applicationKeyEncrypted, pinKey)
        if not applicationKey.startswith(Vault.__APPLICATION_KEY_SETTING):
            Logger.Critical("Invalid Retrospect PIN")
            XbmcWrapper.ShowNotification("", vaultIncorrectPin, XbmcWrapper.Error)
            raise RuntimeError("Incorrect Retrospect PIN specified")

        applicationKeyValue = applicationKey[len(Vault.__APPLICATION_KEY_SETTING) + 1:]
        Logger.Info("Successfully decrypted the ApplicationKey.")
        return applicationKeyValue
コード例 #2
0
    def __GetParameters(self, queryString):
        """ Extracts the actual parameters as a dictionary from the passed in
        querystring. This method takes the self.quotedPlus into account.

        Arguments:
        queryString : String - The querystring

        Returns:
        dict() of keywords and values.

        """
        result = dict()
        queryString = queryString.strip('?')
        if queryString != '':
            try:
                for pair in queryString.split("&"):
                    (k, v) = pair.split("=")
                    result[k] = v

                # if the channelcode was empty, it was stripped, add it again.
                if self.keywordChannelCode not in result:
                    Logger.Debug("Adding ChannelCode=None as it was missing from the dict: %s", result)
                    result[self.keywordChannelCode] = None
            except:
                Logger.Critical("Cannot determine query strings from %s", queryString, exc_info=True)
                raise

        return result
コード例 #3
0
        def Header(self,
                   uri,
                   proxy=None,
                   params="",
                   referer=None,
                   additionalHeaders=None,
                   noCache=False):
            """Retrieves header information only

            Arguments:
            uri      : string - the URI to download

            Keyword Arguments:
            proxy  : [opt] string  - The address and port (proxy.address.ext:port) of
                                     a proxy server that should be used.
            params : [opt] string  - data to send with the request (open(uri, params))

            Returns:
            Data and the URL to which a redirect could have occurred.

            """

            Logger.Info("Retreiving Header info for %s", uri)
            # uri = uri
            # params = params

            if not uri:
                return "", ""

            try:
                if params == "":
                    uriHandle = self.__GetOpener(
                        uri,
                        proxy,
                        disableCaching=noCache,
                        headOnly=True,
                        referer=referer,
                        additionalHeaders=additionalHeaders).open(uri)
                else:
                    uriHandle = self.__GetOpener(
                        uri,
                        proxy,
                        disableCaching=noCache,
                        headOnly=True,
                        referer=referer,
                        additionalHeaders=additionalHeaders).open(uri, params)

                data = uriHandle.info()
                realUrl = uriHandle.geturl()
                data = data.get('Content-Type')
                uriHandle.close()
                Logger.Debug("Header info retreived: %s for realUrl %s", data,
                             realUrl)
                return data, realUrl
            except:
                Logger.Critical("Header info not retreived", exc_info=True)
                return "", ""
コード例 #4
0
ファイル: regexer.py プロジェクト: normico21/repository.xvbmc
    def DoRegex(regex, data):
        """Performs a regular expression
        
        Arguments:
        regex : string - the regex to perform on the data.
        data  : string - the data to perform the regex on.
        
        Returns:
        A list of matches that came from the regex.findall method.
        
        Performs a regular expression findall on the <data> and returns the results
        that came from the method.     
        
        From the sre.py library:
        If one or more groups are present in the pattern, return a
        list of groups; this will be a list of tuples if the pattern
        has more than one group.
    
        Empty matches are included in the result.
        
        """

        try:

            if not isinstance(regex, (tuple, list)):
                if "?P<" in regex:
                    return Regexer.__DoDictionaryRegex(regex, data)
                else:
                    return Regexer.__DoRegex(regex, data)
            else:
                Logger.Debug("Performing multi-regex find on '%s'", regex)
                results = []
                count = 0
                for r in regex:
                    if "?P<" in r:
                        regexResults = Regexer.__DoDictionaryRegex(r, data)
                        # add to the results with a count in front of the results
                        results += map(lambda x: (count, x), regexResults)
                    else:
                        regexResults = Regexer.__DoRegex(r, data)
                        if len(regexResults) > 0:
                            if isinstance(regexResults[0], (tuple, list)):
                                # is a tupe/list was returned, prepend it with the count
                                results += map(lambda x: (count, ) + x,
                                               regexResults)
                            else:
                                # create a tuple with the results
                                results += map(lambda x: (count, x),
                                               regexResults)
                    # increase count
                    count += 1
                Logger.Debug("Returning %s results", len(results))
                return results
        except:
            Logger.Critical('error regexing', exc_info=True)
            return []
コード例 #5
0
    def CreateVideoItem(self, resultSet):
        """Creates a MediaItem of type 'video' using the resultSet from the regex.

        Arguments:
        resultSet : tuple (string) - the resultSet of the self.videoItemRegex

        Returns:
        A new MediaItem of type 'video' or 'audio' (despite the method's name)

        This method creates a new MediaItem from the Regular Expression or Json
        results <resultSet>. The method should be implemented by derived classes
        and are specific to the channel.

        If the item is completely processed an no further data needs to be fetched
        the self.complete property should be set to True. If not set to True, the
        self.UpdateVideoItem method is called if the item is focussed or selected
        for playback.

        """

        Logger.Trace(resultSet)

        # Validate the input and raise errors
        if not isinstance(resultSet, dict):
            Logger.Critical("No Dictionary as a resultSet. Implement a custom CreateVideoItem")
            raise NotImplementedError("No Dictionary as a resultSet. Implement a custom CreateVideoItem")
            # return None
        elif "title" not in resultSet or "url" not in resultSet:
            Logger.Warning("No ?P<title> or ?P<url> in resultSet")
            raise LookupError("No ?P<title> or ?P<url> in resultSet")
            # return None

        # The URL
        url = resultSet["url"]
        if not url.startswith("http"):
            url = "%s/%s" % (self.baseUrl.rstrip('/'), url.lstrip('/'))

        # The title
        if "subtitle" in resultSet:
            title = "%(title)s - %(subtitle)s" % resultSet
        else:
            title = resultSet["title"]
        if title.isupper():
            title = title.title()

        item = mediaitem.MediaItem(title, url)
        item.description = resultSet.get("description", "")
        item.thumb = resultSet.get("thumburl", "")
        item.icon = self.icon
        item.type = 'video'
        item.fanart = self.fanart
        item.HttpHeaders = self.httpHeaders
        item.complete = False
        return item
コード例 #6
0
ファイル: vault.py プロジェクト: normico21/repository.xvbmc
    def ChangePin(self, applicationKey=None):
        # type: (str) -> bool
        """ Stores an existing ApplicationKey using a new PIN

        @param applicationKey: an existing ApplicationKey that will be stored. If none specified,
                               the existing ApplicationKey of the Vault will be used.
        @return: indication of success
        """

        Logger.Info("Updating the ApplicationKey with a new PIN")

        if self.__newKeyGeneratedInConstructor:
            Logger.Info("A key was just generated, no need to change PINs.")
            return True

        if applicationKey is None:
            Logger.Debug("Using the ApplicationKey from the vault.")
            applicationKey = Vault.__Key
        else:
            Logger.Debug("Using the ApplicationKey from the input parameter.")

        if not applicationKey:
            raise ValueError("No ApplicationKey specified.")

        # Now we get a new PIN and (re)encrypt

        pin = XbmcWrapper.ShowKeyBoard(
            heading=LanguageHelper.GetLocalizedString(LanguageHelper.VaultNewPin),
            hidden=True)
        if not pin:
            XbmcWrapper.ShowNotification(
                "", LanguageHelper.GetLocalizedString(LanguageHelper.VaultNoPin),
                XbmcWrapper.Error)
            return False

        pin2 = XbmcWrapper.ShowKeyBoard(
            heading=LanguageHelper.GetLocalizedString(LanguageHelper.VaultRepeatPin),
            hidden=True)
        if pin != pin2:
            Logger.Critical("Mismatch in PINs")
            XbmcWrapper.ShowNotification(
                "",
                LanguageHelper.GetLocalizedString(LanguageHelper.VaultPinsDontMatch),
                XbmcWrapper.Error)
            return False

        encryptedKey = "%s=%s" % (self.__APPLICATION_KEY_SETTING, applicationKey)

        # let's generate a pin using the scrypt password-based key derivation
        pinKey = self.__GetPBK(pin)
        encryptedKey = self.__Encrypt(encryptedKey, pinKey)
        AddonSettings.SetSetting(Vault.__APPLICATION_KEY_SETTING, encryptedKey)
        Logger.Info("Successfully updated the Retrospect PIN")
        return True
コード例 #7
0
    def ShowChannelList(self, category=None):
        """Displays the channels that are currently available in XOT as a directory
        listing.

        Keyword Arguments:
        category : String - The category to show channels for

        """

        if category:
            Logger.Info("Plugin::ShowChannelList for %s", category)
        else:
            Logger.Info("Plugin::ShowChannelList")
        try:
            # only display channels
            channelRegister = ChannelImporter.GetRegister()
            channels = channelRegister.GetChannels(infoOnly=True)

            xbmcItems = []
            for channel in channels:
                if category and channel.category != category:
                    Logger.Debug("Skipping %s (%s) due to category filter", channel.channelName, channel.category)
                    continue

                # Get the XBMC item
                item = channel.GetXBMCItem()
                # Get the context menu items
                contextMenuItems = self.__GetContextMenuItems(channel)
                item.addContextMenuItems(contextMenuItems)
                # Get the URL for the item
                url = self.__CreateActionUrl(channel, action=self.actionListFolder)

                # Append to the list of XBMC Items
                xbmcItems.append((url, item, True))

            # Add the items
            ok = xbmcplugin.addDirectoryItems(self.handle, xbmcItems, len(xbmcItems))

            # Just let Kodi display the order we give.
            xbmcplugin.addSortMethod(handle=self.handle, sortMethod=xbmcplugin.SORT_METHOD_UNSORTED)
            xbmcplugin.addSortMethod(handle=self.handle, sortMethod=xbmcplugin.SORT_METHOD_TITLE)
            xbmcplugin.addSortMethod(handle=self.handle, sortMethod=xbmcplugin.SORT_METHOD_GENRE)
            xbmcplugin.setContent(handle=self.handle, content=self.contentType)
            xbmcplugin.endOfDirectory(self.handle, ok)
        except:
            xbmcplugin.endOfDirectory(self.handle, False)
            Logger.Critical("Error fetching channels for plugin", exc_info=True)
コード例 #8
0
    def CreateFolderItem(self, resultSet):
        """Creates a MediaItem of type 'folder' using the resultSet from the regex.

        Arguments:
        resultSet : tuple(strig) - the resultSet of the self.folderItemRegex

        Returns:
        A new MediaItem of type 'folder'

        This method creates a new MediaItem from the Regular Expression or Json
        results <resultSet>. The method should be implemented by derived classes
        and are specific to the channel.

        """

        Logger.Trace(resultSet)

        # Validate the input and raise errors
        if not isinstance(resultSet, dict):
            Logger.Critical("No Dictionary as a resultSet. Implement a custom CreateVideoItem")
            raise NotImplementedError("No Dictionary as a resultSet. Implement a custom CreateVideoItem")
            # return None
        elif "title" not in resultSet or "url" not in resultSet:
            Logger.Warning("No ?P<title> or ?P<url> in resultSet")
            raise LookupError("No ?P<title> or ?P<url> in resultSet")
            # return None

        # The URL
        url = resultSet["url"]
        if not url.startswith("http"):
            url = "%s/%s" % (self.baseUrl.rstrip('/'), url.lstrip('/'))

        # The title
        title = resultSet["title"]
        if title.isupper():
            title = title.title()

        item = mediaitem.MediaItem(title, url)
        item.description = resultSet.get("description", "")
        item.thumb = resultSet.get("thumburl", "")
        item.icon = self.icon
        item.type = 'folder'
        item.fanart = self.fanart
        item.HttpHeaders = self.httpHeaders
        item.complete = True
        return item
コード例 #9
0
    def CacheCleanUp(path, cacheTime, mask="*.*"):
        """Cleans up the XOT cache folder.

        Check the cache files create timestamp and compares it with the current datetime extended
        with the amount of seconds as defined in cacheTime.

        Expired items are deleted.

        """

        # let's import htis one here
        import glob

        try:
            Logger.Info("Cleaning up cache in '%s' that is older than %s days",
                        path, cacheTime / 24 / 3600)
            if not os.path.exists(path):
                Logger.Info("Did not cleanup cache: folder does not exist")
                return

            deleteCount = 0
            fileCount = 0

            #for item in os.listdir(path):
            pathMask = os.path.join(path, mask)
            for item in glob.glob(pathMask):
                fileName = os.path.join(path, item)
                if os.path.isfile(fileName):
                    Logger.Trace(fileName)
                    fileCount += 1
                    createTime = os.path.getctime(fileName)
                    if createTime + cacheTime < time.time():
                        os.remove(fileName)
                        Logger.Debug("Removed file: %s", fileName)
                        deleteCount += 1
            Logger.Info("Removed %s of %s files from cache in: '%s'",
                        deleteCount, fileCount, pathMask)
        except:
            Logger.Critical("Error cleaning the cachefolder: %s",
                            path,
                            exc_info=True)
コード例 #10
0
    def __ParseChannelVersionInfo(self, path):
        """ Parses the addon.xml file and checks if all is OK.

        @param path: path to load the addon from
        @return: the AddonId-Version
        """

        addonFile = os.path.join(path, "addon.xml")

        # continue if no addon.xml exists
        if not os.path.isfile(addonFile):
            Logger.Info("No addon.xml found at %s", addonFile)
            return None

        f = open(addonFile, 'r+')
        addonXml = f.read()
        f.close()

        packVersion = Regexer.DoRegex('id="([^"]+)"\W+version="([^"]{5,10})"', addonXml)
        if len(packVersion) > 0:
            # Get the first match
            packVersion = packVersion[0]

            packageId = packVersion[0]
            packageVersion = version.Version(version=packVersion[1])
            # channelAddon = os.path.split(path)[-1]
            # packVersion = packVersion.
            if Config.version.EqualRevisions(packageVersion):
                Logger.Info("Loading %s version %s", packageId, packageVersion)

                # save to the list of present items, for querying in the
                # future (xbox updates)
                channelVersionID = "%s-%s" % (packVersion[0], packVersion[1])
                return channelVersionID
            else:
                Logger.Warning("Skipping %s version %s: Versions do not match.", packageId,
                               packageVersion)
                return None
        else:
            Logger.Critical("Cannot determine channel version. Not loading channel @ '%s'", path)
            return None
コード例 #11
0
    def __GetIndex(self):
        # type: () -> dict
        """ Loads the channel index and if there is none, makes sure one is created.

        Checks:
        1. Existence of the index
        2. Channel add-ons in the index vs actual add-ons

        @return:
        """

        # if it was not already re-index and the bit was set
        if self.__reindex:
            if self.__reindexed:
                Logger.Warning("Forced re-index set, but a re-index was already done previously. Not Rebuilding.")
            else:
                Logger.Info("Forced re-index set. Rebuilding.")
                return self.__RebuildIndex()

        if not os.path.isfile(self.__CHANNEL_INDEX):
            Logger.Info("No index file found at '%s'. Rebuilding.", self.__CHANNEL_INDEX)
            return self.__RebuildIndex()

        fd = None
        try:
            fd = open(self.__CHANNEL_INDEX)
            data = fd.read()
            indexJson = JsonHelper(data, logger=Logger.Instance())
            Logger.Debug("Loaded index from '%s'.", self.__CHANNEL_INDEX)

            if not self.__IsIndexConsistent(indexJson.json):
                return self.__RebuildIndex()
            return indexJson.json
        except:
            Logger.Critical("Error reading channel index. Rebuilding.", exc_info=True)
            return self.__RebuildIndex()
        finally:
            if fd is not None and not fd.closed:
                fd.close()
コード例 #12
0
    def __ValidateAddOnVersion(self, path):
        """ Parses the addon.xml file and checks if all is OK.

        @param path: path to load the addon from
        @return: the AddonId-Version

        """

        addonFile = os.path.join(path, "addon.xml")

        # continue if no addon.xml exists
        if not os.path.isfile(addonFile):
            Logger.Info("No addon.xml found at %s.", addonFile)
            return None, None

        f = open(addonFile, 'r+')
        addonXml = f.read()
        f.close()

        packVersion = Regexer.DoRegex('id="([^"]+)"\W+version="([^"]+)"', addonXml)
        if len(packVersion) > 0:
            # Get the first match
            packVersion = packVersion[0]
            packageId = packVersion[0]
            packageVersion = Version(version=packVersion[1])
            if Config.version.EqualBuilds(packageVersion):
                Logger.Info("Adding %s version %s", packageId, packageVersion)
                return packageId, packageVersion
            else:
                Logger.Warning("Skipping %s version %s: Versions do not match.", packageId,
                               packageVersion)
                return None, None
        else:
            Logger.Critical("Cannot determine Channel Add-on version. Not loading Add-on @ '%s'.",
                            path)
            return None, None
コード例 #13
0
    def ProcessFolderList(self):
        """Wraps the channel.ProcessFolderList"""

        Logger.Info("Plugin::ProcessFolderList Doing ProcessFolderList")
        try:
            ok = True

            selectedItem = None
            if self.keywordPickle in self.params:
                selectedItem = Pickler.DePickleMediaItem(self.params[self.keywordPickle])

            watcher = stopwatch.StopWatch("Plugin ProcessFolderList", Logger.Instance())
            episodeItems = self.channelObject.ProcessFolderList(selectedItem)
            watcher.Lap("Class ProcessFolderList finished")

            if len(episodeItems) == 0:
                Logger.Warning("ProcessFolderList returned %s items", len(episodeItems))
                ok = self.__ShowEmptyInformation(episodeItems)
            else:
                Logger.Debug("ProcessFolderList returned %s items", len(episodeItems))

            xbmcItems = []
            for episodeItem in episodeItems:
                if episodeItem.thumb == "":
                    episodeItem.thumb = self.channelObject.noImage
                if episodeItem.fanart == "":
                    episodeItem.fanart = self.channelObject.fanart

                if episodeItem.type == 'folder' or episodeItem.type == 'append' or episodeItem.type == "page":
                    action = self.actionListFolder
                    folder = True
                elif episodeItem.IsPlayable():
                    action = self.actionPlayVideo
                    folder = False
                else:
                    Logger.Critical("Plugin::ProcessFolderList: Cannot determine what to add")
                    continue

                # Get the XBMC item
                item = episodeItem.GetXBMCItem()
                # Get the context menu items
                contextMenuItems = self.__GetContextMenuItems(self.channelObject, item=episodeItem)
                item.addContextMenuItems(contextMenuItems)
                # Get the action URL
                url = self.__CreateActionUrl(self.channelObject, action=action, item=episodeItem)
                # Add them to the list of XBMC items
                xbmcItems.append((url, item, folder))

            watcher.Lap("Kodi Items generated")
            # add items but if OK was False, keep it like that
            ok = ok and xbmcplugin.addDirectoryItems(self.handle, xbmcItems, len(xbmcItems))
            watcher.Lap("items send to Kodi")

            if selectedItem is None:
                # mainlist item register channel.
                Statistics.RegisterChannelOpen(self.channelObject, Initializer.StartTime)
                watcher.Lap("Statistics send")

            watcher.Stop()

            self.__AddSortMethodToHandle(self.handle, episodeItems)

            # set the content
            xbmcplugin.setContent(handle=self.handle, content=self.contentType)

            xbmcplugin.endOfDirectory(self.handle, ok)
        except:
            Statistics.RegisterError(self.channelObject)
            XbmcWrapper.ShowNotification(LanguageHelper.GetLocalizedString(LanguageHelper.ErrorId),
                                         LanguageHelper.GetLocalizedString(LanguageHelper.ErrorList),
                                         XbmcWrapper.Error, 4000)
            Logger.Error("Plugin::Error Processing FolderList", exc_info=True)
            xbmcplugin.endOfDirectory(self.handle, False)
コード例 #14
0
    from environments import Environments
    from initializer import Initializer
    from updater import Updater
    from favourites import Favourites
    from mediaitem import MediaItem
    from helpers.channelimporter import ChannelImporter
    from helpers.languagehelper import LanguageHelper
    from helpers.htmlentityhelper import HtmlEntityHelper
    from helpers import stopwatch
    from helpers.statistics import Statistics
    from helpers.sessionhelper import SessionHelper
    from textures import TextureHandler
    # from streams.youtube import YouTube
    from pickler import Pickler
except:
    Logger.Critical("Error initializing %s", Config.appName, exc_info=True)


#===============================================================================
# Main Plugin Class
#===============================================================================
class Plugin:
    """Main Plugin Class

    This class makes it possible to access all the XOT channels as a Kodi Add-on
    instead of a script. s

    """

    def __init__(self, pluginName, params, handle=0):
        """Initialises the plugin with given arguments."""
コード例 #15
0
        def __RetreiveData(self,
                           destHandle,
                           uri,
                           timeOutValue,
                           progressCallback=None,
                           proxy=None,
                           maxBytes=0,
                           params="",
                           referer=None,
                           additionalHeaders=None,
                           noCache=False,
                           blockMultiplier=1):
            """Open an URL Async using a thread

            Arguments:
            uri      : string - the URI to download

            Keyword Arguments:
            progressCallback : [opt] boolean - should a progress bar be shown
            proxy            : [opt] string  - The address and port (proxy.address.ext:port) of
                                               a proxy server that should be used.
            bytes            : [opt] integer - the number of bytes to get.
            params           : [opt] string  - data to send with the request (open(uri, params))
            headers          : [opt] dict    - a dictionary of additional headers

            Returns:
            The data that was retrieved from the URI.

            """

            # init parameters
            canceled = False
            timeOut = False
            error = False
            srcHandle = None
            blocksRead = 0
            fileSize = 0
            charSet = None
            blockSize = self.blockSize * blockMultiplier

            try:
                if uri == "":
                    return error, canceled, ""

                if uri.startswith("file:"):
                    index = uri.rfind("?")
                    #index = string.rfind(uri, "?")
                    if index > 0:
                        uri = uri[0:index]

                Logger.Info(
                    "Opening requested uri: %s (callback=%s, timeout=%s)", uri,
                    progressCallback is not None, timeOutValue)
                self.__DoCallback(progressCallback, 0, blockSize, 0, False)

                # set the start time in seconds
                startTime = time.time()

                # get an opener and handle
                opener = self.__GetOpener(uri,
                                          proxy,
                                          disableCaching=noCache,
                                          referer=referer,
                                          additionalHeaders=additionalHeaders)
                if params == '':
                    srcHandle = opener.open(uri)
                else:
                    srcHandle = opener.open(uri, params)

                # get some metadata
                Logger.Debug("Determining number of bytes to fetch")
                data = srcHandle.info()
                if data.get('Content-length'):
                    fileSize = int(data.get('Content-length'))
                    Logger.Debug('ByteSize is known (fileSize=' +
                                 str(fileSize) + ')')
                else:
                    fileSize = -1
                    Logger.Debug('ByteSize is unknown')

                # check for encoding
                charSet = None
                try:
                    contentType = data.get('Content-Type')
                    if contentType:
                        Logger.Trace("Found Content-Type header: %s",
                                     contentType)
                        charSetNeedle = 'charset='
                        charSetIndex = contentType.rfind(charSetNeedle)
                        if charSetIndex > 0:
                            charSet = contentType[charSetIndex +
                                                  len(charSetNeedle):]
                            Logger.Trace("Found Charset HTML Header: %s",
                                         charSet)
                except:
                    charSet = None

                blocksRead = 0
                while True:
                    block = srcHandle.read(blockSize)
                    if block == "":
                        break

                    destHandle.write(block)
                    blocksRead += 1

                    canceled = self.__DoCallback(progressCallback, blocksRead,
                                                 blockSize, fileSize, False)
                    if canceled:
                        break

                    if time.time() > startTime + timeOutValue:
                        timeOut = True
                        break

                    if 0 < maxBytes < blocksRead * blockSize:
                        Logger.Info(
                            'Stopping download because Bytes > maxBytes')
                        break

                srcHandle.close()

            except (IncompleteRead, ValueError):
                # Python 2.6 throws a IncompleteRead on Chuncked data
                # Python 2.4 throws a ValueError on Chuncked data
                Logger.Error("IncompleteRead error opening url %s", uri)
                try:
                    if srcHandle:
                        srcHandle.close()
                except UnboundLocalError:
                    pass

            except:
                Logger.Critical("Error Opening url %s", uri, exc_info=True)
                error = True
                try:
                    if srcHandle:
                        srcHandle.close()
                except UnboundLocalError:
                    pass

            # we are finished now
            self.__DoCallback(progressCallback, blocksRead, blockSize,
                              fileSize, True)

            if timeOut:
                Logger.Critical(
                    "The URL lookup did not respond within the TimeOut (%s s)",
                    timeOutValue)
            elif canceled:
                Logger.Warning("Opening of %s was canceled", uri)
            elif not error:
                Logger.Info("Url %s was opened successfully", uri)

            return error, canceled, charSet
コード例 #16
0
    def UpdateAddOnSettingsWithChannels(channels, config):
        """ updats the settings.xml to include all the channels


        Arguments:
        channels : List<channels> - The channels to add to the settings.xml
        config   : Config         - The configuration object

        """

        # sort the channels
        channels.sort()

        # Then we read the original file
        filenameTemplate = os.path.join(config.rootDir, "resources",
                                        "settings_template.xml")
        # noinspection PyArgumentEqualDefault
        settingsXml = open(filenameTemplate, "r")
        contents = settingsXml.read()
        settingsXml.close()

        newContents = AddonSettings.__UpdateAddOnSettingsWithLanguages(
            contents, channels)
        newContents = AddonSettings.__UpdateAddOnSettingsWithChannelSelection(
            newContents, channels)
        newContents, settingsOffsetForVisibility = \
            AddonSettings.__UpdateAddOnSettingsWithChannelSettings(newContents, channels)
        newContents = AddonSettings.__UpdateAddOnSettingsWithProxies(
            newContents, channels, settingsOffsetForVisibility)

        # Now fill the templates, we only import here due to performance penalties of the
        # large number of imports.
        from helpers.templatehelper import TemplateHelper
        th = TemplateHelper(Logger.Instance(), template=newContents)
        newContents = th.Transform()

        # No more spoofing or proxies
        newContents = newContents.replace('<!-- start of proxy selection -->',
                                          '<!-- start of proxy selection')
        newContents = newContents.replace('<!-- end of proxy selection -->',
                                          'end of proxy selection -->')
        newContents = newContents.replace('<!-- start of proxy settings -->',
                                          '<!-- start of proxy settings')
        newContents = newContents.replace('<!-- end of proxy settings -->',
                                          'end of proxy settings -->')

        # Finally we insert the new XML into the old one
        filename = os.path.join(config.rootDir, "resources", "settings.xml")
        filenameTemp = os.path.join(config.rootDir, "resources",
                                    "settings.tmp.xml")
        try:
            # Backup the user profile settings.xml because sometimes it gets reset. Because in some
            # concurrency situations, Kodi might decide to think we have no settings and just
            # erase all user settings.
            userSettings = os.path.join(Config.profileDir, "settings.xml")
            userSettingsBackup = os.path.join(Config.profileDir,
                                              "settings.old.xml")
            Logger.Debug("Backing-up user settings: %s", userSettingsBackup)
            if os.path.isfile(userSettings):
                shutil.copy(userSettings, userSettingsBackup)
            else:
                Logger.Warning("No user settings found at: %s", userSettings)

            # Update the addonsettings.xml by first updating a temp xml file.
            Logger.Debug("Creating new settings.xml file: %s", filenameTemp)
            Logger.Trace(newContents)
            settingsXml = open(filenameTemp, "w+")
            settingsXml.write(newContents)
            settingsXml.close()
            Logger.Debug("Replacing existing settings.xml file: %s", filename)
            shutil.move(filenameTemp, filename)

            # restore the user profile settings.xml file when needed
            if os.path.isfile(
                    userSettings) and os.stat(userSettings).st_size != os.stat(
                        userSettingsBackup).st_size:
                Logger.Critical(
                    "User settings.xml was overwritten during setttings update. Restoring from %s",
                    userSettingsBackup)
                shutil.copy(userSettingsBackup, userSettings)
        except:
            Logger.Error(
                "Something went wrong trying to update the settings.xml",
                exc_info=True)
            try:
                settingsXml.close()
            except:
                pass

            #  clean up time file
            if os.path.isfile(filenameTemp):
                os.remove(filenameTemp)

            # restore original settings
            settingsXml = open(filenameTemp, "w+")
            settingsXml.write(contents)
            settingsXml.close()
            shutil.move(filenameTemp, filename)
            return

        Logger.Info("Settings.xml updated succesfully. Reloading settings.")
        AddonSettings.__LoadSettings()
        return
コード例 #17
0
    def PlayVideoItem(self):
        """Starts the videoitem using a playlist. """

        Logger.Debug("Playing videoitem using PlayListMethod")

        item = None
        try:
            item = Pickler.DePickleMediaItem(self.params[self.keywordPickle])

            if item.isDrmProtected and AddonSettings.ShowDrmWarning():
                Logger.Debug("Showing DRM Warning message")
                title = LanguageHelper.GetLocalizedString(LanguageHelper.DrmTitle)
                message = LanguageHelper.GetLocalizedString(LanguageHelper.DrmText)
                XbmcWrapper.ShowDialog(title, message)
            elif item.isDrmProtected:
                Logger.Debug("DRM Warning message disabled by settings")

            if not item.complete:
                item = self.channelObject.ProcessVideoItem(item)

            # validated the updated item
            if not item.complete or not item.HasMediaItemParts():
                Logger.Warning("UpdateVideoItem returned an item that had item.complete = False:\n%s", item)
                Statistics.RegisterError(self.channelObject, item=item)

            if not item.HasMediaItemParts():
                # the update failed or no items where found. Don't play
                XbmcWrapper.ShowNotification(LanguageHelper.GetLocalizedString(LanguageHelper.ErrorId),
                                             LanguageHelper.GetLocalizedString(LanguageHelper.NoStreamsId),
                                             XbmcWrapper.Error)
                Logger.Warning("Could not start playback due to missing streams. Item:\n%s", item)
                return

            playData = self.channelObject.PlayVideoItem(item)

            Logger.Debug("Continuing playback in plugin.py")
            if not playData:
                Logger.Warning("PlayVideoItem did not return valid playdata")
                return
            else:
                playList, srt = playData

            # Get the Kodi Player instance (let Kodi decide what player, see
            # http://forum.kodi.tv/showthread.php?tid=173887&pid=1516662#pid1516662)
            xbmcPlayer = xbmc.Player()

            # now we force the busy dialog to close, else the video will not play and the
            # setResolved will not work.
            xbmc.executebuiltin("Dialog.Close(busydialog)")

            resolvedUrl = None
            if item.IsResolvable():
                # now set the resolve to the first URL
                startIndex = playList.getposition()  # the current location
                if startIndex < 0:
                    startIndex = 0
                Logger.Info("Playing stream @ playlist index %s using setResolvedUrl method", startIndex)
                resolvedUrl = playList[startIndex].getfilename()
                xbmcplugin.setResolvedUrl(self.handle, True, playList[startIndex])
            else:
                # playlist do not use the setResolvedUrl
                Logger.Info("Playing stream using Playlist method")
                xbmcPlayer.play(playList)

            # the set the subtitles
            showSubs = AddonSettings.UseSubtitle()
            if srt and (srt != ""):
                Logger.Info("Adding subtitle: %s and setting showSubtitles to %s", srt, showSubs)
                XbmcWrapper.WaitForPlayerToStart(xbmcPlayer, logger=Logger.Instance(), url=resolvedUrl)

                xbmcPlayer.setSubtitles(srt)
                xbmcPlayer.showSubtitles(showSubs)

        except:
            if item:
                Statistics.RegisterError(self.channelObject, item=item)
            else:
                Statistics.RegisterError(self.channelObject)

            XbmcWrapper.ShowNotification(LanguageHelper.GetLocalizedString(LanguageHelper.ErrorId),
                                         LanguageHelper.GetLocalizedString(LanguageHelper.NoPlaybackId),
                                         XbmcWrapper.Error)
            Logger.Critical("Could not playback the url", exc_info=True)

        return
コード例 #18
0
    def __init__(self, pluginName, params, handle=0):
        """Initialises the plugin with given arguments."""

        # some constants
        self.actionDownloadVideo = "downloadVideo".lower()              # : Action used to download a video item
        self.actionFavourites = "favourites".lower()                    # : Action used to show favorites for a channel
        self.actionAllFavourites = "allfavourites".lower()              # : Action used to show all favorites
        self.actionRemoveFavourite = "removefromfavourites".lower()     # : Action used to remove items from favorites
        self.actionAddFavourite = "addtofavourites".lower()             # : Action used to add items to favorites
        self.actionPlayVideo = "playvideo".lower()                      # : Action used to play a video item
        self.actionUpdateChannels = "updatechannels".lower()            # : Action used to update channels
        self.actionListFolder = "listfolder".lower()                    # : Action used to list a folder
        self.actionListCategory = "listcategory"                        # : Action used to show the channels from a category
        self.actionConfigureChannel = "configurechannel"                # : Action used to configure a channel

        self.keywordPickle = "pickle".lower()                           # : Keyword used for the pickle item
        self.keywordAction = "action".lower()                           # : Keyword used for the action item
        self.keywordChannel = "channel".lower()                         # : Keyword used for the channel
        self.keywordChannelCode = "channelcode".lower()                 # : Keyword used for the channelcode
        self.keywordCategory = "category"                               # : Keyword used for the category
        self.keywordRandomLive = "rnd"                                  # : Keyword used for randomizing live items

        self.pluginName = pluginName
        self.handle = int(handle)

        # channel objects
        self.channelObject = None
        self.channelFile = ""
        self.channelCode = None

        self.contentType = "episodes"
        self.methodContainer = dict()   # : storage for the inspect.getmembers(channel) method. Improves performance

        # determine the query parameters
        self.params = self.__GetParameters(params)

        Logger.Info("*********** Starting %s add-on version %s ***********", Config.appName, Config.version)
        Logger.Debug("Plugin Params: %s (%s) [handle=%s, name=%s, query=%s]", self.params, len(self.params),
                     self.handle, self.pluginName, params)

        # are we in session?
        sessionActive = SessionHelper.IsSessionActive(Logger.Instance())

        # fetch some environment settings
        envCtrl = envcontroller.EnvController(Logger.Instance())
        # self.FavouritesEnabled = envCtrl.SQLiteEnabled()
        self.FavouritesEnabled = not envCtrl.IsPlatform(Environments.Xbox)

        if not sessionActive:
            # do add-on start stuff
            Logger.Info("Add-On start detected. Performing startup actions.")

            # print the folder structure
            envCtrl.DirectoryPrinter(Config, AddonSettings)

            # show notification
            XbmcWrapper.ShowNotification(None, LanguageHelper.GetLocalizedString(LanguageHelper.StartingAddonId) % (
                Config.appName,), fallback=False, logger=Logger)

            # check for updates
            if envCtrl.IsPlatform(Environments.Xbox):
                Updater().AutoUpdate()

            # check if the repository is available
            envCtrl.IsInstallMethodValid(Config)

            # check for cache folder
            envCtrl.CacheCheck()

            # do some cache cleanup
            envCtrl.CacheCleanUp(Config.cacheDir, Config.cacheValidTime)
            envCtrl.CacheCleanUp(AddonSettings.GetUzgCachePath(), AddonSettings.GetUzgCacheDuration() * 24 * 3600,
                                 "xot.*")

        # create a session
        SessionHelper.CreateSession(Logger.Instance())

        #===============================================================================
        #        Start the plugin version of progwindow
        #===============================================================================
        if len(self.params) == 0:

            # Show initial start if not in a session
            # now show the list
            if AddonSettings.ShowCategories():
                self.ShowCategories()
            else:
                self.ShowChannelList()

        #===============================================================================
        #        Start the plugin verion of the episode window
        #===============================================================================
        else:
            try:
                # Determine what stage we are in. Check that there are more than 2 Parameters
                if len(self.params) > 1 and self.keywordChannel in self.params:
                    # retrieve channel characteristics
                    self.channelFile = os.path.splitext(self.params[self.keywordChannel])[0]
                    self.channelCode = self.params[self.keywordChannelCode]
                    Logger.Debug("Found Channel data in URL: channel='%s', code='%s'", self.channelFile,
                                 self.channelCode)

                    # import the channel
                    channelRegister = ChannelImporter.GetRegister()
                    channel = channelRegister.GetSingleChannel(self.channelFile, self.channelCode)

                    if channel is not None:
                        self.channelObject = channel
                    else:
                        Logger.Critical("None or more than one channels were found, unable to continue.")
                        return

                    # init the channel as plugin
                    self.channelObject.InitChannel()
                    Logger.Info("Loaded: %s", self.channelObject.channelName)

                elif self.keywordCategory in self.params:
                    # no channel needed.
                    pass

                elif self.keywordAction in self.params and (
                        self.params[self.keywordAction] == self.actionAllFavourites or
                        self.params[self.keywordAction] == self.actionRemoveFavourite):
                    # no channel needed for these favourites actions.
                    pass

                else:
                    Logger.Critical("Error determining Plugin action")
                    return

                #===============================================================================
                # See what needs to be done.
                #===============================================================================
                if self.keywordAction not in self.params:
                    Logger.Critical("Action parameters missing from request. Parameters=%s", self.params)
                    return

                if self.params[self.keywordAction] == self.actionListCategory:
                    self.ShowChannelList(self.params[self.keywordCategory])

                elif self.params[self.keywordAction] == self.actionConfigureChannel:
                    self.__ConfigureChannel(self.channelObject)

                elif self.params[self.keywordAction] == self.actionFavourites:
                    # we should show the favourites
                    self.ShowFavourites(self.channelObject)

                elif self.params[self.keywordAction] == self.actionAllFavourites:
                    if self.channelObject is not None:
                        Logger.Warning("We have a self.channelObject with self.actionAllFavourites")
                    self.ShowFavourites(None)

                elif self.params[self.keywordAction] == self.actionRemoveFavourite:
                    self.RemoveFavourite()

                elif self.params[self.keywordAction] == self.actionAddFavourite:
                    self.AddFavourite()

                elif self.params[self.keywordAction] == self.actionListFolder:
                    # channelName and URL is present, Parse the folder
                    self.ProcessFolderList()

                elif self.params[self.keywordAction] == self.actionPlayVideo:
                    self.PlayVideoItem()

                elif not self.params[self.keywordAction] == "":
                    self.OnActionFromContextMenu(self.params[self.keywordAction])

                else:
                    Logger.Warning("Number of parameters (%s) or parameter (%s) values not implemented",
                                   len(self.params), self.params)

            except:
                Logger.Critical("Error parsing for add-on", exc_info=True)

        self.__FetchTextures()
        return
コード例 #19
0
    def __ImportChannels(self):  # , className = None):
        """Import the available channels

        This method will:
         - iterate through the Addons folder and find all the folders name
           <basename>.channel.<channelname>.
         - then adds all the subfolders into a list (with paths).
         - then all paths are added to the system path, so they can be imported.
         - then read all the chn_<name>.xml metadata files and add the ChannelInfo
           objects to the self.__channelsToImport
         - then the channels in the self.__channelsToImport list are instantiated
           into the self.channels list.

        """

        Logger.Debug("Importing available channels")
        # import each channelPath. On import, the channelPath will call the RegisterChannel Method
        try:
            # clear a possible previous import
            self.__enabledChannels = []
            self.__allChannels = []
            self.__validChannels = []
            self.__channelVersions = []

            # first find all folders with channels that we might need to import
            channelImport = []
            importTimer = StopWatch("ChannelImporter :: importing channels", Logger.Instance())

            addonPath = self.__GetAddonPath()

            channelPathStart = "%s.channel" % (Config.addonDir,)
            for directory in os.listdir(addonPath):
                if channelPathStart in directory and "BUILD" not in directory:
                    path = os.path.join(addonPath, directory)

                    channelVersion = self.__ParseChannelVersionInfo(path)
                    if channelVersion:
                        self.__channelVersions.append(channelVersion)
                    else:
                        # no info was returned, so we will not include the channel
                        continue

                    # get all nested channels
                    subDirs = os.listdir(path)
                    channelImport.extend(
                        [os.path.abspath(os.path.join(path, weapon)) for weapon in subDirs])

            channelImport.sort()
            importTimer.Lap("Directories scanned for .channel")

            # we need to make sure we don't load multiple channel classes and track if we found updates
            channelsUpdated = False
            loadedChannels = []
            channelsToImport = []

            # now import the channels
            for channelPath in channelImport:
                if not os.path.isdir(channelPath):
                    continue

                # determine channelname
                channelName = os.path.split(channelPath)[-1]
                if channelName == self.__updateChannelPath:
                    Logger.Trace("Update path found and skipping: %s", channelName)
                    continue

                # if loadedChannels.count(channelName) > 0:
                if channelName in loadedChannels:
                    Logger.Warning(
                        "Not loading: chn_%s.xml in %s because there is already a path with "
                        "name '%s' that name loaded", channelName, channelPath, channelName)
                    continue

                if channelName.startswith("."):
                    continue

                # now we can continue
                loadedChannels.append(channelName)

                fileName = os.path.join(channelPath, "chn_" + channelName + ".json")
                Logger.Trace("Loading info for chn_%s @ %s", channelName, fileName)
                if os.path.isfile(fileName):
                    try:
                        ci = ChannelInfo.FromJson(fileName)
                        if len(ci) <= 0:
                            Logger.Warning("No channels found in '%s'", fileName)
                            continue

                        # Add them to the list to import
                        channelsToImport += ci

                        if self.__IsChannelSetUpdated(ci[0]):
                            if not channelsUpdated:
                                # this was the first update found (otherwise channelsUpdated was True) show a message:
                                title = LanguageHelper.GetLocalizedString(
                                    LanguageHelper.InitChannelTitle)
                                text = LanguageHelper.GetLocalizedString(
                                    LanguageHelper.InitChannelText)
                                XbmcWrapper.ShowNotification(title, text, displayTime=15000)

                            # set the updates found bit
                            channelsUpdated |= True

                            # Initialise the channelset.
                            self.__InitialiseChannelSet(ci[0])

                            # And perform all first actions for the included channels in the set
                            for channelInfo in ci:
                                self.__FirstTimeChannelActions(channelInfo)
                    except:
                        Logger.Error("Error import chn_%s.json", channelName, exc_info=True)

            importTimer.Lap()

            # What platform are we
            platform = envcontroller.EnvController.GetPlatform()

            # instantiate the registered channels
            for channelInfo in channelsToImport:
                # noinspection PyUnusedLocal
                isValid = self.__ValidateChannelInfo(channelInfo, platform)

            # sort the channels
            self.__enabledChannels.sort()

            if channelsUpdated:
                Logger.Info("New or updated channels found. Updating add-on configuration for all "
                            "channels and user agent")
                AddonSettings.UpdateAddOnSettingsWithChannels(self.__validChannels, Config)
                AddonSettings.UpdateUserAgent()
            else:
                Logger.Debug("No channel changes found. Skipping add-on configuration for channels")

            # Should we update the channel index?
            if channelsUpdated or not os.path.isfile(self.__CHANNEL_INDEX):
                self.__CreateChannelIndex()

            Logger.Info("Imported %s channels from which %s are enabled",
                        len(self.__allChannels), len(self.__enabledChannels))
            importTimer.Stop()
        except:
            Logger.Critical("Error loading channel modules", exc_info=True)