diff options
Diffstat (limited to 'pyload/plugins')
-rw-r--r-- | pyload/plugins/Account.py | 118 | ||||
-rw-r--r-- | pyload/plugins/Base.py | 2 | ||||
-rw-r--r-- | pyload/plugins/Hoster.py | 8 | ||||
-rw-r--r-- | pyload/plugins/ReCaptcha.py | 22 | ||||
-rw-r--r-- | pyload/plugins/accounts/Http.py | 7 | ||||
-rw-r--r-- | pyload/plugins/addons/MultiHoster.py | 8 | ||||
-rw-r--r-- | pyload/plugins/internal/CaptchaService.py | 2 | ||||
-rw-r--r-- | pyload/plugins/network/CurlRequest.py | 2 | ||||
-rw-r--r-- | pyload/plugins/network/XDCCRequest.py | 162 |
9 files changed, 245 insertions, 86 deletions
diff --git a/pyload/plugins/Account.py b/pyload/plugins/Account.py index 4492dfa18..b3e26ce58 100644 --- a/pyload/plugins/Account.py +++ b/pyload/plugins/Account.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- from time import time -from traceback import print_exc from threading import RLock -from pyload.utils import compare_time, format_size, parseFileSize, lock, to_bool -from pyload.Api import AccountInfo +from pyload.Api import AccountInfo, ConfigItem from pyload.network.CookieJar import CookieJar +from pyload.config.convert import from_string, to_configdata +from pyload.utils import to_string, compare_time, format_size, parseFileSize, lock from Base import Base @@ -29,40 +29,30 @@ class Account(Base): UNLIMITED = -2 # Default values - owner = None valid = True validuntil = -1 trafficleft = -1 maxtraffic = -1 premium = True - activated = True - shared = False #: after that time [in minutes] pyload will relogin the account login_timeout = 600 #: account data will be reloaded after this time info_threshold = 600 - # known options - known_opt = ("time", "limitDL") + @classmethod + def fromInfoData(cls, m, info, password, options): + return cls(m, info.loginname, info.owner, + True if info.activated else False, True if info.shared else False, password, options) - def __init__(self, manager, loginname, password, options): - Base.__init__(self, manager.core) - - if "activated" in options: - self.activated = to_bool(options["activated"]) - else: - self.activated = Account.activated - - for opt in self.known_opt: - if opt not in options: - options[opt] = "" - - for opt in options.keys(): - if opt not in self.known_opt: - del options[opt] + def __init__(self, manager, loginname, owner, activated, shared, password, options): + Base.__init__(self, manager.core, owner) self.loginname = loginname + self.owner = owner + self.activated = activated + self.shared = shared + self.password = password self.options = options self.manager = manager @@ -71,25 +61,58 @@ class Account(Base): self.timestamp = 0 self.login_ts = 0 # timestamp for login self.cj = CookieJar() - self.password = password self.error = None + try: + self.config_data = dict(to_configdata(x) for x in self.__config__) + except Exception, e: + self.logError("Invalid config: %s" % e) + self.config_data = {} + self.init() def toInfoData(self): - return AccountInfo(self.__name__, self.loginname, self.owner, self.valid, self.validuntil, self.trafficleft, - self.maxtraffic, - self.premium, self.activated, self.shared, self.options) + info = AccountInfo(self.__name__, self.loginname, self.owner, self.valid, self.validuntil, self.trafficleft, + self.maxtraffic, self.premium, self.activated, self.shared, self.options) + + info.config = [ConfigItem(name, item.label, item.description, item.input, + to_string(self.getConfig(name))) for name, item in + self.config_data.iteritems()] + return info def init(self): pass + def getConfig(self, option): + """ Gets an option that was configured via the account options dialog and + is only valid for this specific instance.""" + if option not in self.config_data: + return Base.getConfig(self, option) + + if option in self.options: + return self.options[option] + + return self.config_data[option].input.default_value + + def setConfig(self, option, value): + """ Sets a config value for this account instance. Fallsback """ + if option not in self.config_data: + return Base.setConfig(self, option, value) + + value = from_string(value, self.config_data[option].input.type) + # given value is the default value and does not need to be saved at all + if value == self.config_data[option].input.default_value: + if option in self.options: + del self.options[option] + else: + self.options[option] = from_string(value, self.config_data[option].input.type) + def login(self, req): """login into account, the cookies will be saved so the user can be recognized :param req: `Request` instance """ - raise NotImplemented + raise NotImplementedError def relogin(self): """ Force a login. """ @@ -123,8 +146,7 @@ class Account(Base): _("Could not login with account %(user)s | %(msg)s") % {"user": self.loginname , "msg": e}) self.valid = False - if self.core.debug: - print_exc() + self.core.print_exc() return self.valid @@ -134,28 +156,24 @@ class Account(Base): self.maxtraffic = Account.maxtraffic self.premium = Account.premium - def update(self, password=None, options=None): - """ updates the account and returns true if anything changed """ - - self.login_ts = 0 - self.valid = True #set valid, so the login will be retried + def setPassword(self, password): + """ updates the password and returns true if anything changed """ - if "activated" in options: - self.activated = True if options["activated"] == "True" else False + if password != self.password: + self.login_ts = 0 + self.valid = True #set valid, so the login will be retried - if password: self.password = password - self.relogin() return True - if options: - # remove unknown options - for opt in options.keys(): - if opt not in self.known_opt: - del options[opt] - before = self.options - self.options.update(options) - return self.options != before + return False + + def updateConfig(self, items): + """ Updates the accounts options from config items """ + for item in items: + # Check if a valid option + if item.name in self.config_data: + self.setConfig(item.name, item.value) def getAccountRequest(self): return self.core.requestFactory.getRequest(self.cj) @@ -163,7 +181,7 @@ class Account(Base): def getDownloadSettings(self): """ Can be overwritten to change download settings. Default is no chunkLimit, max dl limit, resumeDownload - :return: (chunkLimit, limitDL, resumeDownload) / (int, int ,bool) + :return: (chunkLimit, limitDL, resumeDownload) / (int, int, bool) """ return -1, 0, True @@ -229,9 +247,11 @@ class Account(Base): def isUsable(self): """Check several constraints to determine if account should be used""" + if not self.valid or not self.activated: return False - if self.options["time"]: + # TODO: not in ui currently + if "time" in self.options and self.options["time"]: time_data = "" try: time_data = self.options["time"] diff --git a/pyload/plugins/Base.py b/pyload/plugins/Base.py index cd4831d82..3ca8abdd0 100644 --- a/pyload/plugins/Base.py +++ b/pyload/plugins/Base.py @@ -92,7 +92,7 @@ class Base(object): self.evm = core.eventManager #: :class:`InteractionManager` self.im = core.interactionManager - if user: + if user is not None: #: :class:`Api`, user api when user is set self.api = self.core.api.withUserContext(user) if not self.api: diff --git a/pyload/plugins/Hoster.py b/pyload/plugins/Hoster.py index 44b10899d..b3be7a9e9 100644 --- a/pyload/plugins/Hoster.py +++ b/pyload/plugins/Hoster.py @@ -82,17 +82,13 @@ class Hoster(Base): self.ocr = None #captcha reader instance #: account handler instance, see :py:class:`Account` - self.account = self.core.accountManager.getAccountForPlugin(self.__name__) + self.account = self.core.accountManager.selectAccount(self.__name__, self.user) #: premium status self.premium = False - #: username/login - self.user = None - if self.account and not self.account.isUsable(): self.account = None if self.account: - self.user = self.account.loginname - #: Browser instance, see `network.Browser` + #: Request instance bound to account self.req = self.account.getAccountRequest() # Default: -1, True, True self.chunkLimit, self.limitDL, self.resumeDownload = self.account.getDownloadSettings() diff --git a/pyload/plugins/ReCaptcha.py b/pyload/plugins/ReCaptcha.py deleted file mode 100644 index e47522b4a..000000000 --- a/pyload/plugins/ReCaptcha.py +++ /dev/null @@ -1,22 +0,0 @@ -import re - -class ReCaptcha(): - def __init__(self, plugin): - self.plugin = plugin - self.plugin.logDebug("Deprecated usage of ReCaptcha: Use CaptchaService instead") - - def challenge(self, id): - js = self.plugin.req.load("http://www.google.com/recaptcha/api/challenge", get={"k":id}, cookies=True) - - try: - challenge = re.search("challenge : '(.*?)',", js).group(1) - server = re.search("server : '(.*?)',", js).group(1) - except: - self.plugin.fail("recaptcha error") - result = self.result(server,challenge) - - return challenge, result - - def result(self, server, challenge): - return self.plugin.decryptCaptcha("%simage"%server, get={"c":challenge}, cookies=True, imgtype="jpg") - diff --git a/pyload/plugins/accounts/Http.py b/pyload/plugins/accounts/Http.py index 5701d1f03..de9490b2c 100644 --- a/pyload/plugins/accounts/Http.py +++ b/pyload/plugins/accounts/Http.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from module.plugins.Account import Account +from pyload.plugins.Account import Account class Http(Account): @@ -11,4 +11,9 @@ class Http(Account): __author_name__ = ("zoidberg") __author_mail__ = ("zoidberg@mujmail.cz") + __config__ = [("domain", "str", "Domain", "")] + login_timeout = info_threshold = 1000000 + + def login(self, req): + pass
\ No newline at end of file diff --git a/pyload/plugins/addons/MultiHoster.py b/pyload/plugins/addons/MultiHoster.py index 329a87e4a..446dfe922 100644 --- a/pyload/plugins/addons/MultiHoster.py +++ b/pyload/plugins/addons/MultiHoster.py @@ -72,18 +72,18 @@ class MultiHoster(Addon): @AddEventListener("account:deleted") - def refreshAccounts(self, plugin=None, user=None): + def refreshAccounts(self, plugin=None, loginname=None): self.logDebug("Re-checking accounts") self.plugins = {} - for name, account in self.core.accountManager.iterAccounts(): + for plugin, account in self.core.accountManager.iterAccounts(): if isinstance(account, MultiHosterAccount) and account.isUsable(): self.addHoster(account) @AddEventListener("account:updated") - def refreshAccount(self, plugin, user): + def refreshAccount(self, plugin, loginname): - account = self.core.accountManager.getAccount(plugin, user) + account = self.core.accountManager.getAccount(plugin, loginname) if isinstance(account, MultiHosterAccount) and account.isUsable(): self.addHoster(account) diff --git a/pyload/plugins/internal/CaptchaService.py b/pyload/plugins/internal/CaptchaService.py index b912436a7..4f903e3e6 100644 --- a/pyload/plugins/internal/CaptchaService.py +++ b/pyload/plugins/internal/CaptchaService.py @@ -60,8 +60,6 @@ class AdsCaptcha(CaptchaService): return self.plugin.decryptCaptcha("%sChallenge.aspx" % server, get={"cid": challenge, "dummy": random()}, cookies=True, imgtype="jpg") class SolveMedia(CaptchaService): - def __init__(self,plugin): - self.plugin = plugin def challenge(self, src): html = self.plugin.req.load("http://api.solvemedia.com/papi/challenge.noscript?k=%s" % src, cookies=True) diff --git a/pyload/plugins/network/CurlRequest.py b/pyload/plugins/network/CurlRequest.py index 775c98522..b7e37900b 100644 --- a/pyload/plugins/network/CurlRequest.py +++ b/pyload/plugins/network/CurlRequest.py @@ -152,7 +152,6 @@ class CurlRequest(Request): url = "%s?%s" % (url, get) self.c.setopt(pycurl.URL, url) - self.lastURL = url if post: self.c.setopt(pycurl.POST, 1) @@ -222,6 +221,7 @@ class CurlRequest(Request): rep = self.getResponse() self.c.setopt(pycurl.POSTFIELDS, "") + self.lastURL = url self.lastEffectiveURL = self.c.getinfo(pycurl.EFFECTIVE_URL) self.code = self.verifyHeader() diff --git a/pyload/plugins/network/XDCCRequest.py b/pyload/plugins/network/XDCCRequest.py new file mode 100644 index 000000000..6b692ab38 --- /dev/null +++ b/pyload/plugins/network/XDCCRequest.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. + + @author: jeix +""" + +import socket +import re + +from os import remove +from os.path import exists + +from time import time + +import struct +from select import select + +from pyload.plugins.Plugin import Abort + +# TODO: This must be adapted to the new request interfaces +class XDCCRequest(): + def __init__(self, timeout=30, proxies={}): + + self.proxies = proxies + self.timeout = timeout + + self.filesize = 0 + self.recv = 0 + self.speed = 0 + + self.abort = False + + + def createSocket(self): + # proxytype = None + # proxy = None + # if self.proxies.has_key("socks5"): + # proxytype = socks.PROXY_TYPE_SOCKS5 + # proxy = self.proxies["socks5"] + # elif self.proxies.has_key("socks4"): + # proxytype = socks.PROXY_TYPE_SOCKS4 + # proxy = self.proxies["socks4"] + # if proxytype: + # sock = socks.socksocket() + # t = _parse_proxy(proxy) + # sock.setproxy(proxytype, addr=t[3].split(":")[0], port=int(t[3].split(":")[1]), username=t[1], password=t[2]) + # else: + # sock = socket.socket() + # return sock + + return socket.socket() + + def download(self, ip, port, filename, irc, progressNotify=None): + + ircbuffer = "" + lastUpdate = time() + cumRecvLen = 0 + + dccsock = self.createSocket() + + dccsock.settimeout(self.timeout) + dccsock.connect((ip, port)) + + if exists(filename): + i = 0 + nameParts = filename.rpartition(".") + while True: + newfilename = "%s-%d%s%s" % (nameParts[0], i, nameParts[1], nameParts[2]) + i += 1 + + if not exists(newfilename): + filename = newfilename + break + + fh = open(filename, "wb") + + # recv loop for dcc socket + while True: + if self.abort: + dccsock.close() + fh.close() + remove(filename) + raise Abort() + + self._keepAlive(irc, ircbuffer) + + data = dccsock.recv(4096) + dataLen = len(data) + self.recv += dataLen + + cumRecvLen += dataLen + + now = time() + timespan = now - lastUpdate + if timespan > 1: + self.speed = cumRecvLen / timespan + cumRecvLen = 0 + lastUpdate = now + + if progressNotify: + progressNotify(self.percent) + + + if not data: + break + + fh.write(data) + + # acknowledge data by sending number of received bytes + dccsock.send(struct.pack('!I', self.recv)) + + dccsock.close() + fh.close() + + return filename + + def _keepAlive(self, sock, readbuffer): + fdset = select([sock], [], [], 0) + if sock not in fdset[0]: + return + + readbuffer += sock.recv(1024) + temp = readbuffer.split("\n") + readbuffer = temp.pop() + + for line in temp: + line = line.rstrip() + first = line.split() + if first[0] == "PING": + sock.send("PONG %s\r\n" % first[1]) + + def abortDownloads(self): + self.abort = True + + @property + def size(self): + return self.filesize + + @property + def arrived(self): + return self.recv + + @property + def percent(self): + if not self.filesize: return 0 + return (self.recv * 100) / self.filesize + + def close(self): + pass |