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 |