summaryrefslogtreecommitdiffstats
path: root/pyload/api
diff options
context:
space:
mode:
Diffstat (limited to 'pyload/api')
-rw-r--r--pyload/api/AccountApi.py79
-rw-r--r--pyload/api/AddonApi.py27
-rw-r--r--pyload/api/ApiComponent.py23
-rw-r--r--pyload/api/ConfigApi.py135
-rw-r--r--pyload/api/CoreApi.py131
-rw-r--r--pyload/api/DownloadApi.py171
-rw-r--r--pyload/api/DownloadPreparingApi.py105
-rw-r--r--pyload/api/FileApi.py167
-rw-r--r--pyload/api/UserApi.py41
-rw-r--r--pyload/api/UserInteractionApi.py61
-rw-r--r--pyload/api/__init__.py8
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