summaryrefslogtreecommitdiffstats
path: root/pyload/plugins/internal
diff options
context:
space:
mode:
Diffstat (limited to 'pyload/plugins/internal')
-rw-r--r--pyload/plugins/internal/Account.py305
-rw-r--r--pyload/plugins/internal/Addon.py166
-rw-r--r--pyload/plugins/internal/Captcha.py51
-rw-r--r--pyload/plugins/internal/Container.py64
-rw-r--r--pyload/plugins/internal/Crypter.py107
-rw-r--r--pyload/plugins/internal/DeadCrypter.py2
-rw-r--r--pyload/plugins/internal/DeadHoster.py2
-rw-r--r--pyload/plugins/internal/Hoster.py21
-rw-r--r--pyload/plugins/internal/MultiHoster.py4
-rw-r--r--pyload/plugins/internal/OCR.py314
-rw-r--r--pyload/plugins/internal/SimpleCrypter.py6
-rw-r--r--pyload/plugins/internal/SimpleHoster.py4
-rw-r--r--pyload/plugins/internal/UpdateManager.py2
-rw-r--r--pyload/plugins/internal/XFSAccount.py122
-rw-r--r--pyload/plugins/internal/XFSCrypter.py29
-rw-r--r--pyload/plugins/internal/XFSHoster.py345
-rw-r--r--pyload/plugins/internal/XFSPAccount.py96
-rw-r--r--pyload/plugins/internal/XFSPHoster.py54
18 files changed, 1534 insertions, 160 deletions
diff --git a/pyload/plugins/internal/Account.py b/pyload/plugins/internal/Account.py
new file mode 100644
index 000000000..e6895f119
--- /dev/null
+++ b/pyload/plugins/internal/Account.py
@@ -0,0 +1,305 @@
+# -*- coding: utf-8 -*-
+
+from random import choice
+from time import time
+from traceback import print_exc
+from threading import RLock
+
+from pyload.plugins.Plugin import Base
+from pyload.utils import compare_time, parseFileSize, lock
+
+
+class WrongPassword(Exception):
+ pass
+
+
+class Account(Base):
+ """
+ Base class for every Account plugin.
+ Just overwrite `login` and cookies will be stored and account becomes accessible in\
+ associated hoster plugin. Plugin should also provide `loadAccountInfo`
+ """
+ __name__ = "Account"
+ __type__ = "account"
+ __version__ = "0.03"
+
+ __description__ = """Base account plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("mkaay", "mkaay@mkaay.de")]
+
+
+ #: after that time (in minutes) pyload will relogin the account
+ login_timeout = 10 * 60
+ #: after that time (in minutes) account data will be reloaded
+ info_threshold = 10 * 60
+
+
+ def __init__(self, manager, accounts):
+ Base.__init__(self, manager.core)
+
+ self.manager = manager
+ self.accounts = {}
+ self.infos = {} #: cache for account information
+ self.lock = RLock()
+ self.timestamps = {}
+
+ self.init()
+
+ self.setAccounts(accounts)
+
+
+ def init(self):
+ pass
+
+
+ def login(self, user, data, req):
+ """login into account, the cookies will be saved so user can be recognized
+
+ :param user: loginname
+ :param data: data dictionary
+ :param req: `Request` instance
+ """
+ pass
+
+
+ @lock
+ def _login(self, user, data):
+ # set timestamp for login
+ self.timestamps[user] = time()
+
+ req = self.getAccountRequest(user)
+ try:
+ self.login(user, data, req)
+ except WrongPassword:
+ self.logWarning(
+ _("Could not login with account %(user)s | %(msg)s") % {"user": user,
+ "msg": _("Wrong Password")})
+ success = data['valid'] = False
+ except Exception, e:
+ self.logWarning(
+ _("Could not login with account %(user)s | %(msg)s") % {"user": user,
+ "msg": e})
+ success = data['valid'] = False
+ if self.core.debug:
+ print_exc()
+ else:
+ success = True
+ finally:
+ if req:
+ req.close()
+ return success
+
+
+ def relogin(self, user):
+ req = self.getAccountRequest(user)
+ if req:
+ req.cj.clear()
+ req.close()
+ if user in self.infos:
+ del self.infos[user] #delete old information
+
+ return self._login(user, self.accounts[user])
+
+
+ def setAccounts(self, accounts):
+ self.accounts = accounts
+ for user, data in self.accounts.iteritems():
+ self._login(user, data)
+ self.infos[user] = {}
+
+
+ def updateAccounts(self, user, password=None, options={}):
+ """ updates account and return true if anything changed """
+
+ if user in self.accounts:
+ self.accounts[user]['valid'] = True #do not remove or accounts will not login
+ if password:
+ self.accounts[user]['password'] = password
+ self.relogin(user)
+ return True
+ if options:
+ before = self.accounts[user]['options']
+ self.accounts[user]['options'].update(options)
+ return self.accounts[user]['options'] != before
+ else:
+ self.accounts[user] = {"password": password, "options": options, "valid": True}
+ self._login(user, self.accounts[user])
+ return True
+
+
+ def removeAccount(self, user):
+ if user in self.accounts:
+ del self.accounts[user]
+ if user in self.infos:
+ del self.infos[user]
+ if user in self.timestamps:
+ del self.timestamps[user]
+
+
+ @lock
+ def getAccountInfo(self, name, force=False):
+ """retrieve account infos for an user, do **not** overwrite this method!\\
+ just use it to retrieve infos in hoster plugins. see `loadAccountInfo`
+
+ :param name: username
+ :param force: reloads cached account information
+ :return: dictionary with information
+ """
+ data = Account.loadAccountInfo(self, name)
+
+ if force or name not in self.infos:
+ self.logDebug("Get Account Info for %s" % name)
+ req = self.getAccountRequest(name)
+
+ try:
+ infos = self.loadAccountInfo(name, req)
+ if not type(infos) == dict:
+ raise Exception("Wrong return format")
+ except Exception, e:
+ infos = {"error": str(e)}
+ print_exc()
+
+ if req:
+ req.close()
+
+ self.logDebug("Account Info: %s" % infos)
+
+ infos['timestamp'] = time()
+ self.infos[name] = infos
+ elif "timestamp" in self.infos[name] and self.infos[name][
+ "timestamp"] + self.info_threshold * 60 < time():
+ self.logDebug("Reached timeout for account data")
+ self.scheduleRefresh(name)
+
+ data.update(self.infos[name])
+ return data
+
+
+ def isPremium(self, user):
+ info = self.getAccountInfo(user)
+ return info['premium']
+
+
+ def loadAccountInfo(self, name, req=None):
+ """this should be overwritten in account plugin,\
+ and retrieving account information for user
+
+ :param name:
+ :param req: `Request` instance
+ :return:
+ """
+ return {"validuntil": None, #: -1 for unlimited
+ "login": name,
+ # "password": self.accounts[name]['password'], #: commented due security reason
+ "options": self.accounts[name]['options'],
+ "valid": self.accounts[name]['valid'],
+ "trafficleft": None, #: in kb, -1 for unlimited
+ "maxtraffic": None,
+ "premium": None,
+ "timestamp": 0, #: time this info was retrieved
+ "type": self.__name__}
+
+
+ def getAllAccounts(self, force=False):
+ return [self.getAccountInfo(user, force) for user, data in self.accounts.iteritems()]
+
+
+ def getAccountRequest(self, user=None):
+ if not user:
+ user, data = self.selectAccount()
+ if not user:
+ return None
+
+ req = self.core.requestFactory.getRequest(self.__name__, user)
+ return req
+
+
+ def getAccountCookies(self, user=None):
+ if not user:
+ user, data = self.selectAccount()
+ if not user:
+ return None
+
+ cj = self.core.requestFactory.getCookieJar(self.__name__, user)
+ return cj
+
+
+ def getAccountData(self, user):
+ return self.accounts[user]
+
+
+ def selectAccount(self):
+ """ returns an valid account name and data"""
+ usable = []
+ for user, data in self.accounts.iteritems():
+ if not data['valid']: continue
+
+ if "time" in data['options'] and data['options']['time']:
+ time_data = ""
+ try:
+ time_data = data['options']['time'][0]
+ start, end = time_data.split("-")
+ if not compare_time(start.split(":"), end.split(":")):
+ continue
+ except:
+ self.logWarning(_("Your Time %s has wrong format, use: 1:22-3:44") % time_data)
+
+ if user in self.infos:
+ if "validuntil" in self.infos[user]:
+ if self.infos[user]['validuntil'] > 0 and time() > self.infos[user]['validuntil']:
+ continue
+ if "trafficleft" in self.infos[user]:
+ if self.infos[user]['trafficleft'] == 0:
+ continue
+
+ usable.append((user, data))
+
+ if not usable: return None, None
+ return choice(usable)
+
+
+ def canUse(self):
+ return False if self.selectAccount() == (None, None) else True
+
+
+ def parseTraffic(self, string): #returns kbyte
+ return parseFileSize(string)
+
+
+ def wrongPassword(self):
+ raise WrongPassword
+
+
+ def empty(self, user):
+ if user in self.infos:
+ self.logWarning(_("Account %s has not enough traffic, checking again in 30min") % user)
+
+ self.infos[user].update({"trafficleft": 0})
+ self.scheduleRefresh(user, 30 * 60)
+
+
+ def expired(self, user):
+ if user in self.infos:
+ self.logWarning(_("Account %s is expired, checking again in 1h") % user)
+
+ self.infos[user].update({"validuntil": time() - 1})
+ self.scheduleRefresh(user, 60 * 60)
+
+
+ def scheduleRefresh(self, user, time=0, force=True):
+ """ add task to refresh account info to sheduler """
+ self.logDebug("Scheduled Account refresh for %s in %s seconds." % (user, time))
+ self.core.scheduler.addJob(time, self.getAccountInfo, [user, force])
+
+
+ @lock
+ def checkLogin(self, user):
+ """ checks if user is still logged in """
+ if user in self.timestamps:
+ if self.login_timeout > 0 and self.timestamps[user] + self.login_timeout * 60 < time():
+ self.logDebug("Reached login timeout for %s" % user)
+ return self.relogin(user)
+ else:
+ return True
+ else:
+ return False
diff --git a/pyload/plugins/internal/Addon.py b/pyload/plugins/internal/Addon.py
new file mode 100644
index 000000000..1aa51c69a
--- /dev/null
+++ b/pyload/plugins/internal/Addon.py
@@ -0,0 +1,166 @@
+# -*- coding: utf-8 -*-
+
+from traceback import print_exc
+
+from pyload.plugins.Plugin import Base
+
+
+class Expose(object):
+ """ used for decoration to declare rpc services """
+
+ def __new__(cls, f, *args, **kwargs):
+ addonManager.addRPC(f.__module__, f.func_name, f.func_doc)
+ return f
+
+
+def threaded(f):
+
+ def run(*args,**kwargs):
+ addonManager.startThread(f, *args, **kwargs)
+ return run
+
+
+class Addon(Base):
+ """
+ Base class for addon plugins.
+ """
+ __name__ = "Addon"
+ __type__ = "addon"
+ __version__ = "0.03"
+
+ __config__ = [] #: [("name", "type", "desc", "default")]
+
+ __description__ = """Interface for addon"""
+ __license__ = "GPLv3"
+ __authors__ = [("mkaay", "mkaay@mkaay.de"),
+ ("RaNaN", "RaNaN@pyload.org")]
+
+
+ #: automatically register event listeners for functions, attribute will be deleted dont use it yourself
+ event_map = None
+
+ # Alternative to event_map
+ #: List of events the plugin can handle, name the functions exactly like eventname.
+ event_list = None # dont make duplicate entries in event_map
+
+ #: periodic call interval in secondc
+ interval = 60
+
+
+ def __init__(self, core, manager):
+ Base.__init__(self, core)
+
+ #: Provide information in dict here, usable by API `getInfo`
+ self.info = {}
+
+ #: Callback of periodical job task, used by AddonManager
+ self.cb = None
+
+ #: `AddonManager`
+ self.manager = manager
+
+ #register events
+ if self.event_map:
+ for event, funcs in self.event_map.iteritems():
+ if type(funcs) in (list, tuple):
+ for f in funcs:
+ self.manager.addEvent(event, getattr(self,f))
+ else:
+ self.manager.addEvent(event, getattr(self,funcs))
+
+ #delete for various reasons
+ self.event_map = None
+
+ if self.event_list:
+ for f in self.event_list:
+ self.manager.addEvent(f, getattr(self,f))
+
+ self.event_list = None
+
+ self.setup()
+ self.initPeriodical()
+
+
+ def initPeriodical(self):
+ if self.interval >=1:
+ self.cb = self.core.scheduler.addJob(0, self._periodical, threaded=False)
+
+ def _periodical(self):
+ try:
+ if self.isActivated():
+ self.periodical()
+ except Exception, e:
+ self.logError(_("Error executing addon: %s") % e)
+ if self.core.debug:
+ print_exc()
+
+ self.cb = self.core.scheduler.addJob(self.interval, self._periodical, threaded=False)
+
+
+ def __repr__(self):
+ return "<Addon %s>" % self.__name__
+
+
+ def setup(self):
+ """ more init stuff if needed """
+ pass
+
+
+ def unload(self):
+ """ called when addon was deactivated """
+ pass
+
+
+ def isActivated(self):
+ """ checks if addon is activated"""
+ return self.config.getPlugin(self.__name__, "activated")
+
+
+ #event methods - overwrite these if needed
+ def coreReady(self):
+ pass
+
+
+ def coreExiting(self):
+ pass
+
+
+ def downloadPreparing(self, pyfile):
+ pass
+
+
+ def downloadFinished(self, pyfile):
+ pass
+
+
+ def downloadFailed(self, pyfile):
+ pass
+
+
+ def packageFinished(self, pypack):
+ pass
+
+
+ def beforeReconnecting(self, ip):
+ pass
+
+
+ def afterReconnecting(self, ip):
+ pass
+
+
+ def periodical(self):
+ pass
+
+
+ def newCaptchaTask(self, task):
+ """ new captcha task for the plugin, it MUST set the handler and timeout or will be ignored """
+ pass
+
+
+ def captchaCorrect(self, task):
+ pass
+
+
+ def captchaInvalid(self, task):
+ pass
diff --git a/pyload/plugins/internal/Captcha.py b/pyload/plugins/internal/Captcha.py
new file mode 100644
index 000000000..b2fd980e2
--- /dev/null
+++ b/pyload/plugins/internal/Captcha.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+from pyload.plugins.Plugin import Plugin
+
+
+class Captcha(Plugin):
+ __name__ = "Captcha"
+ __type__ = "captcha"
+ __version__ = "0.14"
+
+ __description__ = """Base captcha service plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("pyLoad Team", "admin@pyload.org")]
+
+
+ KEY_PATTERN = None
+
+ key = None #: last key detected
+
+
+ def __init__(self, plugin):
+ self.plugin = plugin
+
+
+ def detect_key(self, html=None):
+ if not html:
+ if hasattr(self.plugin, "html") and self.plugin.html:
+ html = self.plugin.html
+ else:
+ errmsg = _("%s html not found") % self.__name__
+ self.plugin.fail(errmsg) #@TODO: replace all plugin.fail(errmsg) with plugin.error(errmsg) in 0.4.10
+ raise TypeError(errmsg)
+
+ m = re.search(self.KEY_PATTERN, html)
+ if m:
+ self.key = m.group("KEY")
+ self.plugin.logDebug("%s key: %s" % (self.__name__, self.key))
+ return self.key
+ else:
+ self.plugin.logDebug("%s key not found" % self.__name__)
+ return None
+
+
+ def challenge(self, key=None):
+ raise NotImplementedError
+
+
+ def result(self, server, challenge):
+ raise NotImplementedError
diff --git a/pyload/plugins/internal/Container.py b/pyload/plugins/internal/Container.py
new file mode 100644
index 000000000..4bd6644f9
--- /dev/null
+++ b/pyload/plugins/internal/Container.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+from os import remove
+from os.path import basename, exists
+
+from pyload.plugins.internal.Crypter import Crypter
+from pyload.utils import safe_join
+
+
+class Container(Crypter):
+ __name__ = "Container"
+ __type__ = "container"
+ __version__ = "0.01"
+
+ __pattern__ = r'^unmatchable$'
+ __config__ = [] #: [("name", "type", "desc", "default")]
+
+ __description__ = """Base container decrypter plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("mkaay", "mkaay@mkaay.de")]
+
+
+ def preprocessing(self, thread):
+ """prepare"""
+
+ self.setup()
+ self.thread = thread
+
+ self.loadToDisk()
+
+ self.decrypt(self.pyfile)
+ self.deleteTmp()
+
+ self.createPackages()
+
+
+ def loadToDisk(self):
+ """loads container to disk if its stored remotely and overwrite url,
+ or check existent on several places at disk"""
+
+ if self.pyfile.url.startswith("http"):
+ self.pyfile.name = re.findall("([^\/=]+)", self.pyfile.url)[-1]
+ content = self.load(self.pyfile.url)
+ self.pyfile.url = safe_join(self.config['general']['download_folder'], self.pyfile.name)
+ try:
+ with open(self.pyfile.url, "wb") as f:
+ f.write(content)
+ except IOError, e:
+ self.fail(str(e))
+
+ else:
+ self.pyfile.name = basename(self.pyfile.url)
+ if not exists(self.pyfile.url):
+ if exists(safe_join(pypath, self.pyfile.url)):
+ self.pyfile.url = safe_join(pypath, self.pyfile.url)
+ else:
+ self.fail(_("File not exists"))
+
+
+ def deleteTmp(self):
+ if self.pyfile.name.startswith("tmp_"):
+ remove(self.pyfile.url)
diff --git a/pyload/plugins/internal/Crypter.py b/pyload/plugins/internal/Crypter.py
new file mode 100644
index 000000000..ed92357a5
--- /dev/null
+++ b/pyload/plugins/internal/Crypter.py
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+
+from pyload.plugins.Plugin import Plugin
+from pyload.utils import save_filename
+
+
+class Crypter(Plugin):
+ __name__ = "Crypter"
+ __type__ = "crypter"
+ __version__ = "0.05"
+
+ __pattern__ = r'^unmatchable$'
+ __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), #: Overrides core.config['general']['folder_per_package']
+ ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
+
+ __description__ = """Base decrypter plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
+
+
+ html = None #: last html loaded
+
+
+ def __init__(self, pyfile):
+ #: Put all packages here. It's a list of tuples like: ( name, [list of links], folder )
+ self.packages = []
+
+ #: List of urls, pyLoad will generate packagenames
+ self.urls = []
+
+ Plugin.__init__(self, pyfile)
+
+
+ def process(self, pyfile):
+ """ main method """
+
+ self.decrypt(pyfile)
+
+ if self.urls:
+ self.generatePackages()
+
+ elif not self.packages:
+ self.error(_("No link extracted"), "decrypt")
+
+ self.createPackages()
+
+
+ def decrypt(self, pyfile):
+ raise NotImplementedError
+
+
+ def generatePackages(self):
+ """ generate new packages from self.urls """
+
+ packages = map(lambda name, links: (name, links, None), self.core.api.generatePackages(self.urls).iteritems())
+ self.packages.extend(packages)
+
+
+ def createPackages(self):
+ """ create new packages from self.packages """
+
+ package_folder = self.pyfile.package().folder
+ package_password = self.pyfile.package().password
+ package_queue = self.pyfile.package().queue
+
+ folder_per_package = self.config['general']['folder_per_package']
+ try:
+ use_subfolder = self.getConfig('use_subfolder')
+ except:
+ use_subfolder = folder_per_package
+ try:
+ subfolder_per_package = self.getConfig('subfolder_per_package')
+ except:
+ subfolder_per_package = True
+
+ for pack in self.packages:
+ name, links, folder = pack
+
+ self.logDebug("Parsed package: %s" % name,
+ "%d links" % len(links),
+ "Saved to folder: %s" % folder if folder else "Saved to download folder")
+
+ links = map(lambda x: x.decode("utf-8"), links)
+
+ pid = self.core.api.addPackage(name, links, package_queue)
+
+ if package_password:
+ self.core.api.setPackageData(pid, {"password": package_password})
+
+ setFolder = lambda x: self.core.api.setPackageData(pid, {"folder": x or ""}) #: Workaround to do not break API addPackage method
+
+ if use_subfolder:
+ if not subfolder_per_package:
+ setFolder(package_folder)
+ self.logDebug("Set package %(name)s folder to: %(folder)s" % {"name": name, "folder": folder})
+
+ elif not folder_per_package or name != folder:
+ if not folder:
+ folder = name.replace("http://", "").replace(":", "").replace("/", "_").replace("\\", "_")
+
+ folder = save_filename(folder) #@TODO: move to core code
+
+ setFolder(folder)
+ self.logDebug("Set package %(name)s folder to: %(folder)s" % {"name": name, "folder": folder})
+
+ elif folder_per_package:
+ setFolder(None)
diff --git a/pyload/plugins/internal/DeadCrypter.py b/pyload/plugins/internal/DeadCrypter.py
index 83aed6d43..ad99c6a13 100644
--- a/pyload/plugins/internal/DeadCrypter.py
+++ b/pyload/plugins/internal/DeadCrypter.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from pyload.plugins.base.Crypter import Crypter as _Crypter
+from pyload.plugins.internal.Crypter import Crypter as _Crypter
class DeadCrypter(_Crypter):
diff --git a/pyload/plugins/internal/DeadHoster.py b/pyload/plugins/internal/DeadHoster.py
index 76a7b5d80..3c59f1489 100644
--- a/pyload/plugins/internal/DeadHoster.py
+++ b/pyload/plugins/internal/DeadHoster.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-from pyload.plugins.base.Hoster import Hoster as _Hoster
+from pyload.plugins.internal.Hoster import Hoster as _Hoster
def create_getInfo(plugin):
diff --git a/pyload/plugins/internal/Hoster.py b/pyload/plugins/internal/Hoster.py
new file mode 100644
index 000000000..ea225262e
--- /dev/null
+++ b/pyload/plugins/internal/Hoster.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+
+from pyload.plugins.Plugin import Plugin
+
+
+def getInfo(self):
+ #result = [ .. (name, size, status, url) .. ]
+ return
+
+
+class Hoster(Plugin):
+ __name__ = "Hoster"
+ __type__ = "hoster"
+ __version__ = "0.02"
+
+ __pattern__ = r'^unmatchable$'
+ __config__ = [] #: [("name", "type", "desc", "default")]
+
+ __description__ = """Base hoster plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("mkaay", "mkaay@mkaay.de")]
diff --git a/pyload/plugins/internal/MultiHoster.py b/pyload/plugins/internal/MultiHoster.py
index 97cbb4591..88a9c888b 100644
--- a/pyload/plugins/internal/MultiHoster.py
+++ b/pyload/plugins/internal/MultiHoster.py
@@ -2,14 +2,14 @@
import re
-from pyload.plugins.base.Addon import Addon
+from pyload.plugins.internal.Addon import Addon
from pyload.utils import remove_chars
class MultiHoster(Addon):
__name__ = "MultiHoster"
__type__ = "addon"
- __version__ = "0.20"
+ __version__ = "0.20"
__description__ = """Generic MultiHoster plugin"""
__license__ = "GPLv3"
diff --git a/pyload/plugins/internal/OCR.py b/pyload/plugins/internal/OCR.py
new file mode 100644
index 000000000..b5ee30cb7
--- /dev/null
+++ b/pyload/plugins/internal/OCR.py
@@ -0,0 +1,314 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import with_statement
+
+try:
+ from PIL import Image, GifImagePlugin, JpegImagePlugin, PngImagePlugin, TiffImagePlugin
+except ImportError:
+ import Image, GifImagePlugin, JpegImagePlugin, PngImagePlugin, TiffImagePlugin
+
+import logging
+import subprocess
+
+from os.path import abspath, join
+
+
+class OCR(object):
+ __name__ = "OCR"
+ __type__ = "ocr"
+ __version__ = "0.01"
+
+ __description__ = """OCR base plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("pyLoad Team", "admin@pyload.org")]
+
+
+ def __init__(self):
+ self.logger = logging.getLogger("log")
+
+
+ def load_image(self, image):
+ self.image = Image.open(image)
+ self.pixels = self.image.load()
+ self.result_captcha = ''
+
+
+ def unload(self):
+ """delete all tmp images"""
+ pass
+
+
+ def threshold(self, value):
+ self.image = self.image.point(lambda a: a * value + 10)
+
+
+ def run(self, command):
+ """Run a command"""
+
+ popen = subprocess.Popen(command, bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ popen.wait()
+ output = popen.stdout.read() + " | " + popen.stderr.read()
+ popen.stdout.close()
+ popen.stderr.close()
+ self.logger.debug("Tesseract ReturnCode %s Output: %s" % (popen.returncode, output))
+
+
+ def run_tesser(self, subset=False, digits=True, lowercase=True, uppercase=True):
+ #tmpTif = tempfile.NamedTemporaryFile(suffix=".tif")
+ try:
+ tmpTif = open(join("tmp", "tmpTif_%s.tif" % self.__name__), "wb")
+ tmpTif.close()
+
+ #tmpTxt = tempfile.NamedTemporaryFile(suffix=".txt")
+ tmpTxt = open(join("tmp", "tmpTxt_%s.txt" % self.__name__), "wb")
+ tmpTxt.close()
+
+ except IOError, e:
+ self.logError(e)
+ return
+
+ self.logger.debug("save tiff")
+ self.image.save(tmpTif.name, 'TIFF')
+
+ if os.name == "nt":
+ tessparams = [join(pypath, "tesseract", "tesseract.exe")]
+ else:
+ tessparams = ['tesseract']
+
+ tessparams.extend([abspath(tmpTif.name), abspath(tmpTxt.name).replace(".txt", "")] )
+
+ if subset and (digits or lowercase or uppercase):
+ #tmpSub = tempfile.NamedTemporaryFile(suffix=".subset")
+ tmpSub = open(join("tmp", "tmpSub_%s.subset" % self.__name__), "wb")
+ tmpSub.write("tessedit_char_whitelist ")
+ if digits:
+ tmpSub.write("0123456789")
+ if lowercase:
+ tmpSub.write("abcdefghijklmnopqrstuvwxyz")
+ if uppercase:
+ tmpSub.write("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
+ tmpSub.write("\n")
+ tessparams.append("nobatch")
+ tessparams.append(abspath(tmpSub.name))
+ tmpSub.close()
+
+ self.logger.debug("run tesseract")
+ self.run(tessparams)
+ self.logger.debug("read txt")
+
+ try:
+ with open(tmpTxt.name, 'r') as f:
+ self.result_captcha = f.read().replace("\n", "")
+ except:
+ self.result_captcha = ""
+
+ self.logger.debug(self.result_captcha)
+ try:
+ os.remove(tmpTif.name)
+ os.remove(tmpTxt.name)
+ if subset and (digits or lowercase or uppercase):
+ os.remove(tmpSub.name)
+ except:
+ pass
+
+
+ def get_captcha(self, name):
+ raise NotImplementedError
+
+
+ def to_greyscale(self):
+ if self.image.mode != 'L':
+ self.image = self.image.convert('L')
+
+ self.pixels = self.image.load()
+
+
+ def eval_black_white(self, limit):
+ self.pixels = self.image.load()
+ w, h = self.image.size
+ for x in xrange(w):
+ for y in xrange(h):
+ if self.pixels[x, y] > limit:
+ self.pixels[x, y] = 255
+ else:
+ self.pixels[x, y] = 0
+
+
+ def clean(self, allowed):
+ pixels = self.pixels
+
+ w, h = self.image.size
+
+ for x in xrange(w):
+ for y in xrange(h):
+ if pixels[x, y] == 255:
+ continue
+ # No point in processing white pixels since we only want to remove black pixel
+ count = 0
+
+ try:
+ if pixels[x - 1, y - 1] != 255:
+ count += 1
+ if pixels[x - 1, y] != 255:
+ count += 1
+ if pixels[x - 1, y + 1] != 255:
+ count += 1
+ if pixels[x, y + 1] != 255:
+ count += 1
+ if pixels[x + 1, y + 1] != 255:
+ count += 1
+ if pixels[x + 1, y] != 255:
+ count += 1
+ if pixels[x + 1, y - 1] != 255:
+ count += 1
+ if pixels[x, y - 1] != 255:
+ count += 1
+ except:
+ pass
+
+ # not enough neighbors are dark pixels so mark this pixel
+ # to be changed to white
+ if count < allowed:
+ pixels[x, y] = 1
+
+ # second pass: this time set all 1's to 255 (white)
+ for x in xrange(w):
+ for y in xrange(h):
+ if pixels[x, y] == 1:
+ pixels[x, y] = 255
+
+ self.pixels = pixels
+
+
+ def derotate_by_average(self):
+ """rotate by checking each angle and guess most suitable"""
+
+ w, h = self.image.size
+ pixels = self.pixels
+
+ for x in xrange(w):
+ for y in xrange(h):
+ if pixels[x, y] == 0:
+ pixels[x, y] = 155
+
+ highest = {}
+ counts = {}
+
+ for angle in xrange(-45, 45):
+
+ tmpimage = self.image.rotate(angle)
+
+ pixels = tmpimage.load()
+
+ w, h = self.image.size
+
+ for x in xrange(w):
+ for y in xrange(h):
+ if pixels[x, y] == 0:
+ pixels[x, y] = 255
+
+ count = {}
+
+ for x in xrange(w):
+ count[x] = 0
+ for y in xrange(h):
+ if pixels[x, y] == 155:
+ count[x] += 1
+
+ sum = 0
+ cnt = 0
+
+ for x in count.values():
+ if x != 0:
+ sum += x
+ cnt += 1
+
+ avg = sum / cnt
+ counts[angle] = cnt
+ highest[angle] = 0
+ for x in count.values():
+ if x > highest[angle]:
+ highest[angle] = x
+
+ highest[angle] = highest[angle] - avg
+
+ hkey = 0
+ hvalue = 0
+
+ for key, value in highest.iteritems():
+ if value > hvalue:
+ hkey = key
+ hvalue = value
+
+ self.image = self.image.rotate(hkey)
+ pixels = self.image.load()
+
+ for x in xrange(w):
+ for y in xrange(h):
+ if pixels[x, y] == 0:
+ pixels[x, y] = 255
+
+ if pixels[x, y] == 155:
+ pixels[x, y] = 0
+
+ self.pixels = pixels
+
+
+ def split_captcha_letters(self):
+ captcha = self.image
+ started = False
+ letters = []
+ width, height = captcha.size
+ bottomY, topY = 0, height
+ pixels = captcha.load()
+
+ for x in xrange(width):
+ black_pixel_in_col = False
+ for y in xrange(height):
+ if pixels[x, y] != 255:
+ if not started:
+ started = True
+ firstX = x
+ lastX = x
+
+ if y > bottomY:
+ bottomY = y
+ if y < topY:
+ topY = y
+ if x > lastX:
+ lastX = x
+
+ black_pixel_in_col = True
+
+ if black_pixel_in_col is False and started is True:
+ rect = (firstX, topY, lastX, bottomY)
+ new_captcha = captcha.crop(rect)
+
+ w, h = new_captcha.size
+ if w > 5 and h > 5:
+ letters.append(new_captcha)
+
+ started = False
+ bottomY, topY = 0, height
+
+ return letters
+
+
+ def correct(self, values, var=None):
+ if var:
+ result = var
+ else:
+ result = self.result_captcha
+
+ for key, item in values.iteritems():
+
+ if key.__class__ == str:
+ result = result.replace(key, item)
+ else:
+ for expr in key:
+ result = result.replace(expr, item)
+
+ if var:
+ return result
+ else:
+ self.result_captcha = result
diff --git a/pyload/plugins/internal/SimpleCrypter.py b/pyload/plugins/internal/SimpleCrypter.py
index 5f7f89bc2..251acd6b8 100644
--- a/pyload/plugins/internal/SimpleCrypter.py
+++ b/pyload/plugins/internal/SimpleCrypter.py
@@ -4,9 +4,9 @@ import re
from urlparse import urlparse
-from pyload.plugins.Crypter import Crypter
-from module.plugins.Plugin import Fail
-from module.plugins.internal.SimpleHoster import _error, _wait, parseFileInfo, replace_patterns, set_cookies
+from pyload.plugins.internal.Crypter import Crypter
+from pyload.plugins.Plugin import Fail
+from pyload.plugins.internal.SimpleHoster import _error, _wait, parseFileInfo, replace_patterns, set_cookies
from pyload.utils import fixup, html_unescape
diff --git a/pyload/plugins/internal/SimpleHoster.py b/pyload/plugins/internal/SimpleHoster.py
index dd3afdefd..ee8ef712b 100644
--- a/pyload/plugins/internal/SimpleHoster.py
+++ b/pyload/plugins/internal/SimpleHoster.py
@@ -9,8 +9,8 @@ from pycurl import FOLLOWLOCATION
from pyload.network.CookieJar import CookieJar
from pyload.network.RequestFactory import getURL
-from pyload.plugins.base.Hoster import Hoster
-from module.plugins.Plugin import Fail
+from pyload.plugins.internal.Hoster import Hoster
+from pyload.plugins.Plugin import Fail
from pyload.utils import fixup, html_unescape, parseFileSize
diff --git a/pyload/plugins/internal/UpdateManager.py b/pyload/plugins/internal/UpdateManager.py
index 3f5b34c45..f1312dda5 100644
--- a/pyload/plugins/internal/UpdateManager.py
+++ b/pyload/plugins/internal/UpdateManager.py
@@ -7,7 +7,7 @@ from operator import itemgetter
from os import path, remove, stat
from pyload.network.RequestFactory import getURL
-from pyload.plugins.base.Addon import Expose, Addon, threaded
+from pyload.plugins.internal.Addon import Expose, Addon, threaded
from pyload.utils import safe_join
diff --git a/pyload/plugins/internal/XFSAccount.py b/pyload/plugins/internal/XFSAccount.py
new file mode 100644
index 000000000..0992a8c31
--- /dev/null
+++ b/pyload/plugins/internal/XFSAccount.py
@@ -0,0 +1,122 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+from time import gmtime, mktime, strptime
+from urlparse import urljoin
+
+from pyload.plugins.internal.Account import Account
+from pyload.plugins.internal.SimpleHoster import parseHtmlForm, set_cookies
+
+
+class XFSAccount(Account):
+ __name__ = "XFSAccount"
+ __type__ = "account"
+ __version__ = "0.25"
+
+ __description__ = """XFileSharing account plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
+ ("Walter Purcaro", "vuolter@gmail.com")]
+
+
+ HOSTER_DOMAIN = None
+ HOSTER_URL = None
+
+ COOKIES = [(HOSTER_DOMAIN, "lang", "english")]
+
+ PREMIUM_PATTERN = r'\(Premium only\)'
+
+ VALID_UNTIL_PATTERN = r'>Premium.[Aa]ccount expire:.*?(\d{1,2} [\w^_]+ \d{4})'
+
+ TRAFFIC_LEFT_PATTERN = r'>Traffic available today:.*?<b>\s*(?P<S>[\d.,]+|[Uu]nlimited)\s*(?:(?P<U>[\w^_]+)\s*)?</b>'
+ TRAFFIC_LEFT_UNIT = "MB" #: used only if no group <U> was found
+
+ LOGIN_FAIL_PATTERN = r'>(Incorrect Login or Password|Error<)'
+
+
+ def __init__(self, manager, accounts): #@TODO: remove in 0.4.10
+ self.init()
+ return super(XFSAccount, self).__init__(manager, accounts)
+
+
+ def init(self):
+ # if not self.HOSTER_DOMAIN:
+ # self.fail(_("Missing HOSTER_DOMAIN"))
+
+ if not self.HOSTER_URL:
+ self.HOSTER_URL = "http://www.%s/" % self.HOSTER_DOMAIN
+
+
+ def loadAccountInfo(self, user, req):
+ validuntil = None
+ trafficleft = None
+ premium = None
+
+ html = req.load(self.HOSTER_URL, get={'op': "my_account"}, decode=True)
+
+ premium = True if re.search(self.PREMIUM_PATTERN, html) else False
+
+ m = re.search(self.VALID_UNTIL_PATTERN, html)
+ if m:
+ expiredate = m.group(1).strip()
+ self.logDebug("Expire date: " + expiredate)
+
+ try:
+ validuntil = mktime(strptime(expiredate, "%d %B %Y"))
+
+ except Exception, e:
+ self.logError(e)
+
+ else:
+ if validuntil > mktime(gmtime()):
+ premium = True
+ else:
+ premium = False
+ validuntil = None #: registered account type (not premium)
+
+ m = re.search(self.TRAFFIC_LEFT_PATTERN, html)
+ if m:
+ try:
+ traffic = m.groupdict()
+ size = traffic['S']
+
+ if "nlimited" in size:
+ trafficleft = -1
+ if validuntil is None:
+ validuntil = -1
+ else:
+ if 'U' in traffic:
+ unit = traffic['U']
+ elif isinstance(self.TRAFFIC_LEFT_UNIT, basestring):
+ unit = self.TRAFFIC_LEFT_UNIT
+ else:
+ unit = ""
+
+ trafficleft = self.parseTraffic(size + unit)
+
+ except Exception, e:
+ self.logError(e)
+
+ return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium}
+
+
+ def login(self, user, data, req):
+ if isinstance(self.COOKIES, list):
+ set_cookies(req.cj, self.COOKIES)
+
+ url = urljoin(self.HOSTER_URL, "login.html")
+ html = req.load(url, decode=True)
+
+ action, inputs = parseHtmlForm('name="FL"', html)
+ if not inputs:
+ inputs = {'op': "login",
+ 'redirect': self.HOSTER_URL}
+
+ inputs.update({'login': user,
+ 'password': data['password']})
+
+ html = req.load(self.HOSTER_URL, post=inputs, decode=True)
+
+ if re.search(self.LOGIN_FAIL_PATTERN, html):
+ self.wrongPassword()
diff --git a/pyload/plugins/internal/XFSCrypter.py b/pyload/plugins/internal/XFSCrypter.py
new file mode 100644
index 000000000..2de39f4bc
--- /dev/null
+++ b/pyload/plugins/internal/XFSCrypter.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+
+from pyload.plugins.internal.SimpleCrypter import SimpleCrypter
+
+
+class XFSCrypter(SimpleCrypter):
+ __name__ = "XFSCrypter"
+ __type__ = "crypter"
+ __version__ = "0.04"
+
+ __pattern__ = r'^unmatchable$'
+
+ __description__ = """XFileSharing decrypter plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
+
+
+ HOSTER_DOMAIN = None
+ HOSTER_NAME = None
+
+ URL_REPLACEMENTS = [(r'&?per_page=\d+', ""), (r'[?/&]+$', ""), (r'(.+/[^?]+)$', r'\1?'), (r'$', r'&per_page=10000')]
+
+ COOKIES = [(HOSTER_DOMAIN, "lang", "english")]
+
+ LINK_PATTERN = r'<(?:td|TD).*?>\s*<a href="(.+?)".*?>.+?(?:</a>)?\s*</(?:td|TD)>'
+ NAME_PATTERN = r'<[tT]itle>.*?\: (?P<N>.+) folder</[tT]itle>'
+
+ OFFLINE_PATTERN = r'>\s*\w+ (Not Found|file (was|has been) removed)'
+ TEMP_OFFLINE_PATTERN = r'>\s*\w+ server (is in )?(maintenance|maintainance)'
diff --git a/pyload/plugins/internal/XFSHoster.py b/pyload/plugins/internal/XFSHoster.py
new file mode 100644
index 000000000..80ddda002
--- /dev/null
+++ b/pyload/plugins/internal/XFSHoster.py
@@ -0,0 +1,345 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+from random import random
+from time import sleep
+
+from pycurl import FOLLOWLOCATION, LOW_SPEED_TIME
+
+from pyload.plugins.hoster.UnrestrictLi import secondsToMidnight
+from pyload.plugins.internal.CaptchaService import ReCaptcha, SolveMedia
+from pyload.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
+from pyload.utils import html_unescape
+
+
+class XFSHoster(SimpleHoster):
+ __name__ = "XFSHoster"
+ __type__ = "hoster"
+ __version__ = "0.15"
+
+ __pattern__ = r'^unmatchable$'
+
+ __description__ = """XFileSharing hoster plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
+ ("stickell", "l.stickell@yahoo.it"),
+ ("Walter Purcaro", "vuolter@gmail.com")]
+
+
+ HOSTER_DOMAIN = None
+ HOSTER_NAME = None
+
+ URL_REPLACEMENTS = [(r'/(?:embed-)?(\w{12}).*', r'/\1')] #: plus support embedded files
+
+ COOKIES = [(HOSTER_DOMAIN, "lang", "english")]
+
+ INFO_PATTERN = r'<tr><td align=right><b>Filename:</b></td><td nowrap>(?P<N>[^<]+)</td></tr>\s*.*?<small>\((?P<S>[^<]+)\)</small>'
+ NAME_PATTERN = r'<input type="hidden" name="fname" value="(?P<N>[^"]+)"'
+ SIZE_PATTERN = r'You have requested .*\((?P<S>[\d.,]+) ?(?P<U>[\w^_]+)?\)</font>'
+
+ OFFLINE_PATTERN = r'>\s*\w+ (Not Found|file (was|has been) removed)'
+ TEMP_OFFLINE_PATTERN = r'>\s*\w+ server (is in )?(maintenance|maintainance)'
+
+ WAIT_PATTERN = r'<span id="countdown_str">.*?>(\d+)</span>'
+
+ OVR_LINK_PATTERN = r'<h2>Download Link</h2>\s*<textarea[^>]*>([^<]+)'
+ LINK_PATTERN = None #: final download url pattern
+
+ CAPTCHA_PATTERN = r'(http://[^"\']+?/captchas?/[^"\']+)'
+ CAPTCHA_DIV_PATTERN = r'>Enter code.*?<div.*?>(.+?)</div>'
+ RECAPTCHA_PATTERN = None
+ SOLVEMEDIA_PATTERN = None
+
+ ERROR_PATTERN = r'(?:class=["\']err["\'][^>]*>|<[Cc]enter><b>)(.+?)(?:["\']|</)|>\(ERROR:(.+?)\)'
+
+
+ def setup(self):
+ self.chunkLimit = 1
+ self.resumeDownload = self.multiDL = self.premium
+
+
+ def prepare(self):
+ """ Initialize important variables """
+ if not self.HOSTER_DOMAIN:
+ self.fail(_("Missing HOSTER_DOMAIN"))
+
+ if not self.HOSTER_NAME:
+ self.HOSTER_NAME = "".join([str.capitalize() for str in self.HOSTER_DOMAIN.split('.')])
+
+ if not self.LINK_PATTERN:
+ pattern = r'(https?://(www\.)?([^/]*?%s|\d+\.\d+\.\d+\.\d+)(\:\d+)?(/d/|(/files)?/\d+/\w+/).+?)["\'<]'
+ self.LINK_PATTERN = pattern % self.HOSTER_DOMAIN.replace('.', '\.')
+
+ self.captcha = None
+ self.errmsg = None
+ self.passwords = self.getPassword().splitlines()
+
+ if (self.__pattern__ != self.core.pluginManager.hosterPlugins[self.__name__]['pattern']
+ and re.match(self.__pattern__, self.pyfile.url) is None):
+ self.logInfo(_("Multi hoster detected"))
+ if self.premium:
+ self.logDebug(_("Looking for download link..."))
+ self.handleOverriden()
+ else:
+ self.fail(_("Only premium users can use url leech feature"))
+
+ return super(XFSHoster, self).prepare()
+
+
+ def handleFree(self):
+ link = self.getDownloadLink()
+
+ if link:
+ if self.captcha:
+ self.correctCaptcha()
+
+ self.download(link, ref=True, cookies=True, disposition=True)
+
+ elif self.errmsg:
+ if 'captcha' in self.errmsg:
+ self.fail(_("No valid captcha code entered"))
+ else:
+ self.fail(self.errmsg)
+
+ else:
+ self.fail(_("Download link not found"))
+
+
+ def handlePremium(self):
+ return self.handleFree()
+
+
+ def getDownloadLink(self):
+ for i in xrange(1, 5):
+ self.logDebug("Getting download link: #%d" % i)
+
+ self.checkErrors()
+
+ m = re.search(self.LINK_PATTERN, self.html, re.S)
+ if m:
+ break
+
+ data = self.getPostParameters()
+
+ # sleep(10)
+
+ self.req.http.c.setopt(FOLLOWLOCATION, 0)
+
+ self.html = self.load(self.pyfile.url, post=data, ref=True, decode=True)
+ self.header = self.req.http.header
+
+ self.req.http.c.setopt(FOLLOWLOCATION, 1)
+
+ m = re.search(r'Location\s*:\s*(.+)', self.header, re.I)
+ if m:
+ break
+
+ m = re.search(self.LINK_PATTERN, self.html, re.S)
+ if m:
+ break
+ else:
+ return
+
+ self.errmsg = None
+
+ return m.group(1).strip() #@TODO: Remove .strip() in 0.4.10
+
+
+ def handleOverriden(self):
+ #only tested with easybytez.com
+ self.html = self.load("http://www.%s/" % self.HOSTER_DOMAIN)
+
+ action, inputs = self.parseHtmlForm('')
+
+ upload_id = "%012d" % int(random() * 10 ** 12)
+ action += upload_id + "&js_on=1&utype=prem&upload_type=url"
+
+ inputs['tos'] = '1'
+ inputs['url_mass'] = self.pyfile.url
+ inputs['up1oad_type'] = 'url'
+
+ self.logDebug(action, inputs)
+
+ self.req.setOption("timeout", 600) #: wait for file to upload to easybytez.com
+
+ self.html = self.load(action, post=inputs)
+
+ self.checkErrors()
+
+ action, inputs = self.parseHtmlForm('F1')
+ if not inputs:
+ if self.errmsg:
+ self.retry(reason=self.errmsg)
+ else:
+ self.error(_("TEXTAREA F1 not found"))
+
+ self.logDebug(inputs)
+
+ stmsg = inputs['st']
+
+ if stmsg == 'OK':
+ self.html = self.load(action, post=inputs)
+
+ elif 'Can not leech file' in stmsg:
+ self.retry(20, 3 * 60, _("Can not leech file"))
+
+ elif 'today' in stmsg:
+ self.retry(wait_time=secondsToMidnight(gmt=2), reason=_("You've used all Leech traffic today"))
+
+ else:
+ self.fail(stmsg)
+
+ #get easybytez.com link for uploaded file
+ m = re.search(self.OVR_LINK_PATTERN, self.html)
+ if m is None:
+ self.error(_("OVR_LINK_PATTERN not found"))
+
+ header = self.load(m.group(1).strip(), just_header=True, decode=True) #@TODO: Remove .strip() in 0.4.10
+ if 'location' in header: #: Direct download link
+ self.download(header['location'], ref=True, cookies=True, disposition=True)
+ else:
+ self.fail(_("Download link not found"))
+
+
+ def checkErrors(self):
+ m = re.search(self.ERROR_PATTERN, self.html)
+
+ if m is None:
+ self.errmsg = None
+ else:
+ self.errmsg = m.group(1)
+
+ self.logWarning(re.sub(r"<.*?>", " ", self.errmsg))
+
+ if 'wait' in self.errmsg:
+ wait_time = sum([int(v) * {"hour": 3600, "minute": 60, "second": 1}[u] for v, u in
+ re.findall(r'(\d+)\s*(hour|minute|second)', self.errmsg)])
+ self.wait(wait_time, True)
+
+ elif 'captcha' in self.errmsg:
+ self.invalidCaptcha()
+
+ elif 'premium' in self.errmsg and 'require' in self.errmsg:
+ self.fail(_("File can be downloaded by premium users only"))
+
+ elif 'limit' in self.errmsg:
+ if 'days' in self.errmsg:
+ delay = secondsToMidnight(gmt=2)
+ retries = 3
+ else:
+ delay = 1 * 60 * 60
+ retries = 25
+
+ self.wait(delay, True)
+ self.retry(retries, reason=_("Download limit exceeded"))
+
+ elif 'countdown' in self.errmsg or 'Expired' in self.errmsg:
+ self.retry(reason=_("Link expired"))
+
+ elif 'maintenance' in self.errmsg or 'maintainance' in self.errmsg:
+ self.tempOffline()
+
+ elif 'download files up to' in self.errmsg:
+ self.fail(_("File too large for free download"))
+
+ else:
+ self.retry(wait_time=60, reason=self.errmsg)
+
+ return self.errmsg
+
+
+ def getPostParameters(self):
+ for _i in xrange(3):
+ if hasattr(self, "FORM_PATTERN"):
+ action, inputs = self.parseHtmlForm(self.FORM_PATTERN)
+ else:
+ action, inputs = self.parseHtmlForm(input_names={"op": re.compile("^download")})
+
+ if not inputs:
+ action, inputs = self.parseHtmlForm('F1')
+ if not inputs:
+ if self.errmsg:
+ self.retry(reason=self.errmsg)
+ else:
+ self.error(_("TEXTAREA F1 not found"))
+
+ self.logDebug(inputs)
+
+ if 'op' in inputs and inputs['op'] in ("download2", "download3"):
+ if "password" in inputs:
+ if self.passwords:
+ inputs['password'] = self.passwords.pop(0)
+ else:
+ self.fail(_("No or invalid passport"))
+
+ if not self.premium:
+ m = re.search(self.WAIT_PATTERN, self.html)
+ if m:
+ wait_time = int(m.group(1))
+ self.setWait(wait_time, False)
+ else:
+ wait_time = 0
+
+ self.captcha = self.handleCaptcha(inputs)
+
+ if wait_time:
+ self.wait()
+
+ return inputs
+ else:
+ inputs['referer'] = self.pyfile.url
+
+ if self.premium:
+ inputs['method_premium'] = "Premium Download"
+ if 'method_free' in inputs:
+ del inputs['method_free']
+ else:
+ inputs['method_free'] = "Free Download"
+ if 'method_premium' in inputs:
+ del inputs['method_premium']
+
+ self.html = self.load(self.pyfile.url, post=inputs, ref=True)
+ else:
+ self.error(_("FORM: %s") % (inputs['op'] if 'op' in inputs else _("UNKNOWN")))
+
+
+ def handleCaptcha(self, inputs):
+ m = re.search(self.CAPTCHA_PATTERN, self.html)
+ if m:
+ captcha_url = m.group(1)
+ inputs['code'] = self.decryptCaptcha(captcha_url)
+ return 1
+
+ m = re.search(self.CAPTCHA_DIV_PATTERN, self.html, re.S)
+ if m:
+ captcha_div = m.group(1)
+ self.logDebug(captcha_div)
+ numerals = re.findall(r'<span.*?padding-left\s*:\s*(\d+).*?>(\d)</span>', html_unescape(captcha_div))
+ inputs['code'] = "".join([a[1] for a in sorted(numerals, key=lambda num: int(num[0]))])
+ self.logDebug("Captcha code: %s" % inputs['code'], numerals)
+ return 2
+
+ recaptcha = ReCaptcha(self)
+ try:
+ captcha_key = re.search(self.RECAPTCHA_PATTERN, self.html).group(1)
+ except:
+ captcha_key = recaptcha.detect_key()
+
+ if captcha_key:
+ self.logDebug("ReCaptcha key: %s" % captcha_key)
+ inputs['recaptcha_challenge_field'], inputs['recaptcha_response_field'] = recaptcha.challenge(captcha_key)
+ return 3
+
+ solvemedia = SolveMedia(self)
+ try:
+ captcha_key = re.search(self.SOLVEMEDIA_PATTERN, self.html).group(1)
+ except:
+ captcha_key = solvemedia.detect_key()
+
+ if captcha_key:
+ self.logDebug("SolveMedia key: %s" % captcha_key)
+ inputs['adcopy_challenge'], inputs['adcopy_response'] = solvemedia.challenge(captcha_key)
+ return 4
+
+ return 0
diff --git a/pyload/plugins/internal/XFSPAccount.py b/pyload/plugins/internal/XFSPAccount.py
deleted file mode 100644
index edf6ad3cc..000000000
--- a/pyload/plugins/internal/XFSPAccount.py
+++ /dev/null
@@ -1,96 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urlparse import urljoin
-from time import mktime, strptime
-
-from pyload.plugins.base.Account import Account
-from pyload.plugins.internal.SimpleHoster import parseHtmlForm, set_cookies
-from pyload.utils import parseFileSize
-
-
-class XFSPAccount(Account):
- __name__ = "XFSPAccount"
- __type__ = "account"
- __version__ = "0.09"
-
- __description__ = """XFileSharingPro base account plugin"""
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_URL = None
-
- COOKIES = None #: or list of tuples [(domain, name, value)]
-
- VALID_UNTIL_PATTERN = r'>Premium.[Aa]ccount expire:.*?<b>(.+?)</b>'
- TRAFFIC_LEFT_PATTERN = r'>Traffic available today:.*?<b>(?P<S>.+?)</b>'
- LOGIN_FAIL_PATTERN = r'>(Incorrect Login or Password|Error<)'
- # PREMIUM_PATTERN = r'>Renew premium<'
-
-
- def loadAccountInfo(self, user, req):
- html = req.load(self.HOSTER_URL, get={'op': "my_account"}, decode=True)
-
- validuntil = None
- trafficleft = None
- premium = None
-
- if hasattr(self, "PREMIUM_PATTERN"):
- premium = True if re.search(self.PREMIUM_PATTERN, html) else False
-
- m = re.search(self.VALID_UNTIL_PATTERN, html)
- if m:
- expiredate = m.group(1)
- self.logDebug("Expire date: " + expiredate)
-
- try:
- validuntil = mktime(strptime(expiredate, "%d %B %Y"))
- except Exception, e:
- self.logError(e)
- else:
- if validuntil > mktime(gmtime()):
- premium = True
- trafficleft = -1
- else:
- if premium is False: #: registered account type (not premium)
- validuntil = -1
- premium = False
-
- try:
- traffic = re.search(self.TRAFFIC_LEFT_PATTERN, html).groupdict()
- trafficsize = traffic['S'] + traffic['U'] if 'U' in traffic else traffic['S']
- if "Unlimited" in trafficsize:
- trafficleft = -1
- if premium is None:
- premium = True
- else:
- trafficleft = parseFileSize(trafficsize)
- except:
- pass
-
- if premium is None:
- premium = False
-
- return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium}
-
-
- def login(self, user, data, req):
- set_cookies(req.cj, self.COOKIES)
-
- url = urljoin(self.HOSTER_URL, "login.html")
- html = req.load(url, decode=True)
-
- action, inputs = parseHtmlForm('name="FL"', html)
- if not inputs:
- inputs = {'op': "login",
- 'redirect': self.HOSTER_URL}
-
- inputs.update({'login': user,
- 'password': data['password']})
-
- html = req.load(self.HOSTER_URL, post=inputs, decode=True)
-
- if re.search(self.LOGIN_FAIL_PATTERN, html):
- self.wrongPassword()
diff --git a/pyload/plugins/internal/XFSPHoster.py b/pyload/plugins/internal/XFSPHoster.py
deleted file mode 100644
index c3f8b6f41..000000000
--- a/pyload/plugins/internal/XFSPHoster.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class XFileSharingPro(XFSHoster):
- __name__ = "XFileSharingPro"
- __type__ = "hoster"
- __version__ = "0.42"
-
- __pattern__ = r'^unmatchable$'
-
- __description__ = """XFileSharingPro dummy hoster plugin for hook"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- def _log(self, type, args):
- msg = " | ".join([str(a).strip() for a in args if a])
- logger = getattr(self.log, type)
- logger("%s: %s: %s" % (self.__name__, self.HOSTER_NAME, msg or _("%s MARK" % type.upper())))
-
-
- def init(self):
- super(XFileSharingPro, self).init()
-
- self.__pattern__ = self.core.pluginManager.hosterPlugins[self.__name__]['pattern']
-
- self.HOSTER_DOMAIN = re.match(self.__pattern__, self.pyfile.url).group(1).lower()
- self.HOSTER_NAME = "".join([str.capitalize() for str in self.HOSTER_DOMAIN.split('.')])
-
- account = self.core.accountManager.getAccountPlugin(self.HOSTER_NAME)
-
- if account and account.canUse():
- self.account = account
- elif self.account:
- self.account.HOSTER_DOMAIN = self.HOSTER_DOMAIN
- else:
- return
-
- self.user, data = self.account.selectAccount()
- self.req = self.account.getAccountRequest(self.user)
- self.premium = self.account.isPremium(self.user)
-
-
- def setup(self):
- self.chunkLimit = 1
- self.resumeDownload = self.premium
- self.multiDL = True
-
-
-getInfo = create_getInfo(XFileSharingPro)