summaryrefslogtreecommitdiffstats
path: root/pyload/plugins/Account.py
diff options
context:
space:
mode:
Diffstat (limited to 'pyload/plugins/Account.py')
-rw-r--r--pyload/plugins/Account.py295
1 files changed, 295 insertions, 0 deletions
diff --git a/pyload/plugins/Account.py b/pyload/plugins/Account.py
new file mode 100644
index 000000000..4492dfa18
--- /dev/null
+++ b/pyload/plugins/Account.py
@@ -0,0 +1,295 @@
+# -*- 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.network.CookieJar import CookieJar
+
+from Base import Base
+
+
+class WrongPassword(Exception):
+ pass
+
+#noinspection PyUnresolvedReferences
+class Account(Base):
+ """
+ Base class for every account plugin.
+ Just overwrite `login` and cookies will be stored and the account becomes accessible in\
+ associated hoster plugin. Plugin should also provide `loadAccountInfo`. \
+ An instance of this class is created for every entered account, it holds all \
+ fields of AccountInfo ttype, and can be set easily at runtime.
+ """
+
+ # constants for special values
+ UNKNOWN = -1
+ 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")
+
+ 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]
+
+ self.loginname = loginname
+ self.options = options
+
+ self.manager = manager
+
+ self.lock = RLock()
+ self.timestamp = 0
+ self.login_ts = 0 # timestamp for login
+ self.cj = CookieJar()
+ self.password = password
+ self.error = None
+
+ 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)
+
+ def init(self):
+ pass
+
+ def login(self, req):
+ """login into account, the cookies will be saved so the user can be recognized
+
+ :param req: `Request` instance
+ """
+ raise NotImplemented
+
+ def relogin(self):
+ """ Force a login. """
+ req = self.getAccountRequest()
+ try:
+ return self._login(req)
+ finally:
+ req.close()
+
+ @lock
+ def _login(self, req):
+ # set timestamp for login
+ self.login_ts = time()
+
+ try:
+ try:
+ self.login(req)
+ except TypeError: #TODO: temporary
+ self.logDebug("Deprecated .login(...) signature omit user, data")
+ self.login(self.loginname, {"password": self.password}, req)
+
+ self.valid = True
+ except WrongPassword:
+ self.logWarning(
+ _("Could not login with account %(user)s | %(msg)s") % {"user": self.loginname
+ , "msg": _("Wrong Password")})
+ self.valid = False
+
+ except Exception, e:
+ self.logWarning(
+ _("Could not login with account %(user)s | %(msg)s") % {"user": self.loginname
+ , "msg": e})
+ self.valid = False
+ if self.core.debug:
+ print_exc()
+
+ return self.valid
+
+ def restoreDefaults(self):
+ self.validuntil = Account.validuntil
+ self.trafficleft = Account.trafficleft
+ 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
+
+ if "activated" in options:
+ self.activated = True if options["activated"] == "True" else False
+
+ 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
+
+ def getAccountRequest(self):
+ return self.core.requestFactory.getRequest(self.cj)
+
+ 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 -1, 0, True
+
+ @lock
+ def getAccountInfo(self, force=False):
+ """retrieve account info's for an user, do **not** overwrite this method!\\
+ just use it to retrieve info's in hoster plugins. see `loadAccountInfo`
+
+ :param name: username
+ :param force: reloads cached account information
+ :return: dictionary with information
+ """
+ if force or self.timestamp + self.info_threshold * 60 < time():
+
+ # make sure to login
+ req = self.getAccountRequest()
+ self.checkLogin(req)
+ self.logInfo(_("Get Account Info for %s") % self.loginname)
+ try:
+ try:
+ infos = self.loadAccountInfo(req)
+ except TypeError: #TODO: temporary
+ self.logDebug("Deprecated .loadAccountInfo(...) signature, omit user argument.")
+ infos = self.loadAccountInfo(self.loginname, req)
+ except Exception, e:
+ infos = {"error": str(e)}
+ self.logError(_("Error: %s") % e)
+ finally:
+ req.close()
+
+ self.logDebug("Account Info: %s" % str(infos))
+ self.timestamp = time()
+
+ self.restoreDefaults() # reset to initial state
+ if type(infos) == dict: # copy result from dict to class
+ for k, v in infos.iteritems():
+ if hasattr(self, k):
+ setattr(self, k, v)
+ else:
+ self.logDebug("Unknown attribute %s=%s" % (k, v))
+
+ #TODO: remove user
+ def loadAccountInfo(self, req):
+ """ Overwrite this method and set account attributes within this method.
+
+ :param user: Deprecated
+ :param req: Request instance
+ :return:
+ """
+ pass
+
+ def getAccountCookies(self, user):
+ self.logDebug("Deprecated method .getAccountCookies -> use account.cj")
+ return self.cj
+
+ def getAccountData(self, user):
+ self.logDebug("Deprecated method .getAccountData -> use fields directly")
+ return {"password": self.password}
+
+ def isPremium(self, user=None):
+ if user: self.logDebug("Deprecated Argument user for .isPremium()", user)
+ return self.premium
+
+ 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"]:
+ time_data = ""
+ try:
+ time_data = self.options["time"]
+ start, end = time_data.split("-")
+ if not compare_time(start.split(":"), end.split(":")):
+ return False
+ except:
+ self.logWarning(_("Your Time %s has a wrong format, use: 1:22-3:44") % time_data)
+
+ if 0 <= self.validuntil < time():
+ return False
+ if self.trafficleft is 0: # test explicitly for 0
+ return False
+
+ return True
+
+ def parseTraffic(self, string): #returns kbyte
+ return parseFileSize(string) / 1024
+
+ def formatTrafficleft(self):
+ if self.trafficleft is None:
+ self.getAccountInfo(force=True)
+ return format_size(self.trafficleft * 1024)
+
+ def wrongPassword(self):
+ raise WrongPassword
+
+ def empty(self, user=None):
+ if user: self.logDebug("Deprecated argument user for .empty()", user)
+
+ self.logWarning(_("Account %s has not enough traffic, checking again in 30min") % self.login)
+
+ self.trafficleft = 0
+ self.scheduleRefresh(30 * 60)
+
+ def expired(self, user=None):
+ if user: self.logDebug("Deprecated argument user for .expired()", user)
+
+ self.logWarning(_("Account %s is expired, checking again in 1h") % user)
+
+ self.validuntil = time() - 1
+ self.scheduleRefresh(60 * 60)
+
+ def scheduleRefresh(self, time=0, force=True):
+ """ add a task for refreshing the account info to the scheduler """
+ self.logDebug("Scheduled Account refresh for %s in %s seconds." % (self.loginname, time))
+ self.core.scheduler.addJob(time, self.getAccountInfo, [force])
+
+ @lock
+ def checkLogin(self, req):
+ """ checks if the user is still logged in """
+ if self.login_ts + self.login_timeout * 60 < time():
+ if self.login_ts: # separate from fresh login to have better debug logs
+ self.logDebug("Reached login timeout for %s" % self.loginname)
+ else:
+ self.logInfo(_("Login with %s") % self.loginname)
+
+ self._login(req)
+ return False
+
+ return True