diff options
Diffstat (limited to 'pyload/api')
| -rw-r--r-- | pyload/api/AccountApi.py | 79 | ||||
| -rw-r--r-- | pyload/api/AddonApi.py | 27 | ||||
| -rw-r--r-- | pyload/api/ApiComponent.py | 23 | ||||
| -rw-r--r-- | pyload/api/ConfigApi.py | 135 | ||||
| -rw-r--r-- | pyload/api/CoreApi.py | 131 | ||||
| -rw-r--r-- | pyload/api/DownloadApi.py | 171 | ||||
| -rw-r--r-- | pyload/api/DownloadPreparingApi.py | 105 | ||||
| -rw-r--r-- | pyload/api/FileApi.py | 167 | ||||
| -rw-r--r-- | pyload/api/UserApi.py | 41 | ||||
| -rw-r--r-- | pyload/api/UserInteractionApi.py | 61 | ||||
| -rw-r--r-- | pyload/api/__init__.py | 8 | 
11 files changed, 948 insertions, 0 deletions
diff --git a/pyload/api/AccountApi.py b/pyload/api/AccountApi.py new file mode 100644 index 000000000..d4b39c12b --- /dev/null +++ b/pyload/api/AccountApi.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from pyload.utils import to_bool +from pyload.Api import Api, RequirePerm, Permission, Conflict +from ApiComponent import ApiComponent + + +class AccountApi(ApiComponent): +    """ All methods to control accounts """ + +    @RequirePerm(Permission.All) +    def getAccountTypes(self): +        """All available account types. + +        :return: string list +        """ +        return self.core.pluginManager.getPlugins("accounts").keys() + +    @RequirePerm(Permission.Accounts) +    def getAccounts(self): +        """Get information about all entered accounts. + +        :return: list of `AccountInfo` +        """ +        accounts = self.core.accountManager.getAllAccounts(self.primaryUID) +        return [acc.toInfoData() for acc in accounts] + +    @RequirePerm(Permission.Accounts) +    def getAccountInfo(self, plugin, loginname, refresh=False): +        """ Returns :class:`AccountInfo` for a specific account + +            :param refresh: reload account info +        """ +        account = self.core.accountManager.getAccount(plugin, loginname) + +        # Admins can see and refresh accounts +        if not account or (self.primaryUID and self.primaryUID != account.owner): +            return None + +        if refresh: +            # reload account in place +            account.getAccountInfo(True) + +        return account.toInfoData() + +    @RequirePerm(Permission.Accounts) +    def updateAccount(self, plugin, loginname, password): +        """Creates an account if not existent or updates the password + +        :return: newly created or updated account info +        """ +        # TODO: None pointer +        return self.core.accountManager.updateAccount(plugin, loginname, password, self.user).toInfoData() + + +    @RequirePerm(Permission.Accounts) +    def updateAccountInfo(self, account): +        """ Update account settings from :class:`AccountInfo` """ +        inst = self.core.accountManager.getAccount(account.plugin, account.loginname, self.user) +        if not account: +            return + +        inst.activated = to_bool(account.activated) +        inst.shared = to_bool(account.shared) +        inst.updateConfig(account.config) + + +    @RequirePerm(Permission.Accounts) +    def removeAccount(self, account): +        """Remove account from pyload. + +        :param account: :class:`ÀccountInfo` instance +        """ +        self.core.accountManager.removeAccount(account.plugin, account.loginname, self.primaryUID) + + +if Api.extend(AccountApi): +    del AccountApi
\ No newline at end of file diff --git a/pyload/api/AddonApi.py b/pyload/api/AddonApi.py new file mode 100644 index 000000000..4ae686d2d --- /dev/null +++ b/pyload/api/AddonApi.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from pyload.Api import Api, RequirePerm, Permission + +from ApiComponent import ApiComponent + +class AddonApi(ApiComponent): +    """ Methods to interact with addons """ + +    def getAllInfo(self): +        """Returns all information stored by addon plugins. Values are always strings + +        :return: {"plugin": {"name": value } } +        """ +        return self.core.addonManager.getAllInfo() + +    def getInfoByPlugin(self, plugin): +        """Returns information stored by a specific plugin. + +        :param plugin: pluginname +        :return: dict of attr names mapped to value {"name": value} +        """ +        return self.core.addonManager.getInfo(plugin) + +if Api.extend(AddonApi): +    del AddonApi
\ No newline at end of file diff --git a/pyload/api/ApiComponent.py b/pyload/api/ApiComponent.py new file mode 100644 index 000000000..bb333c259 --- /dev/null +++ b/pyload/api/ApiComponent.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from pyload.remote.apitypes import Iface + +# Workaround to let code-completion think, this is subclass of Iface +Iface = object +class ApiComponent(Iface): + +    __slots__ = [] + +    def __init__(self, core, user): +        # Only for auto completion, this class can not be instantiated +        from pyload import Core +        from pyload.datatypes.User import User +        assert isinstance(core, Core) +        assert issubclass(ApiComponent, Iface) +        self.core = core +        assert isinstance(user, User) +        self.user = user +        self.primaryUID = 0 +        # No instantiating! +        raise Exception()
\ No newline at end of file diff --git a/pyload/api/ConfigApi.py b/pyload/api/ConfigApi.py new file mode 100644 index 000000000..2adc0c565 --- /dev/null +++ b/pyload/api/ConfigApi.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from pyload.Api import Api, RequirePerm, Permission, ConfigHolder, ConfigItem, ConfigInfo +from pyload.utils import to_string + +from ApiComponent import ApiComponent + +# helper function to create a ConfigHolder +def toConfigHolder(section, config, values): +    holder = ConfigHolder(section, config.label, config.description, config.explanation) +    holder.items = [ConfigItem(option, x.label, x.description, x.input, +                               to_string(values.get(option, x.input.default_value))) for option, x in +                    config.config.iteritems()] +    return holder + + +class ConfigApi(ApiComponent): +    """ Everything related to configuration """ + +    def getConfigValue(self, section, option): +        """Retrieve config value. + +        :param section: name of category, or plugin +        :param option: config option +        :rtype: str +        :return: config value as string +        """ +        value = self.core.config.get(section, option, self.primaryUID) +        return to_string(value) + +    def setConfigValue(self, section, option, value): +        """Set new config value. + +        :param section: +        :param option: +        :param value: new config value +        """ +        if option in ("limit_speed", "max_speed"): #not so nice to update the limit +            self.core.requestFactory.updateBucket() + +        self.core.config.set(section, option, value, self.primaryUID) + +    def getConfig(self): +        """Retrieves complete config of core. + +        :rtype: dict of section -> ConfigHolder +        """ +        data = {} +        for section, config, values in self.core.config.iterCoreSections(): +            data[section] = toConfigHolder(section, config, values) +        return data + +    def getCoreConfig(self): +        """ Retrieves core config sections + +        :rtype: list of PluginInfo +        """ +        return [ConfigInfo(section, config.label, config.description, False, False) +                for section, config, values in self.core.config.iterCoreSections()] + +    @RequirePerm(Permission.Plugins) +    def getPluginConfig(self): +        """All plugins and addons the current user has configured + +        :rtype: list of PluginInfo +        """ +        # TODO: include addons that are activated by default +        # TODO: multi user +        # TODO: better plugin / addon activated config +        data = [] +        active = [x.getName() for x in self.core.addonManager.activePlugins()] +        for name, config, values in self.core.config.iterSections(self.primaryUID): +            # skip unmodified and inactive addons +            if not values and name not in active: continue + +            item = ConfigInfo(name, config.label, config.description, +                              self.core.pluginManager.getCategory(name), +                              self.core.pluginManager.isUserPlugin(name), +                              # TODO: won't work probably +                              values.get("activated", None if "activated" not in config.config else config.config[ +                                  "activated"].input.default_value)) +            data.append(item) + +        return data + +    @RequirePerm(Permission.Plugins) +    def getAvailablePlugins(self): +        """List of all available plugins, that are configurable + +        :rtype: list of PluginInfo +        """ +        # TODO: filter user_context / addons when not allowed +        plugins = [ConfigInfo(name, config.label, config.description, +                              self.core.pluginManager.getCategory(name), +                              self.core.pluginManager.isUserPlugin(name)) +                   for name, config, values in self.core.config.iterSections(self.primaryUID)] + +        return plugins + +    @RequirePerm(Permission.Plugins) +    def loadConfig(self, name): +        """Get complete config options for desired section + +        :param name: Name of plugin or config section +        :rtype: ConfigHolder +        """ +        # requires at least plugin permissions, but only admin can load core config +        config, values = self.core.config.getSection(name, self.primaryUID) +        return toConfigHolder(name, config, values) + + +    @RequirePerm(Permission.Plugins) +    def saveConfig(self, config): +        """Used to save a configuration, core config can only be saved by admins + +        :param config: :class:`ConfigHolder` +        """ +        for item in config.items: +            self.core.config.set(config.name, item.name, item.value, sync=False, user=self.primaryUID) +            # save the changes +        self.core.config.saveValues(self.primaryUID, config.name) + +    @RequirePerm(Permission.Plugins) +    def deleteConfig(self, plugin): +        """Deletes modified config + +        :param plugin: plugin name +        """ +        #TODO: delete should deactivate addons? +        self.core.config.delete(plugin, self.primaryUID) + + +if Api.extend(ConfigApi): +    del ConfigApi
\ No newline at end of file diff --git a/pyload/api/CoreApi.py b/pyload/api/CoreApi.py new file mode 100644 index 000000000..ebb194134 --- /dev/null +++ b/pyload/api/CoreApi.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from pyload.Api import Api, RequirePerm, Permission, ServerStatus, Interaction +from pyload.utils.fs import join, free_space +from pyload.utils import compare_time + +from ApiComponent import ApiComponent + +class CoreApi(ApiComponent): +    """ This module provides methods for general interaction with the core, like status or progress retrieval  """ + +    @RequirePerm(Permission.All) +    def getServerVersion(self): +        """pyLoad Core version """ +        return self.core.version + +    @RequirePerm(Permission.All) +    def getWSAddress(self): +        """Gets and address for the websocket based on configuration""" +        # TODO SSL (wss) +        return "ws://%%s:%d" % self.core.config['remote']['port'] + +    @RequirePerm(Permission.All) +    def getServerStatus(self): +        """Some general information about the current status of pyLoad. + +        :return: `ServerStatus` +        """ +        queue = self.core.files.getQueueStats(self.primaryUID) +        total = self.core.files.getDownloadStats(self.primaryUID) + +        serverStatus = ServerStatus(0, +                                    total[0], queue[0], +                                    total[1], queue[1], +                                    self.isInteractionWaiting(Interaction.All), +                                    not self.core.threadManager.pause and self.isTimeDownload(), +                                    self.core.threadManager.pause, +                                    self.core.config['reconnect']['activated'] and self.isTimeReconnect()) + + +        for pyfile in self.core.threadManager.getActiveDownloads(self.primaryUID): +            serverStatus.speed += pyfile.getSpeed() #bytes/s + +        return serverStatus + +    @RequirePerm(Permission.All) +    def getProgressInfo(self): +        """ Status of all currently running tasks + +        :rtype: list of :class:`ProgressInfo` +        """ +        return self.core.threadManager.getProgressList(self.primaryUID) + +    def pauseServer(self): +        """Pause server: It won't start any new downloads, but nothing gets aborted.""" +        self.core.threadManager.pause = True + +    def unpauseServer(self): +        """Unpause server: New Downloads will be started.""" +        self.core.threadManager.pause = False + +    def togglePause(self): +        """Toggle pause state. + +        :return: new pause state +        """ +        self.core.threadManager.pause ^= True +        return self.core.threadManager.pause + +    def toggleReconnect(self): +        """Toggle reconnect activation. + +        :return: new reconnect state +        """ +        self.core.config["reconnect"]["activated"] ^= True +        return self.core.config["reconnect"]["activated"] + +    def freeSpace(self): +        """Available free space at download directory in bytes""" +        return free_space(self.core.config["general"]["download_folder"]) + + +    def quit(self): +        """Clean way to quit pyLoad""" +        self.core.do_kill = True + +    def restart(self): +        """Restart pyload core""" +        self.core.do_restart = True + +    def getLog(self, offset=0): +        """Returns most recent log entries. + +        :param offset: line offset +        :return: List of log entries +        """ +        filename = join(self.core.config['log']['log_folder'], 'log.txt') +        try: +            fh = open(filename, "r") +            lines = fh.readlines() +            fh.close() +            if offset >= len(lines): +                return [] +            return lines[offset:] +        except: +            return ['No log available'] + +    @RequirePerm(Permission.All) +    def isTimeDownload(self): +        """Checks if pyload will start new downloads according to time in config. + +        :return: bool +        """ +        start = self.core.config['downloadTime']['start'].split(":") +        end = self.core.config['downloadTime']['end'].split(":") +        return compare_time(start, end) + +    @RequirePerm(Permission.All) +    def isTimeReconnect(self): +        """Checks if pyload will try to make a reconnect + +        :return: bool +        """ +        start = self.core.config['reconnect']['startTime'].split(":") +        end = self.core.config['reconnect']['endTime'].split(":") +        return compare_time(start, end) and self.core.config["reconnect"]["activated"] + + +if Api.extend(CoreApi): +    del CoreApi
\ No newline at end of file diff --git a/pyload/api/DownloadApi.py b/pyload/api/DownloadApi.py new file mode 100644 index 000000000..d855dd882 --- /dev/null +++ b/pyload/api/DownloadApi.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from os.path import isabs + +from pyload.Api import Api, RequirePerm, Permission +from pyload.utils.fs import join + +from ApiComponent import ApiComponent + +class DownloadApi(ApiComponent): +    """ Component to create, add, delete or modify downloads.""" + +    @RequirePerm(Permission.Add) +    def createPackage(self, name, folder, root, password="", site="", comment="", paused=False): +        """Create a new package. + +        :param name: display name of the package +        :param folder: folder name or relative path, abs path are not allowed +        :param root: package id of root package, -1 for top level package +        :param password: single pw or list of passwords separated with new line +        :param site: arbitrary url to site for more information +        :param comment: arbitrary comment +        :param paused: No downloads will be started when True +        :return: pid of newly created package +        """ + +        if isabs(folder): +            folder = folder.replace("/", "_") + +        folder = folder.replace("http://", "").replace(":", "").replace("\\", "_").replace("..", "") + +        self.core.log.info(_("Added package %(name)s as folder %(folder)s") % {"name": name, "folder": folder}) +        pid = self.core.files.addPackage(name, folder, root, password, site, comment, paused) + +        return pid + + +    @RequirePerm(Permission.Add) +    def addPackage(self, name, links, password=""): +        """Convenient method to add a package to the top-level and for adding links. + +        :return: package id +        """ +        return self.addPackageChild(name, links, password, -1, False) + +    @RequirePerm(Permission.Add) +    def addPackageP(self, name, links, password, paused): +        """ Same as above with additional paused attribute. """ +        return self.addPackageChild(name, links, password, -1, paused) + +    @RequirePerm(Permission.Add) +    def addPackageChild(self, name, links, password, root, paused): +        """Adds a package, with links to desired package. + +        :param root: parents package id +        :return: package id of the new package +        """ +        if self.core.config['general']['folder_per_package']: +            folder = name +        else: +            folder = "" + +        pid = self.createPackage(name, folder, root, password) +        self.addLinks(pid, links) + +        return pid + +    @RequirePerm(Permission.Add) +    def addLinks(self, pid, links): +        """Adds links to specific package. Initiates online status fetching. + +        :param pid: package id +        :param links: list of urls +        """ +        hoster, crypter = self.core.pluginManager.parseUrls(links) + +        if hoster: +            self.core.files.addLinks(hoster, pid) +            self.core.threadManager.createInfoThread(hoster, pid) + +        self.core.threadManager.createDecryptThread(crypter, pid) + +        self.core.log.info((_("Added %d links to package") + " #%d" % pid) % len(hoster)) +        self.core.files.save() + +    @RequirePerm(Permission.Add) +    def uploadContainer(self, filename, data): +        """Uploads and adds a container file to pyLoad. + +        :param filename: filename, extension is important so it can correctly decrypted +        :param data: file content +        """ +        th = open(join(self.core.config["general"]["download_folder"], "tmp_" + filename), "wb") +        th.write(str(data)) +        th.close() + +        return self.addPackage(th.name, [th.name]) + +    @RequirePerm(Permission.Delete) +    def deleteFiles(self, fids): +        """Deletes several file entries from pyload. + +        :param fids: list of file ids +        """ +        for fid in fids: +            self.core.files.deleteFile(fid) + +        self.core.files.save() + +    @RequirePerm(Permission.Delete) +    def deletePackages(self, pids): +        """Deletes packages and containing links. + +        :param pids: list of package ids +        """ +        for pid in pids: +            self.core.files.deletePackage(pid) + +        self.core.files.save() + + +    @RequirePerm(Permission.Modify) +    def restartPackage(self, pid): +        """Restarts a package, resets every containing files. + +        :param pid: package id +        """ +        self.core.files.restartPackage(pid) + +    @RequirePerm(Permission.Modify) +    def restartFile(self, fid): +        """Resets file status, so it will be downloaded again. + +        :param fid: file id +        """ +        self.core.files.restartFile(fid) + +    @RequirePerm(Permission.Modify) +    def recheckPackage(self, pid): +        """Check online status of all files in a package, also a default action when package is added. """ +        self.core.files.reCheckPackage(pid) + +    @RequirePerm(Permission.Modify) +    def restartFailed(self): +        """Restarts all failed failes.""" +        self.core.files.restartFailed() + +    @RequirePerm(Permission.Modify) +    def stopAllDownloads(self): +        """Aborts all running downloads.""" + +        pyfiles = self.core.files.cachedFiles() +        for pyfile in pyfiles: +            pyfile.abortDownload() + +    @RequirePerm(Permission.Modify) +    def stopDownloads(self, fids): +        """Aborts specific downloads. + +        :param fids: list of file ids +        :return: +        """ +        pyfiles = self.core.files.cachedFiles() +        for pyfile in pyfiles: +            if pyfile.id in fids: +                pyfile.abortDownload() + + +if Api.extend(DownloadApi): +    del DownloadApi
\ No newline at end of file diff --git a/pyload/api/DownloadPreparingApi.py b/pyload/api/DownloadPreparingApi.py new file mode 100644 index 000000000..a7e32c4eb --- /dev/null +++ b/pyload/api/DownloadPreparingApi.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from itertools import chain + +from pyload.Api import Api, DownloadStatus as DS,\ +    RequirePerm, Permission, OnlineCheck, LinkStatus, urlmatcher +from pyload.utils import uniqify +from pyload.utils.fs import join +from pyload.utils.packagetools import parseNames +from pyload.network.RequestFactory import getURL + +from ApiComponent import ApiComponent + +class DownloadPreparingApi(ApiComponent): +    """ All kind of methods to parse links or retrieve online status """ + +    @RequirePerm(Permission.Add) +    def parseLinks(self, links): +        """ Gets urls and returns pluginname mapped to list of matching urls. + +        :param links: +        :return: {plugin: urls} +        """ +        data, crypter = self.core.pluginManager.parseUrls(links) +        plugins = {} + +        for url, plugin in chain(data, crypter): +            if plugin in plugins: +                plugins[plugin].append(url) +            else: +                plugins[plugin] = [url] + +        return plugins + +    @RequirePerm(Permission.Add) +    def checkLinks(self, links): +        """ initiates online status check, will also decrypt files. + +        :param links: +        :return: initial set of data as :class:`OnlineCheck` instance containing the result id +        """ +        hoster, crypter = self.core.pluginManager.parseUrls(links) + +        #: TODO: withhold crypter, derypt or add later +        # initial result does not contain the crypter links +        tmp = [(url, LinkStatus(url, url, -1, DS.Queued, pluginname)) for url, pluginname in hoster] +        data = parseNames(tmp) +        rid = self.core.threadManager.createResultThread(self.primaryUID, hoster + crypter) + +        return OnlineCheck(rid, data) + +    @RequirePerm(Permission.Add) +    def checkContainer(self, filename, data): +        """ checks online status of urls and a submitted container file + +        :param filename: name of the file +        :param data: file content +        :return: :class:`OnlineCheck` +        """ +        th = open(join(self.core.config["general"]["download_folder"], "tmp_" + filename), "wb") +        th.write(str(data)) +        th.close() +        return self.checkLinks([th.name]) + +    @RequirePerm(Permission.Add) +    def checkHTML(self, html, url): +        """Parses html content or any arbitrary text for links and returns result of `checkURLs` + +        :param html: html source +        :return: +        """ +        urls = [] +        if html: +            urls += [x[0] for x in urlmatcher.findall(html)] +        if url: +            page = getURL(url) +            urls += [x[0] for x in urlmatcher.findall(page)] + +        return self.checkLinks(uniqify(urls)) + +    @RequirePerm(Permission.Add) +    def pollResults(self, rid): +        """ Polls the result available for ResultID + +        :param rid: `ResultID` +        :return: `OnlineCheck`, if rid is -1 then there is no more data available +        """ +        result = self.core.threadManager.getInfoResult(rid) +        if result and result.owner == self.primaryUID: +            return result.toApiData() + +    @RequirePerm(Permission.Add) +    def generatePackages(self, links): +        """ Parses links, generates packages names from urls + +        :param links: list of urls +        :return: package names mapped to urls +        """ +        result = parseNames((x, x) for x in links) +        return result + + +if Api.extend(DownloadPreparingApi): +    del DownloadPreparingApi
\ No newline at end of file diff --git a/pyload/api/FileApi.py b/pyload/api/FileApi.py new file mode 100644 index 000000000..984729b8c --- /dev/null +++ b/pyload/api/FileApi.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from pyload.Api import Api, RequirePerm, Permission, DownloadState, PackageDoesNotExists, FileDoesNotExists +from pyload.utils import uniqify + +from ApiComponent import ApiComponent + +# TODO: user context +class FileApi(ApiComponent): +    """Everything related to available packages or files. Deleting, Modifying and so on.""" + +    @RequirePerm(Permission.All) +    def getAllFiles(self): +        """ same as `getFileTree` for toplevel root and full tree""" +        return self.getFileTree(-1, True) + +    @RequirePerm(Permission.All) +    def getFilteredFiles(self, state): +        """ same as `getFilteredFileTree` for toplevel root and full tree""" +        return self.getFilteredFileTree(-1, state, True) + +    @RequirePerm(Permission.All) +    def getFileTree(self, pid, full): +        """ Retrieve data for specific package. full=True will retrieve all data available +            and can result in greater delays. + +        :param pid: package id +        :param full: go down the complete tree or only the first layer +        :return: :class:`TreeCollection` +        """ +        return self.core.files.getTree(pid, full, DownloadState.All) + +    @RequirePerm(Permission.All) +    def getFilteredFileTree(self, pid, full, state): +        """ Same as `getFileTree` but only contains files with specific download state. + +        :param pid: package id +        :param full: go down the complete tree or only the first layer +        :param state: :class:`DownloadState`, the attributes used for filtering +        :return: :class:`TreeCollection` +        """ +        return self.core.files.getTree(pid, full, state) + +    @RequirePerm(Permission.All) +    def getPackageContent(self, pid): +        """  Only retrieve content of a specific package. see `getFileTree`""" +        return self.getFileTree(pid, False) + +    @RequirePerm(Permission.All) +    def getPackageInfo(self, pid): +        """Returns information about package, without detailed information about containing files + +        :param pid: package id +        :raises PackageDoesNotExists: +        :return: :class:`PackageInfo` +        """ +        info = self.core.files.getPackageInfo(pid) +        if not info: +            raise PackageDoesNotExists(pid) +        return info + +    @RequirePerm(Permission.All) +    def getFileInfo(self, fid): +        """ Info for specific file + +        :param fid: file id +        :raises FileDoesNotExists: +        :return: :class:`FileInfo` + +        """ +        info = self.core.files.getFileInfo(fid) +        if not info: +            raise FileDoesNotExists(fid) +        return info + +    def getFilePath(self, fid): +        """ Internal method to get the filepath""" +        info = self.getFileInfo(fid) +        pack = self.core.files.getPackage(info.package) +        return pack.getPath(), info.name + +    @RequirePerm(Permission.All) +    def findFiles(self, pattern): +        return self.core.files.getTree(-1, True, DownloadState.All, pattern) + +    @RequirePerm(Permission.All) +    def searchSuggestions(self, pattern): +        names = self.core.db.getMatchingFilenames(pattern, self.primaryUID) +        # TODO: stemming and reducing the names to provide better suggestions +        return uniqify(names) + +    @RequirePerm(Permission.All) +    def findPackages(self, tags): +        pass + +    @RequirePerm(Permission.Modify) +    def updatePackage(self, pack): +        """Allows to modify several package attributes. + +        :param pid: package id +        :param data: :class:`PackageInfo` +        """ +        pid = pack.pid +        p = self.core.files.getPackage(pid) +        if not p: raise PackageDoesNotExists(pid) + +        #TODO: fix +        for key, value in data.iteritems(): +            if key == "id": continue +            setattr(p, key, value) + +        p.sync() +        self.core.files.save() + +    @RequirePerm(Permission.Modify) +    def setPackageFolder(self, pid, path): +        pass + +    @RequirePerm(Permission.Modify) +    def movePackage(self, pid, root): +        """ Set a new root for specific package. This will also moves the files on disk\ +           and will only work when no file is currently downloading. + +        :param pid: package id +        :param root: package id of new root +        :raises PackageDoesNotExists: When pid or root is missing +        :return: False if package can't be moved +        """ +        return self.core.files.movePackage(pid, root) + +    @RequirePerm(Permission.Modify) +    def moveFiles(self, fids, pid): +        """Move multiple files to another package. This will move the files on disk and\ +        only work when files are not downloading. All files needs to be continuous ordered +        in the current package. + +        :param fids: list of file ids +        :param pid: destination package +        :return: False if files can't be moved +        """ +        return self.core.files.moveFiles(fids, pid) + +    @RequirePerm(Permission.Modify) +    def orderPackage(self, pid, position): +        """Set new position for a package. + +        :param pid: package id +        :param position: new position, 0 for very beginning +        """ +        self.core.files.orderPackage(pid, position) + +    @RequirePerm(Permission.Modify) +    def orderFiles(self, fids, pid, position): +        """ Set a new position for a bunch of files within a package. +        All files have to be in the same package and must be **continuous**\ +        in the package. That means no gaps between them. + +        :param fids: list of file ids +        :param pid: package id of parent package +        :param position:  new position: 0 for very beginning +        """ +        self.core.files.orderFiles(fids, pid, position) + + +if Api.extend(FileApi): +    del FileApi
\ No newline at end of file diff --git a/pyload/api/UserApi.py b/pyload/api/UserApi.py new file mode 100644 index 000000000..d6fbb2646 --- /dev/null +++ b/pyload/api/UserApi.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from pyload.Api import Api, RequirePerm, Permission + +from ApiComponent import ApiComponent + +class UserApi(ApiComponent): +    """ Api methods to retrieve user profile and manage users. """ + +    @RequirePerm(Permission.All) +    def getUserData(self): +        """ Retrieves :class:`UserData` for the currently logged in user. """ + +    @RequirePerm(Permission.All) +    def setPassword(self, username, old_password, new_password): +        """ Changes password for specific user. User can only change their password. +        Admins can change every password! """ + +    def getAllUserData(self): +        """ Retrieves :class:`UserData` of all exisitng users.""" + +    def addUser(self, username, password): +        """ Adds an user to the db. + +        :param username: desired username +        :param password: password for authentication +        """ + +    def updateUserData(self, data): +        """ Change parameters of user account.  """ + +    def removeUser(self, uid): +        """ Removes user from the db. + +        :param uid: users uid +        """ + + +if Api.extend(UserApi): +    del UserApi
\ No newline at end of file diff --git a/pyload/api/UserInteractionApi.py b/pyload/api/UserInteractionApi.py new file mode 100644 index 000000000..f5a9e9290 --- /dev/null +++ b/pyload/api/UserInteractionApi.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from pyload.Api import Api, RequirePerm, Permission, Interaction + +from ApiComponent import ApiComponent + +class UserInteractionApi(ApiComponent): +    """ Everything needed for user interaction """ + +    @RequirePerm(Permission.Interaction) +    def isInteractionWaiting(self, mode): +        """ Check if task is waiting. + +        :param mode: binary or'ed output type +        :return: boolean +        """ +        return self.core.interactionManager.isTaskWaiting(self.primaryUID, mode) + +    @RequirePerm(Permission.Interaction) +    def getInteractionTasks(self, mode): +        """Retrieve task for specific mode. + +        :param mode: binary or'ed interaction types which should be retrieved +        :rtype list of :class:`InteractionTask` +        """ +        tasks = self.core.interactionManager.getTasks(self.primaryUID, mode) +        # retrieved tasks count as seen +        for t in tasks: +            t.seen = True +            if t.type == Interaction.Notification: +                t.setWaiting(self.core.interactionManager.CLIENT_THRESHOLD) + +        return tasks + +    @RequirePerm(Permission.Interaction) +    def setInteractionResult(self, iid, result): +        """Set Result for a interaction task. It will be immediately removed from task queue afterwards + +        :param iid: interaction id +        :param result: result as json string +        """ +        task = self.core.interactionManager.getTaskByID(iid) +        if task and self.primaryUID == task.owner: +            task.setResult(result) + +    @RequirePerm(Permission.Interaction) +    def getAddonHandler(self): +        pass + +    @RequirePerm(Permission.Interaction) +    def callAddonHandler(self, plugin, func, pid_or_fid): +        pass + +    @RequirePerm(Permission.Download) +    def generateDownloadLink(self, fid, timeout): +        pass + + +if Api.extend(UserInteractionApi): +    del UserInteractionApi
\ No newline at end of file diff --git a/pyload/api/__init__.py b/pyload/api/__init__.py new file mode 100644 index 000000000..a2b292a27 --- /dev/null +++ b/pyload/api/__init__.py @@ -0,0 +1,8 @@ +__all__ = ["CoreApi", "ConfigApi", "DownloadApi", "DownloadPreparingApi", "FileApi", +            "UserInteractionApi", "AccountApi", "AddonApi", "UserApi"] + +# Import all components +# from .import * +# Above does not work in py 2.5 +for name in __all__: +    __import__(__name__ + "." + name)
\ No newline at end of file  | 
