diff options
Diffstat (limited to 'module/api')
| -rw-r--r-- | module/api/AccountApi.py | 49 | ||||
| -rw-r--r-- | module/api/AddonApi.py | 27 | ||||
| -rw-r--r-- | module/api/ApiComponent.py | 7 | ||||
| -rw-r--r-- | module/api/CollectorApi.py | 37 | ||||
| -rw-r--r-- | module/api/DownloadApi.py | 182 | ||||
| -rw-r--r-- | module/api/DownloadPreparingApi.py | 121 | ||||
| -rw-r--r-- | module/api/FileApi.py | 153 | ||||
| -rw-r--r-- | module/api/UserInteractionApi.py | 65 | ||||
| -rw-r--r-- | module/api/__init__.py | 5 | 
9 files changed, 644 insertions, 2 deletions
| diff --git a/module/api/AccountApi.py b/module/api/AccountApi.py new file mode 100644 index 000000000..396824a55 --- /dev/null +++ b/module/api/AccountApi.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from module.Api import Api, RequirePerm, Permission + +from ApiComponent import ApiComponent + +class AccountApi(ApiComponent): +    """ All methods to control accounts """ + +    @RequirePerm(Permission.Accounts) +    def getAccounts(self, refresh): +        """Get information about all entered accounts. + +        :param refresh: reload account info +        :return: list of `AccountInfo` +        """ +        accs = self.core.accountManager.getAllAccounts(refresh) +        accounts = [] +        for plugin in accs.itervalues(): +            accounts.extend(plugin.values()) + +        return 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 updateAccount(self, plugin, account, password=None, options={}): +        """Changes pw/options for specific account.""" +        self.core.accountManager.updateAccount(plugin, account, password, options) + +    @RequirePerm(Permission.Accounts) +    def removeAccount(self, plugin, account): +        """Remove account from pyload. + +        :param plugin: pluginname +        :param account: accountname +        """ +        self.core.accountManager.removeAccount(plugin, account) + + +if Api.extend(AccountApi): +    del AccountApi
\ No newline at end of file diff --git a/module/api/AddonApi.py b/module/api/AddonApi.py new file mode 100644 index 000000000..917c7dc4c --- /dev/null +++ b/module/api/AddonApi.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from module.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/module/api/ApiComponent.py b/module/api/ApiComponent.py index 2b09d05a3..ba96b3be9 100644 --- a/module/api/ApiComponent.py +++ b/module/api/ApiComponent.py @@ -1,12 +1,17 @@  #!/usr/bin/env python  # -*- coding: utf-8 -*- -class ApiComponent: +from module.remote.ttypes import Iface + +# Workaround to let code-completion think, this is subclass of Iface +Iface = object +class ApiComponent(Iface):      def __init__(self, core):          # Only for auto completion, this class can not be instantiated          from pyload import Core          assert isinstance(core, Core) +        assert issubclass(ApiComponent, Iface)          self.core = core          # No instantiating!          raise Exception()
\ No newline at end of file diff --git a/module/api/CollectorApi.py b/module/api/CollectorApi.py new file mode 100644 index 000000000..eb36f7a21 --- /dev/null +++ b/module/api/CollectorApi.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from module.Api import Api, RequirePerm, Permission + +from ApiComponent import ApiComponent + +class CollectorApi(ApiComponent): +    """ Link collector """ + +    @RequirePerm(Permission.All) +    def getCollector(self): +        pass + +    @RequirePerm(Permission.Add) +    def addToCollector(self, links): +        pass + +    @RequirePerm(Permission.Add) +    def addFromCollector(self, name, new_name): +        pass + +    @RequirePerm(Permission.Delete) +    def deleteCollPack(self, name): +        pass + +    @RequirePerm(Permission.Add) +    def renameCollPack(self, name, new_name): +        pass + +    @RequirePerm(Permission.Delete) +    def deleteCollLink(self, url): +        pass + + +if Api.extend(CollectorApi): +    del CollectorApi
\ No newline at end of file diff --git a/module/api/DownloadApi.py b/module/api/DownloadApi.py new file mode 100644 index 000000000..ba49435b3 --- /dev/null +++ b/module/api/DownloadApi.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from os.path import isabs + +from module.Api import Api, RequirePerm, Permission +from module.utils.fs import join + +from ApiComponent import ApiComponent + +class DownloadApi(ApiComponent): +    """ Component to create, add, delete or modify downloads.""" + +    @RequirePerm(Permission.Add) +    def generateAndAddPackages(self, links, paused=False): +        """Generates and add packages + +        :param links: list of urls +        :param paused: paused package +        :return: list of package ids +        """ +        return [self.addPackageP(name, urls, "", paused) for name, urls +                in self.generatePackages(links).iteritems()] + +    @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/module/api/DownloadPreparingApi.py b/module/api/DownloadPreparingApi.py new file mode 100644 index 000000000..4fc5b1ff9 --- /dev/null +++ b/module/api/DownloadPreparingApi.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from itertools import chain + +from module.Api import Api, RequirePerm, Permission, OnlineCheck, LinkStatus, urlmatcher +from module.utils.fs import join +from module.network.RequestFactory import getURL +from module.common.packagetools import parseNames + +from ApiComponent import ApiComponent + +class DownloadPreparingApi(ApiComponent): +    """ All kind of methods to parse links or retrieve online status """ + +    @RequirePerm(Permission.Add) +    def parseURLs(self, html=None, url=None): +        """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)] + +        # remove duplicates +        return self.checkURLs(set(urls)) + + +    @RequirePerm(Permission.Add) +    def checkURLs(self, urls): +        """ Gets urls and returns pluginname mapped to list of matching urls. + +        :param urls: +        :return: {plugin: urls} +        """ +        data, crypter = self.core.pluginManager.parseUrls(urls) +        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 checkOnlineStatus(self, urls): +        """ initiates online status check, will also decrypt files. + +        :param urls: +        :return: initial set of data as :class:`OnlineCheck` instance containing the result id +        """ +        data, crypter = self.core.pluginManager.parseUrls(urls) + +        # initial result does not contain the crypter links +        tmp = [(url, (url, LinkStatus(url, pluginname, "unknown", 3, 0))) for url, pluginname in data] +        data = parseNames(tmp) +        result = {} + +        for k, v in data.iteritems(): +            for url, status in v: +                status.packagename = k +                result[url] = status + +        data.update(crypter) # hoster and crypter will be processed +        rid = self.core.threadManager.createResultThread(data, False) + +        return OnlineCheck(rid, result) + +    @RequirePerm(Permission.Add) +    def checkOnlineStatusContainer(self, urls, container, data): +        """ checks online status of urls and a submitted container file + +        :param urls: list of urls +        :param container: container file name +        :param data: file content +        :return: :class:`OnlineCheck` +        """ +        th = open(join(self.core.config["general"]["download_folder"], "tmp_" + container), "wb") +        th.write(str(data)) +        th.close() +        urls.append(th.name) +        return self.checkOnlineStatus(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 "ALL_INFO_FETCHED" in result: +            del result["ALL_INFO_FETCHED"] +            return OnlineCheck(-1, result) +        else: +            return OnlineCheck(rid, result) + + +    @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/module/api/FileApi.py b/module/api/FileApi.py new file mode 100644 index 000000000..f470cfa3e --- /dev/null +++ b/module/api/FileApi.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from module.Api import Api, RequirePerm, Permission, DownloadState, PackageDoesNotExists, FileDoesNotExists + +from ApiComponent import ApiComponent + +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 + +    @RequirePerm(Permission.All) +    def findFiles(self, pattern): +        pass + +    @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/module/api/UserInteractionApi.py b/module/api/UserInteractionApi.py new file mode 100644 index 000000000..ded305c30 --- /dev/null +++ b/module/api/UserInteractionApi.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from module.Api import Api, RequirePerm, Permission, InteractionTask + +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(mode) + +    @RequirePerm(Permission.Interaction) +    def getInteractionTask(self, mode): +        """Retrieve task for specific mode. + +        :param mode: binary or'ed output type +        :return: :class:`InteractionTask` +        """ +        task = self.core.interactionManager.getTask(mode) +        return InteractionTask(-1) if  not task else task + + +    @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 string +        """ +        task = self.core.interactionManager.getTaskByID(iid) +        if task: +            task.setResult(result) + +    @RequirePerm(Permission.Interaction) +    def getNotifications(self): +        """List of all available notifcations. They stay in queue for some time, client should\ +           save which notifications it already has seen. + +        :return: list of :class:`InteractionTask` +        """ +        return self.core.interactionManager.getNotifications() + +    @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/module/api/__init__.py b/module/api/__init__.py index f7ceb6183..754ed4b19 100644 --- a/module/api/__init__.py +++ b/module/api/__init__.py @@ -1 +1,4 @@ -__all__ = ["CoreApi", "ConfigApi"]
\ No newline at end of file +__all__ = ["CoreApi", "ConfigApi", "DownloadApi", "DownloadPreparingApi", "FileApi", +           "CollectorApi", "UserInteractionApi", "AccountApi", "AddonApi"] +# Import all components +from .import *
\ No newline at end of file | 
