summaryrefslogtreecommitdiffstats
path: root/pyload/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'pyload/plugins')
-rw-r--r--pyload/plugins/Account.py118
-rw-r--r--pyload/plugins/Base.py2
-rw-r--r--pyload/plugins/Hoster.py8
-rw-r--r--pyload/plugins/ReCaptcha.py22
-rw-r--r--pyload/plugins/accounts/Http.py7
-rw-r--r--pyload/plugins/addons/MultiHoster.py8
-rw-r--r--pyload/plugins/internal/CaptchaService.py2
-rw-r--r--pyload/plugins/network/CurlRequest.py2
-rw-r--r--pyload/plugins/network/XDCCRequest.py162
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