diff options
author | Walter Purcaro <vuolter@gmail.com> | 2015-02-16 21:59:10 +0100 |
---|---|---|
committer | Walter Purcaro <vuolter@gmail.com> | 2015-02-16 21:59:10 +0100 |
commit | 8e7d14bae4d3c836f029a1235eb227380acc3f75 (patch) | |
tree | ebd0679642cccb994e70a89a106b394189cb28bc /pyload/api | |
parent | Merge branch 'stable' into 0.4.10 (diff) | |
download | pyload-8e7d14bae4d3c836f029a1235eb227380acc3f75.tar.xz |
Fix plugins to work on 0.4.10
Diffstat (limited to 'pyload/api')
-rw-r--r-- | pyload/api/__init__.py | 973 | ||||
-rw-r--r-- | pyload/api/types.py | 381 |
2 files changed, 1354 insertions, 0 deletions
diff --git a/pyload/api/__init__.py b/pyload/api/__init__.py new file mode 100644 index 000000000..387481da2 --- /dev/null +++ b/pyload/api/__init__.py @@ -0,0 +1,973 @@ +# -*- coding: utf-8 -*- +# @author: RaNaN + +from base64 import standard_b64encode +from os.path import join +from time import time +import re + +from urlparse import urlparse + +from pyload.datatype.File import PyFile +from pyload.utils.packagetools import parseNames +from pyload.network.RequestFactory import getURL +from pyload.remote import activated +from pyload.utils import compare_time, freeSpace, safe_filename + +if activated: + try: + from thrift.protocol import TBase + from pyload.remote.thriftbackend.thriftgen.pyload.ttypes import * + from pyload.remote.thriftbackend.thriftgen.pyload.Pyload import Iface + + BaseObject = TBase + + except ImportError: + from pyload.api.types import * + + print "Thrift not imported" + +else: + from pyload.api.types import * + +# contains function names mapped to their permissions +# unlisted functions are for admins only +permMap = {} + + +# decorator only called on init, never initialized, so has no effect on runtime +def permission(bits): + class _Dec(object): + def __new__(cls, func, *args, **kwargs): + permMap[func.__name__] = bits + return func + return _Dec + + +urlmatcher = re.compile(r"((https?|ftps?|xdcc|sftp):((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-=\\\.&\[\]\|]*)", re.IGNORECASE) + + +class PERMS(object): + ALL = 0 # requires no permission, but login + ADD = 1 # can add packages + DELETE = 2 # can delete packages + STATUS = 4 # see and change server status + LIST = 16 # see queue and collector + MODIFY = 32 # moddify some attribute of downloads + DOWNLOAD = 64 # can download from webinterface + SETTINGS = 128 # can access settings + ACCOUNTS = 256 # can access accounts + LOGS = 512 # can see server logs + + +class ROLE(object): + ADMIN = 0 # admin has all permissions implicit + USER = 1 + + +def has_permission(userperms, perms): + # bytewise or perms before if needed + return perms == (userperms & perms) + + +class Api(Iface): + """ + **pyLoads API** + + This is accessible either internal via core.api or via thrift backend. + + see Thrift specification file remote/thriftbackend/pyload.thrift\ + for information about data structures and what methods are usuable with rpc. + + Most methods requires specific permissions, please look at the source code if you need to know.\ + These can be configured via webinterface. + Admin user have all permissions, and are the only ones who can access the methods with no specific permission. + """ + EXTERNAL = Iface # let the json api know which methods are external + + def __init__(self, core): + self.core = core + + def _convertPyFile(self, p): + fdata = FileData(p["id"], p["url"], p["name"], p["plugin"], p["size"], + p["format_size"], p["status"], p["statusmsg"], + p["package"], p["error"], p["order"]) + return fdata + + def _convertConfigFormat(self, c): + sections = {} + for sectionName, sub in c.iteritems(): + section = ConfigSection(sectionName, sub["desc"]) + items = [] + for key, data in sub.iteritems(): + if key in ("desc", "outline"): + continue + item = ConfigItem() + item.name = key + item.description = data["desc"] + item.value = str(data["value"]) if not isinstance(data["value"], basestring) else data["value"] + item.type = data["type"] + items.append(item) + section.items = items + sections[sectionName] = section + if "outline" in sub: + section.outline = sub["outline"] + return sections + + @permission(PERMS.SETTINGS) + def getConfigValue(self, category, option, section="core"): + """Retrieve config value. + + :param category: name of category, or plugin + :param option: config option + :param section: 'plugin' or 'core' + :return: config value as string + """ + if section == "core": + value = self.core.config[category][option] + else: + value = self.core.config.getPlugin(category, option) + return str(value) + + @permission(PERMS.SETTINGS) + def setConfigValue(self, category, option, value, section="core"): + """Set new config value. + + :param category: + :param option: + :param value: new config value + :param section: 'plugin' or 'core + """ + self.core.addonManager.dispatchEvent("config-changed", category, option, value, section) + if section == "core": + self.core.config[category][option] = value + if option in ("limit_speed", "max_speed"): # not so nice to update the limit + self.core.requestFactory.updateBucket() + elif section == "plugin": + self.core.config.setPlugin(category, option, value) + + @permission(PERMS.SETTINGS) + def getConfig(self): + """Retrieves complete config of core. + + :return: list of `ConfigSection` + """ + return self._convertConfigFormat(self.core.config.config) + + def getConfigDict(self): + """Retrieves complete config in dict format, not for RPC. + + :return: dict + """ + return self.core.config.config + + @permission(PERMS.SETTINGS) + def getPluginConfig(self): + """Retrieves complete config for all plugins. + + :return: list of `ConfigSection` + """ + return self._convertConfigFormat(self.core.config.plugin) + + def getPluginConfigDict(self): + """Plugin config as dict, not for RPC. + + :return: dict + """ + return self.core.config.plugin + + @permission(PERMS.STATUS) + def pauseServer(self): + """Pause server: Tt wont start any new downloads, but nothing gets aborted.""" + self.core.threadManager.pause = True + + @permission(PERMS.STATUS) + def unpauseServer(self): + """Unpause server: New Downloads will be started.""" + self.core.threadManager.pause = False + + @permission(PERMS.STATUS) + def togglePause(self): + """Toggle pause state. + + :return: new pause state + """ + self.core.threadManager.pause ^= True + return self.core.threadManager.pause + + @permission(PERMS.STATUS) + def toggleReconnect(self): + """Toggle reconnect activation. + + :return: new reconnect state + """ + self.core.config["reconnect"]["activated"] ^= True + return self.core.config["reconnect"]["activated"] + + @permission(PERMS.LIST) + def statusServer(self): + """Some general information about the current status of pyLoad. + + :return: `ServerStatus` + """ + serverStatus = ServerStatus(self.core.threadManager.pause, len(self.core.threadManager.processingIds()), + self.core.files.getQueueCount(), self.core.files.getFileCount(), 0, + not self.core.threadManager.pause and self.isTimeDownload(), + self.core.config['reconnect']['activated'] and self.isTimeReconnect()) + for pyfile in [x.active for x in self.core.threadManager.threads if x.active and isinstance(x.active, PyFile)]: + serverStatus.speed += pyfile.getSpeed() # bytes/s + return serverStatus + + @permission(PERMS.STATUS) + def freeSpace(self): + """Available free space at download directory in bytes""" + return freeSpace(self.core.config["general"]["download_folder"]) + + @permission(PERMS.ALL) + def getServerVersion(self): + """pyLoad Core version """ + return self.core.version + + def kill(self): + """Clean way to quit pyLoad""" + self.core.do_kill = True + + def restart(self): + """Restart pyload core""" + self.core.do_restart = True + + @permission(PERMS.LOGS) + 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 Exception: + return ['No log available'] + + @permission(PERMS.STATUS) + 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) + + @permission(PERMS.STATUS) + 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"] + + @permission(PERMS.LIST) + def statusDownloads(self): + """ Status off all currently running downloads. + + :return: list of `DownloadStatus` + """ + data = [] + for pyfile in self.core.threadManager.getActiveFiles(): + if not isinstance(pyfile, PyFile): + continue + data.append(DownloadInfo( + pyfile.id, pyfile.name, pyfile.getSpeed(), pyfile.getETA(), pyfile.formatETA(), + pyfile.getBytesLeft(), pyfile.getSize(), pyfile.formatSize(), pyfile.getPercent(), + pyfile.status, pyfile.getStatusName(), pyfile.formatWait(), + pyfile.waitUntil, pyfile.packageid, pyfile.package().name, pyfile.pluginname)) + return data + + @permission(PERMS.ADD) + def addPackage(self, name, links, dest=Destination.Queue): + """Adds a package, with links to desired destination. + + :param name: name of the new package + :param links: list of urls + :param dest: `Destination` + :return: package id of the new package + """ + if self.core.config['general']['folder_per_package']: + folder = urlparse(name).path.split("/")[-1] + else: + folder = "" + + folder = safe_filename(folder) + + pid = self.core.files.addPackage(name, folder, dest) + + self.core.files.addLinks(links, pid) + + self.core.log.info(_("Added package %(name)s containing %(count)d links") % {"name": name, "count": len(links)}) + + self.core.files.save() + + return pid + + @permission(PERMS.ADD) + def parseURLs(self, html=None, url=None): + """Parses html content or any arbitaty 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)) + + @permission(PERMS.ADD) + def checkURLs(self, urls): + """ Gets urls and returns pluginname mapped to list of matches urls. + + :param urls: + :return: {plugin: urls} + """ + data = self.core.pluginManager.parseUrls(urls) + plugins = {} + + for url, plugintype, pluginname in data: + try: + plugins[plugintype][pluginname].append(url) + except Exception: + plugins[plugintype][pluginname] = [url] + + return plugins + + @permission(PERMS.ADD) + def checkOnlineStatus(self, urls): + """ initiates online status check + + :param urls: + :return: initial set of data as `OnlineCheck` instance containing the result id + """ + data = self.core.pluginManager.parseUrls(urls) + + rid = self.core.threadManager.createResultThread(data, False) + + tmp = [(url, (url, OnlineStatus(url, (plugintype, pluginname), "unknown", 3, 0))) for url, plugintype, pluginname in data] + data = parseNames(tmp) + result = {} + for k, v in data.iteritems(): + for url, status in v: + status.packagename = k + result[url] = status + + return OnlineCheck(rid, result) + + @permission(PERMS.ADD) + def checkOnlineStatusContainer(self, urls, container, data): + """ checks online status of urls and a submited container file + + :param urls: list of urls + :param container: container file name + :param data: file content + :return: online check + """ + th = open(join(self.core.config["general"]["download_folder"], "tmp_" + container), "wb") + th.write(str(data)) + th.close() + return self.checkOnlineStatus(urls + [th.name]) + + @permission(PERMS.ADD) + def pollResults(self, rid): + """ Polls the result available for ResultID + + :param rid: `ResultID` + :return: `OnlineCheck`, if rid is -1 then 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) + + @permission(PERMS.ADD) + def generatePackages(self, links): + """ Parses links, generates packages names from urls + + :param links: list of urls + :return: package names mapped to urls + """ + return parseNames((x, x) for x in links) + + @permission(PERMS.ADD) + def generateAndAddPackages(self, links, dest=Destination.Queue): + """Generates and add packages + + :param links: list of urls + :param dest: `Destination` + :return: list of package ids + """ + return [self.addPackage(name, urls, dest) for name, urls + in self.generatePackages(links).iteritems()] + + @permission(PERMS.ADD) + def checkAndAddPackages(self, links, dest=Destination.Queue): + """Checks online status, retrieves names, and will add packages.\ + Because of this packages are not added immediatly, only for internal use. + + :param links: list of urls + :param dest: `Destination` + :return: None + """ + data = self.core.pluginManager.parseUrls(links) + self.core.threadManager.createResultThread(data, True) + + @permission(PERMS.LIST) + def getPackageData(self, pid): + """Returns complete information about package, and included files. + + :param pid: package id + :return: `PackageData` with .links attribute + """ + data = self.core.files.getPackageData(int(pid)) + if not data: + raise PackageDoesNotExists(pid) + return PackageData(data["id"], data["name"], data["folder"], data["site"], data["password"], + data["queue"], data["order"], + links=[self._convertPyFile(x) for x in data["links"].itervalues()]) + + @permission(PERMS.LIST) + def getPackageInfo(self, pid): + """Returns information about package, without detailed information about containing files + + :param pid: package id + :return: `PackageData` with .fid attribute + """ + data = self.core.files.getPackageData(int(pid)) + + if not data: + raise PackageDoesNotExists(pid) + return PackageData(data["id"], data["name"], data["folder"], data["site"], data["password"], + data["queue"], data["order"], + fids=[int(x) for x in data["links"]]) + + @permission(PERMS.LIST) + def getFileData(self, fid): + """Get complete information about a specific file. + + :param fid: file id + :return: `FileData` + """ + info = self.core.files.getFileData(int(fid)) + if not info: + raise FileDoesNotExists(fid) + return self._convertPyFile(info.values()[0]) + + @permission(PERMS.DELETE) + def deleteFiles(self, fids): + """Deletes several file entries from pyload. + + :param fids: list of file ids + """ + for fid in fids: + self.core.files.deleteLink(int(fid)) + self.core.files.save() + + @permission(PERMS.DELETE) + def deletePackages(self, pids): + """Deletes packages and containing links. + + :param pids: list of package ids + """ + for pid in pids: + self.core.files.deletePackage(int(pid)) + self.core.files.save() + + @permission(PERMS.LIST) + def getQueue(self): + """Returns info about queue and packages, **not** about files, see `getQueueData` \ + or `getPackageData` instead. + + :return: list of `PackageInfo` + """ + return [PackageData(pack["id"], pack["name"], pack["folder"], pack["site"], + pack["password"], pack["queue"], pack["order"], + pack["linksdone"], pack["sizedone"], pack["sizetotal"], + pack["linkstotal"]) + for pack in self.core.files.getInfoData(Destination.Queue).itervalues()] + + @permission(PERMS.LIST) + def getQueueData(self): + """Return complete data about everything in queue, this is very expensive use it sparely.\ + See `getQueue` for alternative. + + :return: list of `PackageData` + """ + return [PackageData(pack["id"], pack["name"], pack["folder"], pack["site"], + pack["password"], pack["queue"], pack["order"], + pack["linksdone"], pack["sizedone"], pack["sizetotal"], + links=[self._convertPyFile(x) for x in pack["links"].itervalues()]) + for pack in self.core.files.getCompleteData(Destination.Queue).itervalues()] + + @permission(PERMS.LIST) + def getCollector(self): + """same as `getQueue` for collector. + + :return: list of `PackageInfo` + """ + return [PackageData(pack["id"], pack["name"], pack["folder"], pack["site"], + pack["password"], pack["queue"], pack["order"], + pack["linksdone"], pack["sizedone"], pack["sizetotal"], + pack["linkstotal"]) + for pack in self.core.files.getInfoData(Destination.Collector).itervalues()] + + @permission(PERMS.LIST) + def getCollectorData(self): + """same as `getQueueData` for collector. + + :return: list of `PackageInfo` + """ + return [PackageData(pack["id"], pack["name"], pack["folder"], pack["site"], + pack["password"], pack["queue"], pack["order"], + pack["linksdone"], pack["sizedone"], pack["sizetotal"], + links=[self._convertPyFile(x) for x in pack["links"].itervalues()]) + for pack in self.core.files.getCompleteData(Destination.Collector).itervalues()] + + @permission(PERMS.ADD) + def addFiles(self, pid, links): + """Adds files to specific package. + + :param pid: package id + :param links: list of urls + """ + self.core.files.addLinks(links, int(pid)) + self.core.log.info(_("Added %(count)d links to package #%(package)d ") % {"count": len(links), "package": pid}) + self.core.files.save() + + @permission(PERMS.MODIFY) + def pushToQueue(self, pid): + """Moves package from Collector to Queue. + + :param pid: package id + """ + self.core.files.setPackageLocation(pid, Destination.Queue) + + @permission(PERMS.MODIFY) + def pullFromQueue(self, pid): + """Moves package from Queue to Collector. + + :param pid: package id + """ + self.core.files.setPackageLocation(pid, Destination.Collector) + + @permission(PERMS.MODIFY) + def restartPackage(self, pid): + """Restarts a package, resets every containing files. + + :param pid: package id + """ + self.core.files.restartPackage(int(pid)) + + @permission(PERMS.MODIFY) + def restartFile(self, fid): + """Resets file status, so it will be downloaded again. + + :param fid: file id + """ + self.core.files.restartFile(int(fid)) + + @permission(PERMS.MODIFY) + def recheckPackage(self, pid): + """Proofes online status of all files in a package, also a default action when package is added. + + :param pid: + :return: + """ + self.core.files.reCheckPackage(int(pid)) + + @permission(PERMS.MODIFY) + def stopAllDownloads(self): + """Aborts all running downloads.""" + + pyfiles = self.core.files.cache.values() + for pyfile in pyfiles: + pyfile.abortDownload() + + @permission(PERMS.MODIFY) + def stopDownloads(self, fids): + """Aborts specific downloads. + + :param fids: list of file ids + :return: + """ + pyfiles = self.core.files.cache.values() + for pyfile in pyfiles: + if pyfile.id in fids: + pyfile.abortDownload() + + @permission(PERMS.MODIFY) + def setPackageName(self, pid, name): + """Renames a package. + + :param pid: package id + :param name: new package name + """ + pack = self.core.files.getPackage(pid) + pack.name = name + pack.sync() + + @permission(PERMS.MODIFY) + def movePackage(self, destination, pid): + """Set a new package location. + + :param destination: `Destination` + :param pid: package id + """ + if destination in (0, 1): + self.core.files.setPackageLocation(pid, destination) + + @permission(PERMS.MODIFY) + def moveFiles(self, fids, pid): + """Move multiple files to another package + + :param fids: list of file ids + :param pid: destination package + :return: + """ + # TODO: implement + pass + + + @permission(PERMS.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() + self.addPackage(th.name, [th.name], Destination.Queue) + + @permission(PERMS.MODIFY) + def orderPackage(self, pid, position): + """Gives a package a new position. + + :param pid: package id + :param position: + """ + self.core.files.reorderPackage(pid, position) + + @permission(PERMS.MODIFY) + def orderFile(self, fid, position): + """Gives a new position to a file within its package. + + :param fid: file id + :param position: + """ + self.core.files.reorderFile(fid, position) + + @permission(PERMS.MODIFY) + def setPackageData(self, pid, data): + """Allows to modify several package attributes. + + :param pid: package id + :param data: dict that maps attribute to desired value + """ + package = self.core.files.getPackage(pid) + if not package: + raise PackageDoesNotExists(pid) + for key, value in data.iteritems(): + if key == "id": + continue + setattr(package, key, value) + package.sync() + self.core.files.save() + + @permission(PERMS.DELETE) + def deleteFinished(self): + """Deletes all finished files and completly finished packages. + + :return: list of deleted package ids + """ + return self.core.files.deleteFinishedLinks() + + @permission(PERMS.MODIFY) + def restartFailed(self): + """Restarts all failed failes.""" + self.core.files.restartFailed() + + @permission(PERMS.LIST) + def getPackageOrder(self, destination): + """Returns information about package order. + + :param destination: `Destination` + :return: dict mapping order to package id + """ + packs = self.core.files.getInfoData(destination) + order = {} + for pid in packs: + pack = self.core.files.getPackageData(int(pid)) + while pack["order"] in order.keys(): # just in case + pack["order"] += 1 + order[pack["order"]] = pack["id"] + return order + + @permission(PERMS.LIST) + def getFileOrder(self, pid): + """Information about file order within package. + + :param pid: + :return: dict mapping order to file id + """ + rawdata = self.core.files.getPackageData(int(pid)) + order = {} + for id, pyfile in rawdata["links"].iteritems(): + while pyfile["order"] in order.keys(): # just in case + pyfile["order"] += 1 + order[pyfile["order"]] = pyfile["id"] + return order + + + @permission(PERMS.STATUS) + def isCaptchaWaiting(self): + """Indicates wether a captcha task is available + + :return: bool + """ + self.core.lastClientConnected = time() + task = self.core.captchaManager.getTask() + return not task is None + + @permission(PERMS.STATUS) + def getCaptchaTask(self, exclusive=False): + """Returns a captcha task + + :param exclusive: unused + :return: `CaptchaTask` + """ + self.core.lastClientConnected = time() + task = self.core.captchaManager.getTask() + if task: + task.setWatingForUser(exclusive=exclusive) + data, type, result = task.getCaptcha() + ctask = CaptchaTask(int(task.id), standard_b64encode(data), type, result) + return ctask + return CaptchaTask(-1) + + @permission(PERMS.STATUS) + def getCaptchaTaskStatus(self, tid): + """Get information about captcha task + + :param tid: task id + :return: string + """ + self.core.lastClientConnected = time() + task = self.core.captchaManager.getTaskByID(tid) + return task.getStatus() if task else "" + + @permission(PERMS.STATUS) + def setCaptchaResult(self, tid, result): + """Set result for a captcha task + + :param tid: task id + :param result: captcha result + """ + self.core.lastClientConnected = time() + task = self.core.captchaManager.getTaskByID(tid) + if task: + task.setResult(result) + self.core.captchaManager.removeTask(task) + + @permission(PERMS.STATUS) + def getEvents(self, uuid): + """Lists occured events, may be affected to changes in future. + + :param uuid: + :return: list of `Events` + """ + events = self.core.pullManager.getEvents(uuid) + new_events = [] + + def convDest(d): + return Destination.Queue if d == "queue" else Destination.Collector + + for e in events: + event = EventInfo() + event.eventname = e[0] + if e[0] in ("update", "remove", "insert"): + event.id = e[3] + event.type = ElementType.Package if e[2] == "pack" else ElementType.File + event.destination = convDest(e[1]) + elif e[0] == "order": + if e[1]: + event.id = e[1] + event.type = ElementType.Package if e[2] == "pack" else ElementType.File + event.destination = convDest(e[3]) + elif e[0] == "reload": + event.destination = convDest(e[1]) + new_events.append(event) + return new_events + + @permission(PERMS.ACCOUNTS) + def getAccounts(self, refresh): + """Get information about all entered accounts. + + :param refresh: reload account info + :return: list of `AccountInfo` + """ + accs = self.core.accountManager.getAccountInfos(False, refresh) + for group in accs.values(): + accounts = [AccountInfo(acc["validuntil"], acc["login"], acc["options"], acc["valid"], + acc["trafficleft"], acc["maxtraffic"], acc["premium"], acc["type"]) + for acc in group] + return accounts or [] + + @permission(PERMS.ALL) + def getAccountTypes(self): + """All available account types. + + :return: list + """ + return self.core.accountManager.accounts.keys() + + @permission(PERMS.ACCOUNTS) + def updateAccount(self, plugin, account, password=None, options=None): + """Changes pw/options for specific account.""" + self.core.accountManager.updateAccount(plugin, account, password, options or {}) + + @permission(PERMS.ACCOUNTS) + def removeAccount(self, plugin, account): + """Remove account from pyload. + + :param plugin: pluginname + :param account: accountname + """ + self.core.accountManager.removeAccount(plugin, account) + + @permission(PERMS.ALL) + def login(self, username, password, remoteip=None): + """Login into pyLoad, this **must** be called when using rpc before any methods can be used. + + :param username: + :param password: + :param remoteip: Omit this argument, its only used internal + :return: bool indicating login was successful + """ + return bool(self.checkAuth(username, password, remoteip)) + + def checkAuth(self, username, password, remoteip=None): + """Check authentication and returns details + + :param username: + :param password: + :param remoteip: + :return: dict with info, empty when login is incorrect + """ + if self.core.config["remote"]["nolocalauth"] and remoteip == "127.0.0.1": + return "local" + else: + return self.core.db.checkAuth(username, password) + + def isAuthorized(self, func, userdata): + """checks if the user is authorized for specific method + + :param func: function name + :param userdata: dictionary of user data + :return: boolean + """ + if userdata == "local" or userdata["role"] == ROLE.ADMIN: + return True + elif func in permMap and has_permission(userdata["permission"], permMap[func]): + return True + else: + return False + + @permission(PERMS.ALL) + def getUserData(self, username, password): + """similar to `checkAuth` but returns UserData thrift type """ + user = self.checkAuth(username, password) + if user: + return UserData(user["name"], user["email"], user["role"], user["permission"], user["template"]) + else: + return UserData() + + def getAllUserData(self): + """returns all known user and info""" + return dict((user, UserData(user, data["email"], data["role"], data["permission"], data["template"])) for user, data + in self.core.db.getAllUserData().iteritems()) + + @permission(PERMS.STATUS) + def getServices(self): + """ A dict of available services, these can be defined by addon plugins. + + :return: dict with this style: {"plugin": {"method": "description"}} + """ + return dict((plugin, funcs) for plugin, funcs in self.core.addonManager.methods.iteritems()) + + @permission(PERMS.STATUS) + def hasService(self, plugin, func): + """Checks wether a service is available. + + :param plugin: + :param func: + :return: bool + """ + cont = self.core.addonManager.methods + return plugin in cont and func in cont[plugin] + + @permission(PERMS.STATUS) + def call(self, info): + """Calls a service (a method in addon plugin). + + :param info: `ServiceCall` + :return: result + :raises: ServiceDoesNotExists, when its not available + :raises: ServiceException, when a exception was raised + """ + plugin = info.plugin + func = info.func + args = info.arguments + parse = info.parseArguments + if not self.hasService(plugin, func): + raise ServiceDoesNotExists(plugin, func) + try: + ret = self.core.addonManager.callRPC(plugin, func, args, parse) + except Exception, e: + raise ServiceException(e.message) + + @permission(PERMS.STATUS) + def getAllInfo(self): + """Returns all information stored by addon plugins. Values are always strings + + :return: {"plugin": {"name": value}} + """ + return self.core.addonManager.getAllInfo() + + @permission(PERMS.STATUS) + 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) + + def changePassword(self, user, oldpw, newpw): + """ changes password for specific user """ + return self.core.db.changePassword(user, oldpw, newpw) + + def setUserPermission(self, user, perm, role): + self.core.db.setPermission(user, perm) + self.core.db.setRole(user, role) diff --git a/pyload/api/types.py b/pyload/api/types.py new file mode 100644 index 000000000..81385bf9f --- /dev/null +++ b/pyload/api/types.py @@ -0,0 +1,381 @@ +# -*- coding: utf-8 -*- +# Autogenerated by pyload +# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +class BaseObject(object): + __slots__ = [] + +class Destination(object): + Collector = 0 + Queue = 1 + +class DownloadStatus(object): + Aborted = 9 + Custom = 11 + Decrypting = 10 + Downloading = 12 + Failed = 8 + Finished = 0 + Offline = 1 + Online = 2 + Processing = 13 + Queued = 3 + Skipped = 4 + Starting = 7 + TempOffline = 6 + Unknown = 14 + Waiting = 5 + +class ElementType(object): + File = 1 + Package = 0 + +class Input(object): + BOOL = 4 + CHOICE = 6 + CLICK = 5 + LIST = 8 + MULTIPLE = 7 + NONE = 0 + PASSWORD = 3 + TABLE = 9 + TEXT = 1 + TEXTBOX = 2 + +class Output(object): + CAPTCHA = 1 + NOTIFICATION = 4 + QUESTION = 2 + +class AccountInfo(BaseObject): + __slots__ = ['validuntil', 'login', 'options', 'valid', 'trafficleft', 'maxtraffic', 'premium', 'type'] + + def __init__(self, validuntil=None, login=None, options=None, valid=None, trafficleft=None, maxtraffic=None, premium=None, type=None): + self.validuntil = validuntil + self.login = login + self.options = options + self.valid = valid + self.trafficleft = trafficleft + self.maxtraffic = maxtraffic + self.premium = premium + self.type = type + +class CaptchaTask(BaseObject): + __slots__ = ['tid', 'data', 'type', 'resultType'] + + def __init__(self, tid=None, data=None, type=None, resultType=None): + self.tid = tid + self.data = data + self.type = type + self.resultType = resultType + +class ConfigItem(BaseObject): + __slots__ = ['name', 'description', 'value', 'type'] + + def __init__(self, name=None, description=None, value=None, type=None): + self.name = name + self.description = description + self.value = value + self.type = type + +class ConfigSection(BaseObject): + __slots__ = ['name', 'description', 'items', 'outline'] + + def __init__(self, name=None, description=None, items=None, outline=None): + self.name = name + self.description = description + self.items = items + self.outline = outline + +class DownloadInfo(BaseObject): + __slots__ = ['fid', 'name', 'speed', 'eta', 'format_eta', 'bleft', 'size', 'format_size', 'percent', 'status', 'statusmsg', 'format_wait', 'wait_until', 'packageID', 'packageName', 'plugin'] + + def __init__(self, fid=None, name=None, speed=None, eta=None, format_eta=None, bleft=None, size=None, format_size=None, percent=None, status=None, statusmsg=None, format_wait=None, wait_until=None, packageID=None, packageName=None, plugin=None): + self.fid = fid + self.name = name + self.speed = speed + self.eta = eta + self.format_eta = format_eta + self.bleft = bleft + self.size = size + self.format_size = format_size + self.percent = percent + self.status = status + self.statusmsg = statusmsg + self.format_wait = format_wait + self.wait_until = wait_until + self.packageID = packageID + self.packageName = packageName + self.plugin = plugin + +class EventInfo(BaseObject): + __slots__ = ['eventname', 'id', 'type', 'destination'] + + def __init__(self, eventname=None, id=None, type=None, destination=None): + self.eventname = eventname + self.id = id + self.type = type + self.destination = destination + +class FileData(BaseObject): + __slots__ = ['fid', 'url', 'name', 'plugin', 'size', 'format_size', 'status', 'statusmsg', 'packageID', 'error', 'order'] + + def __init__(self, fid=None, url=None, name=None, plugin=None, size=None, format_size=None, status=None, statusmsg=None, packageID=None, error=None, order=None): + self.fid = fid + self.url = url + self.name = name + self.plugin = plugin + self.size = size + self.format_size = format_size + self.status = status + self.statusmsg = statusmsg + self.packageID = packageID + self.error = error + self.order = order + +class FileDoesNotExists(Exception): + __slots__ = ['fid'] + + def __init__(self, fid=None): + self.fid = fid + +class InteractionTask(BaseObject): + __slots__ = ['iid', 'input', 'structure', 'preset', 'output', 'data', 'title', 'description', 'plugin'] + + def __init__(self, iid=None, input=None, structure=None, preset=None, output=None, data=None, title=None, description=None, plugin=None): + self.iid = iid + self.input = input + self.structure = structure + self.preset = preset + self.output = output + self.data = data + self.title = title + self.description = description + self.plugin = plugin + +class OnlineCheck(BaseObject): + __slots__ = ['rid', 'data'] + + def __init__(self, rid=None, data=None): + self.rid = rid + self.data = data + +class OnlineStatus(BaseObject): + __slots__ = ['name', 'plugin', 'packagename', 'status', 'size'] + + def __init__(self, name=None, plugin=(None, None), packagename=None, status=None, size=None): + self.name = name + self.plugin = plugin + self.packagename = packagename + self.status = status + self.size = size + +class PackageData(BaseObject): + __slots__ = ['pid', 'name', 'folder', 'site', 'password', 'dest', 'order', 'linksdone', 'sizedone', 'sizetotal', 'linkstotal', 'links', 'fids'] + + def __init__(self, pid=None, name=None, folder=None, site=None, password=None, dest=None, order=None, linksdone=None, sizedone=None, sizetotal=None, linkstotal=None, links=None, fids=None): + self.pid = pid + self.name = name + self.folder = folder + self.site = site + self.password = password + self.dest = dest + self.order = order + self.linksdone = linksdone + self.sizedone = sizedone + self.sizetotal = sizetotal + self.linkstotal = linkstotal + self.links = links + self.fids = fids + +class PackageDoesNotExists(Exception): + __slots__ = ['pid'] + + def __init__(self, pid=None): + self.pid = pid + +class ServerStatus(BaseObject): + __slots__ = ['pause', 'active', 'queue', 'total', 'speed', 'download', 'reconnect'] + + def __init__(self, pause=None, active=None, queue=None, total=None, speed=None, download=None, reconnect=None): + self.pause = pause + self.active = active + self.queue = queue + self.total = total + self.speed = speed + self.download = download + self.reconnect = reconnect + +class ServiceCall(BaseObject): + __slots__ = ['plugin', 'func', 'arguments', 'parseArguments'] + + def __init__(self, plugin=None, func=None, arguments=None, parseArguments=None): + self.plugin = plugin + self.func = func + self.arguments = arguments + self.parseArguments = parseArguments + +class ServiceDoesNotExists(Exception): + __slots__ = ['plugin', 'func'] + + def __init__(self, plugin=None, func=None): + self.plugin = plugin + self.func = func + +class ServiceException(Exception): + __slots__ = ['msg'] + + def __init__(self, msg=None): + self.msg = msg + +class UserData(BaseObject): + __slots__ = ['name', 'email', 'role', 'permission', 'templateName'] + + def __init__(self, name=None, email=None, role=None, permission=None, templateName=None): + self.name = name + self.email = email + self.role = role + self.permission = permission + self.templateName = templateName + +class Iface(object): + def addFiles(self, pid, links): + pass + def addPackage(self, name, links, dest): + pass + def call(self, info): + pass + def checkOnlineStatus(self, urls): + pass + def checkOnlineStatusContainer(self, urls, filename, data): + pass + def checkURLs(self, urls): + pass + def deleteFiles(self, fids): + pass + def deleteFinished(self): + pass + def deletePackages(self, pids): + pass + def freeSpace(self): + pass + def generateAndAddPackages(self, links, dest): + pass + def generatePackages(self, links): + pass + def getAccountTypes(self): + pass + def getAccounts(self, refresh): + pass + def getAllInfo(self): + pass + def getAllUserData(self): + pass + def getCaptchaTask(self, exclusive): + pass + def getCaptchaTaskStatus(self, tid): + pass + def getCollector(self): + pass + def getCollectorData(self): + pass + def getConfig(self): + pass + def getConfigValue(self, category, option, section): + pass + def getEvents(self, uuid): + pass + def getFileData(self, fid): + pass + def getFileOrder(self, pid): + pass + def getInfoByPlugin(self, plugin): + pass + def getLog(self, offset): + pass + def getPackageData(self, pid): + pass + def getPackageInfo(self, pid): + pass + def getPackageOrder(self, destination): + pass + def getPluginConfig(self): + pass + def getQueue(self): + pass + def getQueueData(self): + pass + def getServerVersion(self): + pass + def getServices(self): + pass + def getUserData(self, username, password): + pass + def hasService(self, plugin, func): + pass + def isCaptchaWaiting(self): + pass + def isTimeDownload(self): + pass + def isTimeReconnect(self): + pass + def kill(self): + pass + def login(self, username, password): + pass + def moveFiles(self, fids, pid): + pass + def movePackage(self, destination, pid): + pass + def orderFile(self, fid, position): + pass + def orderPackage(self, pid, position): + pass + def parseURLs(self, html, url): + pass + def pauseServer(self): + pass + def pollResults(self, rid): + pass + def pullFromQueue(self, pid): + pass + def pushToQueue(self, pid): + pass + def recheckPackage(self, pid): + pass + def removeAccount(self, plugin, account): + pass + def restart(self): + pass + def restartFailed(self): + pass + def restartFile(self, fid): + pass + def restartPackage(self, pid): + pass + def setCaptchaResult(self, tid, result): + pass + def setConfigValue(self, category, option, value, section): + pass + def setPackageData(self, pid, data): + pass + def setPackageName(self, pid, name): + pass + def statusDownloads(self): + pass + def statusServer(self): + pass + def stopAllDownloads(self): + pass + def stopDownloads(self, fids): + pass + def togglePause(self): + pass + def toggleReconnect(self): + pass + def unpauseServer(self): + pass + def updateAccount(self, plugin, account, password, options): + pass + def uploadContainer(self, filename, data): + pass |