diff options
Diffstat (limited to 'pyload/plugin')
449 files changed, 28767 insertions, 0 deletions
diff --git a/pyload/plugin/Account.py b/pyload/plugin/Account.py new file mode 100644 index 000000000..6a3eddc5b --- /dev/null +++ b/pyload/plugin/Account.py @@ -0,0 +1,307 @@ +# -*- coding: utf-8 -*- + +from random import choice +from time import time +from traceback import print_exc +from threading import RLock + +from pyload.plugin.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 bytes, -1 for unlimited +                "maxtraffic" : None, +                "premium"    : None, +                "timestamp"  : 0,  #: time this info was retrieved +                "type"       : self.__class__.__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.__class__.__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.__class__.__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 Exception: +                    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, value, unit=None):  #: return bytes +        if not unit and not isinstance(value, basestring): +            unit = "KB" +        return parseFileSize(value, unit) + + +    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/plugin/Addon.py b/pyload/plugin/Addon.py new file mode 100644 index 000000000..1f4730851 --- /dev/null +++ b/pyload/plugin/Addon.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- + +from traceback import print_exc + +from pyload.plugin.Plugin import Base +from pyload.utils import has_method + + +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(fn): + +    def run(*args,**kwargs): +        addonManager.startThread(fn, *args, **kwargs) + +    return run + + +class Addon(Base): +    __name    = "Addon" +    __type    = "addon" +    __version = "0.01" + +    __config = []  #: [("name", "type", "desc", "default")] + +    __description = """Base addon plugin""" +    __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 = {} + +    # Deprecated alternative to event_map +    #: List of events the plugin can handle, name the functions exactly like eventname. +    event_list = []  #@NOTE: dont make duplicate entries in event_map + + +    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 +        self.interval = 60 + +        #: `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() + + +    def initPeriodical(self, delay=0, threaded=False): +        self.cb = self.core.scheduler.addJob(max(0, delay), self._periodical, [threaded], threaded=threaded) + + +    def _periodical(self, threaded): +        if self.interval < 0: +            self.cb = None +            return + +        try: +            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], threaded=threaded) + + +    def __repr__(self): +        return "<Addon %s>" % self.__class__.__name__ + + +    def setup(self): +        """ more init stuff if needed """ +        pass + + +    def deactivate(self): +        """ called when addon was deactivated """ +        if has_method(self.__class__, "unload"): +            self.unload() + +    def unload(self):  # Deprecated, use method deactivate() instead +        pass + + +    def isActivated(self): +        """ checks if addon is activated""" +        return self.getConfig("activated") + + +    # Event methods - overwrite these if needed +    def activate(self): +        """ called when addon was activated """ +        if has_method(self.__class__, "coreReady"): +            self.coreReady() + +    def coreReady(self):  # Deprecated, use method activate() instead +        pass + + +    def exit(self): +        """ called by core.shutdown just before pyLoad exit """ +        if has_method(self.__class__, "coreExiting"): +            self.coreExiting() + +    def coreExiting(self):  # Deprecated, use method exit() instead +        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 captchaTask(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/plugin/Captcha.py b/pyload/plugin/Captcha.py new file mode 100644 index 000000000..d7a506979 --- /dev/null +++ b/pyload/plugin/Captcha.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Plugin import Base + + +#@TODO: Extend Plugin class; remove all `html` args +class Captcha(Base): +    __name    = "Captcha" +    __type    = "captcha" +    __version = "0.25" + +    __description = """Base captcha service plugin""" +    __license     = "GPLv3" +    __authors     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    def __init__(self, plugin): +        self.plugin = plugin +        self.key = None  #: last key detected +        super(Captcha, self).__init__(plugin.core) + + +    def detect_key(self, html=None): +        raise NotImplementedError + + +    def challenge(self, key=None, html=None): +        raise NotImplementedError + + +    def result(self, server, challenge): +        raise NotImplementedError diff --git a/pyload/plugin/Container.py b/pyload/plugin/Container.py new file mode 100644 index 000000000..87d75976f --- /dev/null +++ b/pyload/plugin/Container.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import re + +from os import remove +from os.path import basename, exists + +from pyload.plugin.internal.Crypter import Crypter +from pyload.utils import fs_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 = fs_join(self.core.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(fs_join(pypath, self.pyfile.url)): +                    self.pyfile.url = fs_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/plugin/Crypter.py b/pyload/plugin/Crypter.py new file mode 100644 index 000000000..aa9966ab4 --- /dev/null +++ b/pyload/plugin/Crypter.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- + +from urlparse import urlparse + +from pyload.plugin.Plugin import Plugin +from pyload.utils import decode, safe_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.core.config['general']['folder_per_package'] +        try: +            use_subfolder = self.getConfig('use_subfolder') +        except Exception: +            use_subfolder = folder_per_package +        try: +            subfolder_per_package = self.getConfig('subfolder_per_package') +        except Exception: +            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(decode, 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 = urlparse(name).path.split("/")[-1] + +                    setFolder(safe_filename(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/plugin/Extractor.py b/pyload/plugin/Extractor.py new file mode 100644 index 000000000..80f123a52 --- /dev/null +++ b/pyload/plugin/Extractor.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- + +import os +import re + +from pyload.datatype.File import PyFile +from pyload.plugin.Plugin import Base + + +class ArchiveError(Exception): +    pass + + +class CRCError(Exception): +    pass + + +class PasswordError(Exception): +    pass + + +class Extractor: +    __name    = "Extractor" +    __type    = "extractor" +    __version = "0.24" + +    __description = """Base extractor plugin""" +    __license     = "GPLv3" +    __authors     = [("Walter Purcaro", "vuolter@gmail.com"), +                     ("Immenz"        , "immenz@gmx.net"   )] + + +    EXTENSIONS = [] +    VERSION    = "" +    REPAIR     = False + + +    @classmethod +    def isArchive(cls, filename): +        name = os.path.basename(filename).lower() +        return any(name.endswith(ext) for ext in cls.EXTENSIONS) + + +    @classmethod +    def isMultipart(cls, filename): +        return False + + +    @classmethod +    def isUsable(cls): +        """ Check if system statisfy dependencies +        :return: boolean +        """ +        return None + + +    @classmethod +    def getTargets(cls, files_ids): +        """ Filter suited targets from list of filename id tuple list +        :param files_ids: List of filepathes +        :return: List of targets, id tuple list +        """ +        targets = [] +        processed = [] + +        for fname, id, fout in files_ids: +            if cls.isArchive(fname): +                pname = re.sub(cls.re_multipart, '', fname) if cls.isMultipart(fname) else os.path.splitext(fname)[0] +                if pname not in processed: +                    processed.append(pname) +                    targets.append((fname, id, fout)) +        return targets + + +    def __init__(self, manager, filename, out, +                 fullpath=True, +                 overwrite=False, +                 excludefiles=[], +                 renice=0, +                 delete='No', +                 keepbroken=False, +                 fid=None): +        """ Initialize extractor for specific file """ +        self.manager      = manager +        self.filename     = filename +        self.out          = out +        self.fullpath     = fullpath +        self.overwrite    = overwrite +        self.excludefiles = excludefiles +        self.renice       = renice +        self.delete       = delete +        self.keepbroken   = keepbroken +        self.files        = []  #: Store extracted files here + +        pyfile = self.manager.core.files.getFile(fid) if fid else None +        self.notifyProgress = lambda x: pyfile.setProgress(x) if pyfile else lambda x: None + + +    def init(self): +        """ Initialize additional data structures """ +        pass + + +    def check(self): +        """Quick Check by listing content of archive. +        Raises error if password is needed, integrity is questionable or else. + +        :raises PasswordError +        :raises CRCError +        :raises ArchiveError +        """ +        raise NotImplementedError + +    def verify(self): +        """Testing with Extractors buildt-in method +        Raises error if password is needed, integrity is questionable or else. + +        :raises PasswordError +        :raises CRCError +        :raises ArchiveError +        """ +        raise NotImplementedError + + +    def repair(self): +        return None + + +    def extract(self, password=None): +        """Extract the archive. Raise specific errors in case of failure. + +        :param progress: Progress function, call this to update status +        :param password password to use +        :raises PasswordError +        :raises CRCError +        :raises ArchiveError +        :return: +        """ +        raise NotImplementedError + + +    def getDeleteFiles(self): +        """Return list of files to delete, do *not* delete them here. + +        :return: List with paths of files to delete +        """ +        return [self.filename] + + +    def list(self, password=None): +        """Populate self.files at some point while extracting""" +        return self.files diff --git a/pyload/plugin/Hook.py b/pyload/plugin/Hook.py new file mode 100644 index 000000000..ccd4d635b --- /dev/null +++ b/pyload/plugin/Hook.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Addon import Addon, threaded + + +class Hook(Addon): +    __name    = "Hook" +    __type    = "hook" +    __version = "0.03" + +    __config = []  #: [("name", "type", "desc", "default")] + +    __description = """Base hook plugin""" +    __license     = "GPLv3" +    __authors     = [("Walter Purcaro", "vuolter@gmail.com")] diff --git a/pyload/plugin/Hoster.py b/pyload/plugin/Hoster.py new file mode 100644 index 000000000..df778c72f --- /dev/null +++ b/pyload/plugin/Hoster.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.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/plugin/OCR.py b/pyload/plugin/OCR.py new file mode 100644 index 000000000..df32b9f23 --- /dev/null +++ b/pyload/plugin/OCR.py @@ -0,0 +1,309 @@ +# -*- 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 os +import subprocess +# import tempfile + +from pyload.plugin.Plugin import Base +from pyload.utils import fs_join + + +class OCR(Base): +    __name    = "OCR" +    __type    = "ocr" +    __version = "0.12" + +    __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 deactivate(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, pagesegmode=None): +        # tmpTif = tempfile.NamedTemporaryFile(suffix=".tif") +        try: +            tmpTif = open(fs_join("tmp", "tmpTif_%s.tif" % self.__class__.__name__), "wb") +            tmpTif.close() + +            # tmpTxt = tempfile.NamedTemporaryFile(suffix=".txt") +            tmpTxt = open(fs_join("tmp", "tmpTxt_%s.txt" % self.__class__.__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 = [os.path.join(pypath, "tesseract", "tesseract.exe")] +        else: +            tessparams = ["tesseract"] + +        tessparams.extend([os.path.abspath(tmpTif.name), os.path.abspath(tmpTxt.name).replace(".txt", "")]) + +        if pagesegmode: +            tessparams.extend(["-psm", str(pagesegmode)]) + +        if subset and (digits or lowercase or uppercase): +            # tmpSub = tempfile.NamedTemporaryFile(suffix=".subset") +            with open(fs_join("tmp", "tmpSub_%s.subset" % self.__class__.__name__), "wb") as tmpSub: +                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(os.path.abspath(tmpSub.name)) + +        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 Exception: +            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 Exception: +            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 Exception: +                    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/plugin/Plugin.py b/pyload/plugin/Plugin.py new file mode 100644 index 000000000..af70232e0 --- /dev/null +++ b/pyload/plugin/Plugin.py @@ -0,0 +1,716 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +from time import time, sleep +from random import randint + +import os +import re +from os import remove, makedirs, chmod, stat +from os.path import exists, join + +if os.name != "nt": +    from os import chown +    from pwd import getpwnam +    from grp import getgrnam + +from itertools import islice +from traceback import print_exc +from urlparse import urlparse + +from pyload.utils import fs_decode, fs_encode, safe_filename, fs_join, encode + + +def chunks(iterable, size): +    it = iter(iterable) +    item = list(islice(it, size)) +    while item: +        yield item +        item = list(islice(it, size)) + + +class Abort(Exception): +    """ raised when aborted """ + + +class Fail(Exception): +    """ raised when failed """ + + +class Reconnect(Exception): +    """ raised when reconnected """ + + +class Retry(Exception): +    """ raised when start again from beginning """ + + +class SkipDownload(Exception): +    """ raised when download should be skipped """ + + +class Base(object): +    """ +    A Base class with log/config/db methods *all* plugin types can use +    """ + +    def __init__(self, core): +        #: Core instance +        self.core = core + +    def _log(self, type, args): +        msg = " | ".join([encode(str(a)).strip() for a in args if a]) +        logger = getattr(self.core.log, type) +        logger("%s: %s" % (self.__class__.__name__, msg or _("%s MARK" % type.upper()))) + +    def logDebug(self, *args): +        if self.core.debug: +            return self._log("debug", args) + +    def logInfo(self, *args): +        return self._log("info", args) + +    def logWarning(self, *args): +        return self._log("warning", args) + +    def logError(self, *args): +        return self._log("error", args) + +    def logCritical(self, *args): +        return self._log("critical", args) + +    def grtPluginType(self): +        return getattr(self, "_%s__type" % self.__class__.__name__) + +    def getPluginConfSection(self): +        return "%s_%s" % (self.__class__.__name__, getattr(self, "_%s__type" % self.__class__.__name__)) + +    #: Deprecated method +    def setConf(self, option, value): +        """ see `setConfig` """ +        self.setConfig(option, value) + +    def setConfig(self, option, value): +        """ Set config value for current plugin + +        :param option: +        :param value: +        :return: +        """ +        self.core.config.setPlugin(self.getPluginConfSection(), option, value) + +    #: Deprecated method +    def getConf(self, option): +        """ see `getConfig` """ +        return self.core.config.getPlugin(self.getPluginConfSection(), option) + +    def getConfig(self, option): +        """ Returns config value for current plugin + +        :param option: +        :return: +        """ +        return self.core.config.getPlugin(self.getPluginConfSection(), option) + +    def setStorage(self, key, value): +        """ Saves a value persistently to the database """ +        self.core.db.setStorage(self.getPluginConfSection(), key, value) + +    def store(self, key, value): +        """ same as `setStorage` """ +        self.core.db.setStorage(self.getPluginConfSection(), key, value) + +    def getStorage(self, key=None, default=None): +        """ Retrieves saved value or dict of all saved entries if key is None """ +        if key: +            return self.core.db.getStorage(self.getPluginConfSection(), key) or default +        return self.core.db.getStorage(self.getPluginConfSection(), key) + +    def retrieve(self, *args, **kwargs): +        """ same as `getStorage` """ +        return self.getStorage(*args, **kwargs) + +    def delStorage(self, key): +        """ Delete entry in db """ +        self.core.db.delStorage(self.__class__.__name__, key) + + +class Plugin(Base): +    """ +    Base plugin for hoster/crypter. +    Overwrite `process` / `decrypt` in your subclassed plugin. +    """ +    __name    = "Plugin" +    __type    = "hoster" +    __version = "0.07" + +    __pattern = r'^unmatchable$' +    __config  = []  #: [("name", "type", "desc", "default")] + +    __description = """Base plugin""" +    __license     = "GPLv3" +    __authors     = [("RaNaN", "RaNaN@pyload.org"), +                     ("spoob", "spoob@pyload.org"), +                     ("mkaay", "mkaay@mkaay.de")] + +    info = {}  #: file info dict + +    def __init__(self, pyfile): +        Base.__init__(self, pyfile.m.core) + +        #: engage wan reconnection +        self.wantReconnect = False + +        #: enable simultaneous processing of multiple downloads +        self.multiDL = True +        self.limitDL = 0 + +        #: chunk limit +        self.chunkLimit = 1 +        self.resumeDownload = False + +        #: time() + wait in seconds +        self.waitUntil = 0 +        self.waiting = False + +        #: captcha reader instance +        self.ocr = None + +        #: account handler instance, see :py:class:`Account` +        self.account = pyfile.m.core.accountManager.getAccountPlugin(self.__class__.__name__) + +        #: premium status +        self.premium = False +        #: username/login +        self.user = None + +        if self.account and not self.account.canUse(): +            self.account = None + +        if self.account: +            self.user, data = self.account.selectAccount() +            #: Browser instance, see `network.Browser` +            self.req = self.account.getAccountRequest(self.user) +            self.chunkLimit = -1  # chunk limit, -1 for unlimited +            #: enables resume (will be ignored if server dont accept chunks) +            self.resumeDownload = True +            self.multiDL = True  # every hoster with account should provide multiple downloads +            #: premium status +            self.premium = self.account.isPremium(self.user) +        else: +            self.req = pyfile.m.core.requestFactory.getRequest(self.__class__.__name__) + +        #: associated pyfile instance, see `PyFile` +        self.pyfile = pyfile + +        self.thread = None  # holds thread in future + +        #: location where the last call to download was saved +        self.lastDownload = "" +        #: re match of the last call to `checkDownload` +        self.lastCheck = None + +        #: js engine, see `JsEngine` +        self.js = self.core.js + +        #: captcha task +        self.cTask = None + +        self.html = None  # @TODO: Move to hoster class in 0.4.10 +        self.retries = 0 + +        self.init() + +    def getChunkCount(self): +        if self.chunkLimit <= 0: +            return self.core.config['download']['chunks'] +        return min(self.core.config['download']['chunks'], self.chunkLimit) + +    def __call__(self): +        return self.__class__.__name__ + +    def init(self): +        """initialize the plugin (in addition to `__init__`)""" +        pass + +    def setup(self): +        """ setup for enviroment and other things, called before downloading (possibly more than one time)""" +        pass + +    def preprocessing(self, thread): +        """ handles important things to do before starting """ +        self.thread = thread + +        if self.account: +            self.account.checkLogin(self.user) +        else: +            self.req.clearCookies() + +        self.setup() + +        self.pyfile.setStatus("starting") + +        return self.process(self.pyfile) + +    def process(self, pyfile): +        """the 'main' method of every plugin, you **have to** overwrite it""" +        raise NotImplementedError + +    def resetAccount(self): +        """ dont use account and retry download """ +        self.account = None +        self.req = self.core.requestFactory.getRequest(self.__class__.__name__) +        self.retry() + +    def checksum(self, local_file=None): +        """ +        return codes: +        0  - checksum ok +        1  - checksum wrong +        5  - can't get checksum +        10 - not implemented +        20 - unknown error +        """ +        #@TODO checksum check addon + +        return True, 10 + +    def setReconnect(self, reconnect): +        reconnect = bool(reconnect) +        self.logDebug("Set wantReconnect to: %s (previous: %s)" % (reconnect, self.wantReconnect)) +        self.wantReconnect = reconnect + +    def setWait(self, seconds, reconnect=None): +        """Set a specific wait time later used with `wait` + +        :param seconds: wait time in seconds +        :param reconnect: True if a reconnect would avoid wait time +        """ +        wait_time  = int(seconds) + 1 +        wait_until = time() + wait_time + +        self.logDebug("Set waitUntil to: %f (previous: %f)" % (wait_until, self.pyfile.waitUntil), +                      "Wait: %d seconds" % wait_time) + +        self.pyfile.waitUntil = wait_until + +        if reconnect is not None: +            self.setReconnect(reconnect) + +    def wait(self, seconds=None, reconnect=None): +        """ waits the time previously set """ + +        pyfile = self.pyfile + +        if seconds is not None: +            self.setWait(seconds) + +        if reconnect is not None: +            self.setReconnect(reconnect) + +        self.waiting = True + +        status = pyfile.status +        pyfile.setStatus("waiting") + +        self.logInfo(_("Wait: %d seconds") % (pyfile.waitUntil - time()), +                     _("Reconnect: %s")    % self.wantReconnect) + +        if self.account: +            self.logDebug("Ignore reconnection due account logged") + +            while pyfile.waitUntil > time(): +                if pyfile.abort: +                    self.abort() + +                sleep(1) +        else: +            while pyfile.waitUntil > time(): +                self.thread.m.reconnecting.wait(2) + +                if pyfile.abort: +                    self.abort() + +                if self.thread.m.reconnecting.isSet(): +                    self.waiting = False +                    self.wantReconnect = False +                    raise Reconnect + +                sleep(1) + +        self.waiting = False + +        pyfile.status = status + +    def fail(self, reason): +        """ fail and give reason """ +        raise Fail(reason) + +    def abort(self, reason=""): +        """ abort and give reason """ +        if reason: +            self.pyfile.error = str(reason) +        raise Abort + +    def error(self, reason="", type=""): +        if not reason and not type: +            type = "unknown" + +        msg  = _("%s error") % _(type.strip().capitalize()) if type else _("Error") +        msg += ": " + reason.strip() if reason else "" +        msg += _(" | Plugin may be out of date") + +        raise Fail(msg) + +    def offline(self, reason=""): +        """ fail and indicate file is offline """ +        if reason: +            self.pyfile.error = str(reason) +        raise Fail("offline") + +    def tempOffline(self, reason=""): +        """ fail and indicates file ist temporary offline, the core may take consequences """ +        if reason: +            self.pyfile.error = str(reason) +        raise Fail("temp. offline") + +    def retry(self, max_tries=5, wait_time=1, reason=""): +        """Retries and begin again from the beginning + +        :param max_tries: number of maximum retries +        :param wait_time: time to wait in seconds +        :param reason: reason for retrying, will be passed to fail if max_tries reached +        """ +        if 0 < max_tries <= self.retries: +            self.error(reason or _("Max retries reached"), "retry") + +        self.wait(wait_time, False) + +        self.retries += 1 +        raise Retry(reason) + +    def invalidCaptcha(self): +        self.logError(_("Invalid captcha")) +        if self.cTask: +            self.cTask.invalid() + +    def correctCaptcha(self): +        self.logInfo(_("Correct captcha")) +        if self.cTask: +            self.cTask.correct() + +    def decryptCaptcha(self, url, get={}, post={}, cookies=False, forceUser=False, imgtype='jpg', +                       result_type='textual', timeout=290): +        """ Loads a captcha and decrypts it with ocr, plugin, user input + +        :param url: url of captcha image +        :param get: get part for request +        :param post: post part for request +        :param cookies: True if cookies should be enabled +        :param forceUser: if True, ocr is not used +        :param imgtype: Type of the Image +        :param result_type: 'textual' if text is written on the captcha\ +        or 'positional' for captcha where the user have to click\ +        on a specific region on the captcha + +        :return: result of decrypting +        """ + +        img = self.load(url, get=get, post=post, cookies=cookies) + +        id = ("%.2f" % time())[-6:].replace(".", "") + +        with open(join("tmp", "tmpCaptcha_%s_%s.%s" % (self.__class__.__name__, id, imgtype)), "wb") as tmpCaptcha: +            tmpCaptcha.write(img) + +        has_plugin = self.__class__.__name__ in self.core.pluginManager.ocrPlugins + +        if self.core.captcha: +            Ocr = self.core.pluginManager.loadClass("ocr", self.__class__.__name__) +        else: +            Ocr = None + +        if Ocr and not forceUser: +            sleep(randint(3000, 5000) / 1000.0) +            if self.pyfile.abort: +                self.abort() + +            ocr = Ocr() +            result = ocr.get_captcha(tmpCaptcha.name) +        else: +            captchaManager = self.core.captchaManager +            task = captchaManager.newTask(img, imgtype, tmpCaptcha.name, result_type) +            self.cTask = task +            captchaManager.handleCaptcha(task, timeout) + +            while task.isWaiting(): +                if self.pyfile.abort: +                    captchaManager.removeTask(task) +                    self.abort() +                sleep(1) + +            captchaManager.removeTask(task) + +            if task.error and has_plugin:  # ignore default error message since the user could use OCR +                self.fail(_("Pil and tesseract not installed and no Client connected for captcha decrypting")) +            elif task.error: +                self.fail(task.error) +            elif not task.result: +                self.fail(_("No captcha result obtained in appropiate time by any of the plugins")) + +            result = task.result +            self.logDebug("Received captcha result: %s" % result) + +        if not self.core.debug: +            try: +                remove(tmpCaptcha.name) +            except Exception: +                pass + +        return result + +    def load(self, url, get={}, post={}, ref=True, cookies=True, just_header=False, decode=False, follow_location=True, save_cookies=True): +        """Load content at url and returns it + +        :param url: +        :param get: +        :param post: +        :param ref: +        :param cookies: +        :param just_header: If True only the header will be retrieved and returned as dict +        :param decode: Wether to decode the output according to http header, should be True in most cases +        :param follow_location: If True follow location else not +        :param save_cookies: If True saves received cookies else discard them +        :return: Loaded content +        """ +        if self.pyfile.abort: +            self.abort() + +        if not url: +            self.fail(_("No url given")) + +        url = encode(url).strip()  # @NOTE: utf8 vs decode -> please use decode attribute in all future plugins + +        if self.core.debug: +            self.logDebug("Load url: " + url, *["%s=%s" % (key, val) for key, val in locals().iteritems() if key not in ("self", "url")]) + +        res = self.req.load(url, get, post, ref, cookies, just_header, decode=decode, follow_location=follow_location, save_cookies=save_cookies) + +        if decode: +            res = encode(res) + +        if self.core.debug: +            from inspect import currentframe + +            frame = currentframe() +            framefile = fs_join("tmp", self.__class__.__name__, "%s_line%s.dump.html" % (frame.f_back.f_code.co_name, frame.f_back.f_lineno)) +            try: +                if not exists(join("tmp", self.__class__.__name__)): +                    makedirs(join("tmp", self.__class__.__name__)) + +                with open(framefile, "wb") as f: +                    del frame  #: delete the frame or it wont be cleaned +                    f.write(res) +            except IOError, e: +                self.logError(e) + +        if just_header: +            # parse header +            header = {"code": self.req.code} +            for line in res.splitlines(): +                line = line.strip() +                if not line or ":" not in line: continue + +                key, none, value = line.partition(":") +                key = key.strip().lower() +                value = value.strip() + +                if key in header: +                    if type(header[key]) == list: +                        header[key].append(value) +                    else: +                        header[key] = [header[key], value] +                else: +                    header[key] = value +            res = header + +        return res + +    def download(self, url, get={}, post={}, ref=True, cookies=True, disposition=False): +        """Downloads the content at url to download folder + +        :param url: +        :param get: +        :param post: +        :param ref: +        :param cookies: +        :param disposition: if True and server provides content-disposition header\ +        the filename will be changed if needed +        :return: The location where the file was saved +        """ +        if self.pyfile.abort: +            self.abort() + +        if not url: +            self.fail(_("No url given")) + +        url = encode(url).strip() + +        if self.core.debug: +            self.logDebug("Download url: " + url, *["%s=%s" % (key, val) for key, val in locals().iteritems() if key not in ("self", "url")]) + +        self.checkForSameFiles() + +        self.pyfile.setStatus("downloading") + +        download_folder = self.core.config['general']['download_folder'] + +        location = fs_join(download_folder, self.pyfile.package().folder) + +        if not exists(location): +            try: +                makedirs(location, int(self.core.config['permission']['folder'], 8)) + +                if self.core.config['permission']['change_dl'] and os.name != "nt": +                    uid = getpwnam(self.core.config['permission']['user'])[2] +                    gid = getgrnam(self.core.config['permission']['group'])[2] +                    chown(location, uid, gid) + +            except Exception, e: +                self.fail(e) + +        # convert back to unicode +        location = fs_decode(location) +        name = safe_filename(self.pyfile.name) + +        filename = join(location, name) + +        self.core.addonManager.dispatchEvent("download-start", self.pyfile, url, filename) + +        try: +            newname = self.req.httpDownload(url, filename, get=get, post=post, ref=ref, cookies=cookies, +                                            chunks=self.getChunkCount(), resume=self.resumeDownload, +                                            progressNotify=self.pyfile.setProgress, disposition=disposition) +        finally: +            self.pyfile.size = self.req.size + +        if newname: +            newname = urlparse(newname).path.split("/")[-1] + +            if disposition and newname != name: +                self.logInfo(_("%(name)s saved as %(newname)s") % {"name": name, "newname": newname}) +                self.pyfile.name = newname +                filename = join(location, newname) + +        fs_filename = fs_encode(filename) + +        if self.core.config['permission']['change_file']: +            try: +                chmod(fs_filename, int(self.core.config['permission']['file'], 8)) +            except Exception, e: +                self.logWarning(_("Setting file mode failed"), e) + +        if self.core.config['permission']['change_dl'] and os.name != "nt": +            try: +                uid = getpwnam(self.core.config['permission']['user'])[2] +                gid = getgrnam(self.core.config['permission']['group'])[2] +                chown(fs_filename, uid, gid) + +            except Exception, e: +                self.logWarning(_("Setting User and Group failed"), e) + +        self.lastDownload = filename +        return self.lastDownload + +    def checkDownload(self, rules, api_size=0, max_size=50000, delete=True, read_size=0): +        """ checks the content of the last downloaded file, re match is saved to `lastCheck` + +        :param rules: dict with names and rules to match (compiled regexp or strings) +        :param api_size: expected file size +        :param max_size: if the file is larger then it wont be checked +        :param delete: delete if matched +        :param read_size: amount of bytes to read from files larger then max_size +        :return: dictionary key of the first rule that matched +        """ +        lastDownload = fs_encode(self.lastDownload) +        if not exists(lastDownload): +            return None + +        size = stat(lastDownload) +        size = size.st_size + +        if api_size and api_size <= size: return None +        elif size > max_size and not read_size: return None +        self.logDebug("Download Check triggered") + +        with open(lastDownload, "rb") as f: +            content = f.read(read_size if read_size else -1) + +        # produces encoding errors, better log to other file in the future? +        #self.logDebug("Content: %s" % content) +        for name, rule in rules.iteritems(): +            if isinstance(rule, basestring): +                if rule in content: +                    if delete: +                        remove(lastDownload) +                    return name +            elif hasattr(rule, "search"): +                m = rule.search(content) +                if m: +                    if delete: +                        remove(lastDownload) +                    self.lastCheck = m +                    return name + +    def getPassword(self): +        """ get the password the user provided in the package""" +        password = self.pyfile.package().password +        if not password: return "" +        return password + +    def checkForSameFiles(self, starting=False): +        """ checks if same file was/is downloaded within same package + +        :param starting: indicates that the current download is going to start +        :raises SkipDownload: +        """ + +        pack = self.pyfile.package() + +        for pyfile in self.core.files.cache.values(): +            if pyfile != self.pyfile and pyfile.name == self.pyfile.name and pyfile.package().folder == pack.folder: +                if pyfile.status in (0, 12):  # finished or downloading +                    raise SkipDownload(pyfile.pluginname) +                elif pyfile.status in (5, 7) and starting:  # a download is waiting/starting and was appenrently started before +                    raise SkipDownload(pyfile.pluginname) + +        download_folder = self.core.config['general']['download_folder'] +        location = fs_join(download_folder, pack.folder, self.pyfile.name) + +        if starting and self.core.config['download']['skip_existing'] and exists(location): +            size = os.stat(location).st_size +            if size >= self.pyfile.size: +                raise SkipDownload("File exists") + +        pyfile = self.core.db.findDuplicates(self.pyfile.id, self.pyfile.package().folder, self.pyfile.name) +        if pyfile: +            if exists(location): +                raise SkipDownload(pyfile[0]) + +            self.logDebug("File %s not skipped, because it does not exists." % self.pyfile.name) + +    def clean(self): +        """ clean everything and remove references """ +        if hasattr(self, "pyfile"): +            del self.pyfile + +        if hasattr(self, "req"): +            self.req.close() +            del self.req + +        if hasattr(self, "thread"): +            del self.thread + +        if hasattr(self, "html"): +            del self.html diff --git a/pyload/plugin/__init__.py b/pyload/plugin/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/pyload/plugin/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/pyload/plugin/account/AlldebridCom.py b/pyload/plugin/account/AlldebridCom.py new file mode 100644 index 000000000..75ac430f1 --- /dev/null +++ b/pyload/plugin/account/AlldebridCom.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +import re +import time +import xml.dom.minidom as dom + +from BeautifulSoup import BeautifulSoup + +from pyload.plugin.Account import Account + + +class AlldebridCom(Account): +    __name__    = "AlldebridCom" +    __type__    = "account" +    __version__ = "0.23" + +    __description__ = """AllDebrid.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Andy Voigt", "spamsales@online.de")] + + +    def loadAccountInfo(self, user, req): +        data = self.getAccountData(user) +        html = req.load("http://www.alldebrid.com/account/") +        soup = BeautifulSoup(html) + +        #Try to parse expiration date directly from the control panel page (better accuracy) +        try: +            time_text = soup.find('div', attrs={'class': 'remaining_time_text'}).strong.string + +            self.logDebug("Account expires in: %s" % time_text) + +            p = re.compile('\d+') +            exp_data = p.findall(time_text) +            exp_time = time.time() + int(exp_data[0]) * 24 * 60 * 60 + int( +                exp_data[1]) * 60 * 60 + (int(exp_data[2]) - 1) * 60 + +        #Get expiration date from API +        except Exception: +            data = self.getAccountData(user) +            html = req.load("http://www.alldebrid.com/api.php", +                            get={'action': "info_user", 'login': user, 'pw': data['password']}) + +            self.logDebug(html) + +            xml = dom.parseString(html) +            exp_time = time.time() + int(xml.getElementsByTagName("date")[0].childNodes[0].nodeValue) * 24 * 60 * 60 + +        account_info = {"validuntil": exp_time, "trafficleft": -1} +        return account_info + + +    def login(self, user, data, req): +        html = req.load("http://www.alldebrid.com/register/", +                        get={'action'        : "login", +                             'login_login'   : user, +                             'login_password': data['password']}, +                        decode=True) + +        if "This login doesn't exist" in html \ +           or "The password is not valid" in html \ +           or "Invalid captcha" in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/BackinNet.py b/pyload/plugin/account/BackinNet.py new file mode 100644 index 000000000..ac751c8fb --- /dev/null +++ b/pyload/plugin/account/BackinNet.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class BackinNet(XFSAccount): +    __name__    = "BackinNet" +    __type__    = "account" +    __version__ = "0.01" + +    __description__ = """Backin.net account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "backin.net" diff --git a/pyload/plugin/account/BillionuploadsCom.py b/pyload/plugin/account/BillionuploadsCom.py new file mode 100644 index 000000000..a3325c427 --- /dev/null +++ b/pyload/plugin/account/BillionuploadsCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class BillionuploadsCom(XFSAccount): +    __name__    = "BillionuploadsCom" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """Billionuploads.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "billionuploads.com" diff --git a/pyload/plugin/account/BitshareCom.py b/pyload/plugin/account/BitshareCom.py new file mode 100644 index 000000000..7d7b18ff4 --- /dev/null +++ b/pyload/plugin/account/BitshareCom.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account + + +class BitshareCom(Account): +    __name__    = "BitshareCom" +    __type__    = "account" +    __version__ = "0.13" + +    __description__ = """Bitshare account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Paul King", "")] + + +    def loadAccountInfo(self, user, req): +        html = req.load("http://bitshare.com/mysettings.html") + +        if "\"http://bitshare.com/myupgrade.html\">Free" in html: +            return {"validuntil": -1, "trafficleft": -1, "premium": False} + +        if not '<input type="checkbox" name="directdownload" checked="checked" />' in html: +            self.logWarning(_("Activate direct Download in your Bitshare Account")) + +        return {"validuntil": -1, "trafficleft": -1, "premium": True} + + +    def login(self, user, data, req): +        html = req.load("http://bitshare.com/login.html", +                        post={"user": user, "password": data['password'], "submit": "Login"}, +                        decode=True) + +        if "login" in req.lastEffectiveURL: +            self.wrongPassword() diff --git a/pyload/plugin/account/CatShareNet.py b/pyload/plugin/account/CatShareNet.py new file mode 100644 index 000000000..0dcd4a7ad --- /dev/null +++ b/pyload/plugin/account/CatShareNet.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class CatShareNet(Account): +    __name__    = "CatShareNet" +    __type__    = "account" +    __version__ = "0.05" + +    __description__ = """CatShareNet account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("prOq", "")] + + +    PREMIUM_PATTERN      = r'<a href="/premium">Konto:[\s\n]*Premium' +    VALID_UNTIL_PATTERN  = r'>Konto premium.*?<strong>(.*?)</strong></span>' +    TRAFFIC_LEFT_PATTERN = r'<a href="/premium">([0-9.]+ [kMG]B)' + + +    def loadAccountInfo(self, user, req): +        premium     = False +        validuntil  = -1 +        trafficleft = -1 + +        html = req.load("http://catshare.net/", decode=True) + +        if re.search(self.PREMIUM_PATTERN, html): +            premium = True + +        try: +            expiredate = re.search(self.VALID_UNTIL_PATTERN, html).group(1) +            self.logDebug("Expire date: " + expiredate) + +            validuntil = time.mktime(time.strptime(expiredate, "%Y-%m-%d %H:%M:%S")) + +        except Exception: +            pass + +        try: +            trafficleft = self.parseTraffic(re.search(self.TRAFFIC_LEFT_PATTERN, html).group(1)) + +        except Exception: +            pass + +        return {'premium': premium, 'trafficleft': trafficleft, 'validuntil': validuntil} + + +    def login(self, user, data, req): +        html = req.load("http://catshare.net/login", +                        post={'user_email': user, +                              'user_password': data['password'], +                              'remindPassword': 0, +                              'user[submit]': "Login"}, +                        decode=True) + +        if not '<a href="/logout">Wyloguj</a>' in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/CloudzillaTo.py b/pyload/plugin/account/CloudzillaTo.py new file mode 100644 index 000000000..a07621234 --- /dev/null +++ b/pyload/plugin/account/CloudzillaTo.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Account import Account + + +class CloudzillaTo(Account): +    __name__    = "CloudzillaTo" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """Cloudzilla.to account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    PREMIUM_PATTERN = r'<h2>account type</h2>\s*Premium Account' + + +    def loadAccountInfo(self, user, req): +        html = req.load("http://www.cloudzilla.to/") + +        premium = True if re.search(self.PREMIUM_PATTERN, html) else False + +        return {'validuntil': -1, 'trafficleft': -1, 'premium': premium} + + +    def login(self, user, data, req): +        html = req.load("http://www.cloudzilla.to/", +                        post={'lusername': user, +                              'lpassword': data['password'], +                              'w'        : "dologin"}, +                        decode=True) + +        if "ERROR" in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/CramitIn.py b/pyload/plugin/account/CramitIn.py new file mode 100644 index 000000000..21503f625 --- /dev/null +++ b/pyload/plugin/account/CramitIn.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class CramitIn(XFSAccount): +    __name__    = "CramitIn" +    __type__    = "account" +    __version__ = "0.03" + +    __description__ = """Cramit.in account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    HOSTER_DOMAIN = "cramit.in" diff --git a/pyload/plugin/account/CzshareCom.py b/pyload/plugin/account/CzshareCom.py new file mode 100644 index 000000000..c57e8195b --- /dev/null +++ b/pyload/plugin/account/CzshareCom.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class CzshareCom(Account): +    __name__    = "CzshareCom" +    __type__    = "account" +    __version__ = "0.18" + +    __description__ = """Czshare.com account plugin, now Sdilej.cz""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    CREDIT_LEFT_PATTERN = r'<tr class="active">\s*<td>([\d ,]+) (KiB|MiB|GiB)</td>\s*<td>([^<]*)</td>\s*</tr>' + + +    def loadAccountInfo(self, user, req): +        premium     = False +        validuntil  = None +        trafficleft = None + +        html = req.load("http://sdilej.cz/prehled_kreditu/") + +        try: +            m = re.search(self.CREDIT_LEFT_PATTERN, html) +            trafficleft = self.parseTraffic(m.group(1).replace(' ', '').replace(',', '.')) + m.group(2) +            validuntil  = time.mktime(time.strptime(m.group(3), '%d.%m.%y %H:%M')) + +        except Exception, e: +            self.logError(e) + +        else: +            premium = True + +        return {'premium'    : premium, +                'validuntil' : validuntil, +                'trafficleft': trafficleft} + + +    def login(self, user, data, req): +        html = req.load('https://sdilej.cz/index.php', +                        post={"Prihlasit": "Prihlasit", +                              "login-password": data['password'], +                              "login-name": user}, +                        decode=True) + +        if '<div class="login' in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/DebridItaliaCom.py b/pyload/plugin/account/DebridItaliaCom.py new file mode 100644 index 000000000..563a7c531 --- /dev/null +++ b/pyload/plugin/account/DebridItaliaCom.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class DebridItaliaCom(Account): +    __name__    = "DebridItaliaCom" +    __type__    = "account" +    __version__ = "0.13" + +    __description__ = """Debriditalia.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    WALID_UNTIL_PATTERN = r'Premium valid till: (.+?) \|' + + +    def loadAccountInfo(self, user, req): +        info = {'premium': False, 'validuntil': None, 'trafficleft': None} +        html = req.load("http://debriditalia.com/") + +        if 'Account premium not activated' not in html: +            m = re.search(self.WALID_UNTIL_PATTERN, html) +            if m: +                validuntil = time.mktime(time.strptime(m.group(1), "%d/%m/%Y %H:%M")) +                info = {'premium': True, 'validuntil': validuntil, 'trafficleft': -1} +            else: +                self.logError(_("Unable to retrieve account information")) + +        return info + + +    def login(self, user, data, req): +        html = req.load("http://debriditalia.com/login.php", +                        get={'u': user, 'p': data['password']}, +                        decode=True) + +        if 'NO' in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/DepositfilesCom.py b/pyload/plugin/account/DepositfilesCom.py new file mode 100644 index 000000000..28d2178c4 --- /dev/null +++ b/pyload/plugin/account/DepositfilesCom.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class DepositfilesCom(Account): +    __name__    = "DepositfilesCom" +    __type__    = "account" +    __version__ = "0.32" + +    __description__ = """Depositfiles.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de"), +                       ("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    def loadAccountInfo(self, user, req): +        html = req.load("https://dfiles.eu/de/gold/") +        validuntil = re.search(r"Sie haben Gold Zugang bis: <b>(.*?)</b></div>", html).group(1) + +        validuntil = time.mktime(time.strptime(validuntil, "%Y-%m-%d %H:%M:%S")) + +        return {"validuntil": validuntil, "trafficleft": -1} + + +    def login(self, user, data, req): +        html = req.load("https://dfiles.eu/de/login.php", get={"return": "/de/gold/payment.php"}, +                        post={"login": user, "password": data['password']}, +                        decode=True) + +        if r'<div class="error_message">Sie haben eine falsche Benutzername-Passwort-Kombination verwendet.</div>' in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/DropboxCom.py b/pyload/plugin/account/DropboxCom.py new file mode 100644 index 000000000..c6fd2253d --- /dev/null +++ b/pyload/plugin/account/DropboxCom.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class DropboxCom(SimpleHoster): +    __name__    = "DropboxCom" +    __type__    = "hoster" +    __version__ = "0.04" + +    __pattern__ = r'https?://(?:www\.)?dropbox\.com/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Dropbox.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de")] + + +    NAME_PATTERN = r'<title>Dropbox - (?P<N>.+?)<' +    SIZE_PATTERN = r' ·  (?P<S>[\d.,]+) (?P<U>[\w^_]+)' + +    OFFLINE_PATTERN = r'<title>Dropbox - (404|Shared link error)<' + +    COOKIES = [("dropbox.com", "lang", "en")] + + +    def setup(self): +        self.multiDL        = True +        self.chunkLimit     = 1 +        self.resumeDownload = True + + +    def handleFree(self, pyfile): +        self.download(pyfile.url, get={'dl': "1"}) diff --git a/pyload/plugin/account/EasybytezCom.py b/pyload/plugin/account/EasybytezCom.py new file mode 100644 index 000000000..c7d717474 --- /dev/null +++ b/pyload/plugin/account/EasybytezCom.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class EasybytezCom(XFSAccount): +    __name__    = "EasybytezCom" +    __type__    = "account" +    __version__ = "0.12" + +    __description__ = """EasyBytez.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("guidobelix", "guidobelix@hotmail.it")] + + +    HOSTER_DOMAIN = "easybytez.com" diff --git a/pyload/plugin/account/EuroshareEu.py b/pyload/plugin/account/EuroshareEu.py new file mode 100644 index 000000000..6fa9f35b0 --- /dev/null +++ b/pyload/plugin/account/EuroshareEu.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class EuroshareEu(Account): +    __name__    = "EuroshareEu" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """Euroshare.eu account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    def loadAccountInfo(self, user, req): +        self.relogin(user) +        html = req.load("http://euroshare.eu/customer-zone/settings/") + +        m = re.search('id="input_expire_date" value="(\d+\.\d+\.\d+ \d+:\d+)"', html) +        if m is None: +            premium, validuntil = False, -1 +        else: +            premium = True +            validuntil = time.mktime(time.strptime(m.group(1), "%d.%m.%Y %H:%M")) + +        return {"validuntil": validuntil, "trafficleft": -1, "premium": premium} + + +    def login(self, user, data, req): +        html = req.load('http://euroshare.eu/customer-zone/login/', +                        post={"trvale": "1", +                              "login": user, +                              "password": data['password']}, +                        decode=True) + +        if u">Nesprávne prihlasovacie meno alebo heslo" in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/ExashareCom.py b/pyload/plugin/account/ExashareCom.py new file mode 100644 index 000000000..efd2587c0 --- /dev/null +++ b/pyload/plugin/account/ExashareCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class ExashareCom(XFSAccount): +    __name__    = "ExashareCom" +    __type__    = "account" +    __version__ = "0.01" + +    __description__ = """Exashare.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "exashare.com" diff --git a/pyload/plugin/account/FastixRu.py b/pyload/plugin/account/FastixRu.py new file mode 100644 index 000000000..69f78c3d9 --- /dev/null +++ b/pyload/plugin/account/FastixRu.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class FastixRu(Account): +    __name__    = "FastixRu" +    __type__    = "account" +    __version__ = "0.03" + +    __description__ = """Fastix account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Massimo Rosamilia", "max@spiritix.eu")] + + +    def loadAccountInfo(self, user, req): +        data = self.getAccountData(user) +        html = json_loads(req.load("http://fastix.ru/api_v2/", get={'apikey': data['api'], 'sub': "getaccountdetails"})) + +        points = html['points'] +        kb     = float(points) * 1024 ** 2 / 1000 + +        if points > 0: +            account_info = {"validuntil": -1, "trafficleft": kb} +        else: +            account_info = {"validuntil": None, "trafficleft": None, "premium": False} +        return account_info + + +    def login(self, user, data, req): +        html = req.load("http://fastix.ru/api_v2/", +                        get={'sub': "get_apikey", 'email': user, 'password': data['password']}) + +        api = json_loads(html) +        api = api['apikey'] + +        data['api'] = api + +        if "error_code" in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/FastshareCz.py b/pyload/plugin/account/FastshareCz.py new file mode 100644 index 000000000..ce79e26ad --- /dev/null +++ b/pyload/plugin/account/FastshareCz.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Account import Account +from pyload.utils import parseFileSize + + +class FastshareCz(Account): +    __name__    = "FastshareCz" +    __type__    = "account" +    __version__ = "0.06" + +    __description__ = """Fastshare.cz account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    CREDIT_PATTERN = r'Credit\s*:\s*</td>\s*<td>(.+?)\s*<' + + +    def loadAccountInfo(self, user, req): +        validuntil  = -1 +        trafficleft = None +        premium     = False + +        html = req.load("http://www.fastshare.cz/user", decode=True) + +        m = re.search(self.CREDIT_PATTERN, html) +        if m: +            trafficleft = self.parseTraffic(m.group(1)) + +        premium = bool(trafficleft) + +        return {'validuntil' : validuntil, +                'trafficleft': trafficleft, +                'premium'    : premium} + + +    def login(self, user, data, req): +        req.cj.setCookie("fastshare.cz", "lang", "en") + +        req.load('http://www.fastshare.cz/login')  # Do not remove or it will not login + +        html = req.load("http://www.fastshare.cz/sql.php", +                        post={'login': user, 'heslo': data['password']}, +                        decode=True) + +        if ">Wrong username or password" in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/File4SafeCom.py b/pyload/plugin/account/File4SafeCom.py new file mode 100644 index 000000000..c48956d38 --- /dev/null +++ b/pyload/plugin/account/File4SafeCom.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class File4SafeCom(XFSAccount): +    __name__    = "File4SafeCom" +    __type__    = "account" +    __version__ = "0.05" + +    __description__ = """File4Safe.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    HOSTER_DOMAIN = "file4safe.com" + +    LOGIN_FAIL_PATTERN = r'input_login' diff --git a/pyload/plugin/account/FileParadoxIn.py b/pyload/plugin/account/FileParadoxIn.py new file mode 100644 index 000000000..02b923519 --- /dev/null +++ b/pyload/plugin/account/FileParadoxIn.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class FileParadoxIn(XFSAccount): +    __name__    = "FileParadoxIn" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """FileParadox.in account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "fileparadox.in" diff --git a/pyload/plugin/account/FilecloudIo.py b/pyload/plugin/account/FilecloudIo.py new file mode 100644 index 000000000..6d2dcb92a --- /dev/null +++ b/pyload/plugin/account/FilecloudIo.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class FilecloudIo(Account): +    __name__    = "FilecloudIo" +    __type__    = "account" +    __version__ = "0.04" + +    __description__ = """FilecloudIo account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    def loadAccountInfo(self, user, req): +        # It looks like the first API request always fails, so we retry 5 times, it should work on the second try +        for _i in xrange(5): +            rep = req.load("https://secure.filecloud.io/api-fetch_apikey.api", +                           post={"username": user, "password": self.getAccountData(user)['password']}) +            rep = json_loads(rep) +            if rep['status'] == 'ok': +                break +            elif rep['status'] == 'error' and rep['message'] == 'no such user or wrong password': +                self.logError(_("Wrong username or password")) +                return {"valid": False, "premium": False} +        else: +            return {"premium": False} + +        akey = rep['akey'] +        self.accounts[user]['akey'] = akey  # Saved for hoster plugin +        rep = req.load("http://api.filecloud.io/api-fetch_account_details.api", +                       post={"akey": akey}) +        rep = json_loads(rep) + +        if rep['is_premium'] == 1: +            return {"validuntil": float(rep['premium_until']), "trafficleft": -1} +        else: +            return {"premium": False} + + +    def login(self, user, data, req): +        req.cj.setCookie("secure.filecloud.io", "lang", "en") +        html = req.load('https://secure.filecloud.io/user-login.html') + +        if not hasattr(self, "form_data"): +            self.form_data = {} + +        self.form_data['username'] = user +        self.form_data['password'] = data['password'] + +        html = req.load('https://secure.filecloud.io/user-login_p.html', +                        post=self.form_data, +                        multipart=True) + +        if "you have successfully logged in" not in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/FilefactoryCom.py b/pyload/plugin/account/FilefactoryCom.py new file mode 100644 index 000000000..36720595f --- /dev/null +++ b/pyload/plugin/account/FilefactoryCom.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pycurl import REFERER + +from pyload.plugin.Account import Account + + +class FilefactoryCom(Account): +    __name__    = "FilefactoryCom" +    __type__    = "account" +    __version__ = "0.15" + +    __description__ = """Filefactory.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    VALID_UNTIL_PATTERN = r'Premium valid until: <strong>(?P<D>\d{1,2})\w{1,2} (?P<M>\w{3}), (?P<Y>\d{4})</strong>' + + +    def loadAccountInfo(self, user, req): +        html = req.load("http://www.filefactory.com/account/") + +        m = re.search(self.VALID_UNTIL_PATTERN, html) +        if m: +            premium = True +            validuntil = re.sub(self.VALID_UNTIL_PATTERN, '\g<D> \g<M> \g<Y>', m.group(0)) +            validuntil = time.mktime(time.strptime(validuntil, "%d %b %Y")) +        else: +            premium = False +            validuntil = -1 + +        return {"premium": premium, "trafficleft": -1, "validuntil": validuntil} + + +    def login(self, user, data, req): +        req.http.c.setopt(REFERER, "http://www.filefactory.com/member/login.php") + +        html = req.load("http://www.filefactory.com/member/signin.php", +                        post={"loginEmail"   : user, +                              "loginPassword": data['password'], +                              "Submit"       : "Sign In"}) + +        if req.lastEffectiveURL != "http://www.filefactory.com/account/": +            self.wrongPassword() diff --git a/pyload/plugin/account/FilejungleCom.py b/pyload/plugin/account/FilejungleCom.py new file mode 100644 index 000000000..dfa7edddb --- /dev/null +++ b/pyload/plugin/account/FilejungleCom.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class FilejungleCom(Account): +    __name__    = "FilejungleCom" +    __type__    = "account" +    __version__ = "0.12" + +    __description__ = """Filejungle.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    login_timeout = 60 + +    URL = "http://filejungle.com/" +    TRAFFIC_LEFT_PATTERN = r'"/extend_premium\.php">Until (\d+ \w+ \d+)<br' +    LOGIN_FAILED_PATTERN = r'<span htmlfor="loginUser(Name|Password)" generated="true" class="fail_info">' + + +    def loadAccountInfo(self, user, req): +        html = req.load(self.URL + "dashboard.php") +        m = re.search(self.TRAFFIC_LEFT_PATTERN, html) +        if m: +            premium = True +            validuntil = time.mktime(time.strptime(m.group(1), "%d %b %Y")) +        else: +            premium = False +            validuntil = -1 + +        return {"premium": premium, "trafficleft": -1, "validuntil": validuntil} + + +    def login(self, user, data, req): +        html = req.load(self.URL + "login.php", +                        post={"loginUserName": user, +                              "loginUserPassword": data['password'], +                              "loginFormSubmit": "Login", +                              "recaptcha_challenge_field": "", +                              "recaptcha_response_field": "", +                              "recaptcha_shortencode_field": ""}, +                        decode=True) + +        if re.search(self.LOGIN_FAILED_PATTERN, html): +            self.wrongPassword() diff --git a/pyload/plugin/account/FileomCom.py b/pyload/plugin/account/FileomCom.py new file mode 100644 index 000000000..36a11e411 --- /dev/null +++ b/pyload/plugin/account/FileomCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class FileomCom(XFSAccount): +    __name__    = "FileomCom" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """Fileom.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "fileom.com" diff --git a/pyload/plugin/account/FilerNet.py b/pyload/plugin/account/FilerNet.py new file mode 100644 index 000000000..8b1da8ce3 --- /dev/null +++ b/pyload/plugin/account/FilerNet.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class FilerNet(Account): +    __name__    = "FilerNet" +    __type__    = "account" +    __version__ = "0.04" + +    __description__ = """Filer.net account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    TOKEN_PATTERN = r'_csrf_token" value="(.+?)" />' +    WALID_UNTIL_PATTERN = r'Der Premium-Zugang ist gÃŒltig bis (.+)\.\s*</td>' +    TRAFFIC_PATTERN = r'Traffic</th>\s*<td>([^<]+)</td>' +    FREE_PATTERN = r'Account Status</th>\s*<td>\s*Free' + + +    def loadAccountInfo(self, user, req): +        html = req.load("https://filer.net/profile") + +        # Free user +        if re.search(self.FREE_PATTERN, html): +            return {"premium": False, "validuntil": None, "trafficleft": None} + +        until   = re.search(self.WALID_UNTIL_PATTERN, html) +        traffic = re.search(self.TRAFFIC_PATTERN, html) + +        if until and traffic: +            validuntil  = time.mktime(time.strptime(until.group(1), "%d.%m.%Y %H:%M:%S")) +            trafficleft = self.parseTraffic(traffic.group(1)) +            return {"premium": True, "validuntil": validuntil, "trafficleft": trafficleft} + +        else: +            self.logError(_("Unable to retrieve account information")) +            return {"premium": False, "validuntil": None, "trafficleft": None} + + +    def login(self, user, data, req): +        html = req.load("https://filer.net/login") + +        token = re.search(self.TOKEN_PATTERN, html).group(1) + +        html = req.load("https://filer.net/login_check", +                        post={"_username": user, +                              "_password": data['password'], +                              "_remember_me": "on", +                              "_csrf_token": token, +                              "_target_path": "https://filer.net/"}, +                        decode=True) + +        if 'Logout' not in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/FilerioCom.py b/pyload/plugin/account/FilerioCom.py new file mode 100644 index 000000000..1d9f8744b --- /dev/null +++ b/pyload/plugin/account/FilerioCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class FilerioCom(XFSAccount): +    __name__    = "FilerioCom" +    __type__    = "account" +    __version__ = "0.03" + +    __description__ = """FileRio.in account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    HOSTER_DOMAIN = "filerio.in" diff --git a/pyload/plugin/account/FilesMailRu.py b/pyload/plugin/account/FilesMailRu.py new file mode 100644 index 000000000..5989b5f1a --- /dev/null +++ b/pyload/plugin/account/FilesMailRu.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account + + +class FilesMailRu(Account): +    __name__    = "FilesMailRu" +    __type__    = "account" +    __version__ = "0.11" + +    __description__ = """Filesmail.ru account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org")] + + +    def loadAccountInfo(self, user, req): +        return {"validuntil": None, "trafficleft": None} + + +    def login(self, user, data, req): +        user, domain = user.split("@") + +        html = req.load("http://swa.mail.ru/cgi-bin/auth", +                        post={"Domain": domain, +                              "Login": user, +                              "Password": data['password'], +                              "Page": "http://files.mail.ru/"}, +                        decode=True) + +        if "ÐевеÑМПе ÐžÐŒÑ Ð¿ÐŸÐ»ÑзПваÑÐµÐ»Ñ ÐžÐ»Ðž паÑПлÑ" in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/FileserveCom.py b/pyload/plugin/account/FileserveCom.py new file mode 100644 index 000000000..08dfe24c7 --- /dev/null +++ b/pyload/plugin/account/FileserveCom.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +import time + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class FileserveCom(Account): +    __name__    = "FileserveCom" +    __type__    = "account" +    __version__ = "0.20" + +    __description__ = """Fileserve.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de")] + + +    def loadAccountInfo(self, user, req): +        data = self.getAccountData(user) + +        html = req.load("http://app.fileserve.com/api/login/", post={"username": user, "password": data['password'], +                                                                     "submit": "Submit+Query"}) +        res = json_loads(html) + +        if res['type'] == "premium": +            validuntil = time.mktime(time.strptime(res['expireTime'], "%Y-%m-%d %H:%M:%S")) +            return {"trafficleft": res['traffic'], "validuntil": validuntil} +        else: +            return {"premium": False, "trafficleft": None, "validuntil": None} + + +    def login(self, user, data, req): +        html = req.load("http://app.fileserve.com/api/login/", post={"username": user, "password": data['password'], +                                                                     "submit": "Submit+Query"}) +        res = json_loads(html) + +        if not res['type']: +            self.wrongPassword() + +        #login at fileserv html +        req.load("http://www.fileserve.com/login.php", +                 post={"loginUserName": user, "loginUserPassword": data['password'], "autoLogin": "checked", +                       "loginFormSubmit": "Login"}) diff --git a/pyload/plugin/account/FourSharedCom.py b/pyload/plugin/account/FourSharedCom.py new file mode 100644 index 000000000..127f9d58a --- /dev/null +++ b/pyload/plugin/account/FourSharedCom.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class FourSharedCom(Account): +    __name__    = "FourSharedCom" +    __type__    = "account" +    __version__ = "0.04" + +    __description__ = """FourShared.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    def loadAccountInfo(self, user, req): +        # Free mode only for now +        return {"premium": False} + + +    def login(self, user, data, req): +        req.cj.setCookie("4shared.com", "4langcookie", "en") + +        res = req.load("http://www.4shared.com/web/login", +                       post={'login'    : user, +                             'password' : data['password'], +                             'remember' : "on", +                             '_remember': "on", +                             'returnTo' : "http://www.4shared.com/account/home.jsp"}, +                       decode=True) + +        if 'Please log in to access your 4shared account' in res: +            self.wrongPassword() diff --git a/pyload/plugin/account/FreakshareCom.py b/pyload/plugin/account/FreakshareCom.py new file mode 100644 index 000000000..5aef6f86c --- /dev/null +++ b/pyload/plugin/account/FreakshareCom.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class FreakshareCom(Account): +    __name__    = "FreakshareCom" +    __type__    = "account" +    __version__ = "0.13" + +    __description__ = """Freakshare.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org")] + + +    def loadAccountInfo(self, user, req): +        premium = False +        validuntil  = None +        trafficleft = None + +        html = req.load("http://freakshare.com/") + +        try: +            m = re.search(r'ltig bis:</td>\s*<td><b>([\d.:-]+)</b></td>', html, re.M) +            validuntil = time.mktime(time.strptime(m.group(1).strip(), "%d.%m.%Y - %H:%M")) + +        except Exception: +            pass + +        try: +            m = re.search(r'Traffic verbleibend:</td>\s*<td>([^<]+)', html, re.M) +            trafficleft = self.parseTraffic(m.group(1)) + +        except Exception: +            pass + +        return {"premium": premium, "validuntil": validuntil, "trafficleft": trafficleft} + + +    def login(self, user, data, req): +        req.load("http://freakshare.com/index.php?language=EN") + +        html = req.load("http://freakshare.com/login.html", +                        post={"submit": "Login", "user": user, "pass": data['password']}, +                        decode=True) + +        if ">Wrong Username or Password" in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/FreeWayMe.py b/pyload/plugin/account/FreeWayMe.py new file mode 100644 index 000000000..dcd9d34cf --- /dev/null +++ b/pyload/plugin/account/FreeWayMe.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class FreeWayMe(Account): +    __name__    = "FreeWayMe" +    __type__    = "account" +    __version__ = "0.13" + +    __description__ = """FreeWayMe account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Nicolas Giese", "james@free-way.me")] + + +    def loadAccountInfo(self, user, req): +        status = self.getAccountStatus(user, req) + +        self.logDebug(status) + +        account_info = {"validuntil": -1, "premium": False} +        if status['premium'] == "Free": +            account_info['trafficleft'] = self.parseTraffic(status['guthaben'] + "MB") +        elif status['premium'] == "Spender": +            account_info['trafficleft'] = -1 +        elif status['premium'] == "Flatrate": +            account_info = {"validuntil": float(status['Flatrate']), +                            "trafficleft": -1, +                            "premium": True} + +        return account_info + + +    def login(self, user, data, req): +        status = self.getAccountStatus(user, req) + +        # Check if user and password are valid +        if not status: +            self.wrongPassword() + + +    def getAccountStatus(self, user, req): +        answer = req.load("https://www.free-way.me/ajax/jd.php", +                          get={"id": 4, "user": user, "pass": self.getAccountData(user)['password']}) + +        self.logDebug("Login: %s" % answer) + +        if answer == "Invalid login": +            self.wrongPassword() + +        return json_loads(answer) diff --git a/pyload/plugin/account/FshareVn.py b/pyload/plugin/account/FshareVn.py new file mode 100644 index 000000000..85306ce8f --- /dev/null +++ b/pyload/plugin/account/FshareVn.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class FshareVn(Account): +    __name__    = "FshareVn" +    __type__    = "account" +    __version__ = "0.09" + +    __description__ = """Fshare.vn account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    VALID_UNTIL_PATTERN = ur'<dt>Thá»i hạn dùng:</dt>\s*<dd>([^<]+)</dd>' +    LIFETIME_PATTERN = ur'<dt>Lần ÄÄng nháºp trưá»c:</dt>\s*<dd>.+?</dd>' +    TRAFFIC_LEFT_PATTERN = ur'<dt>Tá»ng Dung Lượng Tài Khoản</dt>\s*<dd.*?>([\d.]+) ([kKMG])B</dd>' +    DIRECT_DOWNLOAD_PATTERN = ur'<input type="checkbox"\s*([^=>]*)[^>]*/>KÃch hoạt download trá»±c tiếp</dt>' + + +    def loadAccountInfo(self, user, req): +        html = req.load("http://www.fshare.vn/account_info.php", decode=True) + +        if re.search(self.LIFETIME_PATTERN, html): +            self.logDebug("Lifetime membership detected") +            trafficleft = self.getTrafficLeft() +            return {"validuntil": -1, "trafficleft": trafficleft, "premium": True} + +        m = re.search(self.VALID_UNTIL_PATTERN, html) +        if m: +            premium = True +            validuntil = time.mktime(time.strptime(m.group(1), '%I:%M:%S %p %d-%m-%Y')) +            trafficleft = self.getTrafficLeft() +        else: +            premium = False +            validuntil = None +            trafficleft = None + +        return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium} + + +    def login(self, user, data, req): +        html = req.load("https://www.fshare.vn/login.php", +                        post={'LoginForm[email]'     : user, +                              'LoginForm[password]'  : data['password'], +                              'LoginForm[rememberMe]': 1, +                              'yt0'                  : "Login"}, +                        referer=True, +                        decode=True) + +        if not re.search(r'<img\s+alt="VIP"', html): +            self.wrongPassword() + + +    def getTrafficLeft(self): +        m = re.search(self.TRAFFIC_LEFT_PATTERN, html) +        return self.parseTraffic(m.group(1) + m.group(2)) if m else 0 diff --git a/pyload/plugin/account/Ftp.py b/pyload/plugin/account/Ftp.py new file mode 100644 index 000000000..67cde2cdd --- /dev/null +++ b/pyload/plugin/account/Ftp.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account + + +class Ftp(Account): +    __name__    = "Ftp" +    __type__    = "account" +    __version__ = "0.01" + +    __description__ = """Ftp dummy account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    login_timeout  = -1  #: Unlimited +    info_threshold = -1  #: Unlimited diff --git a/pyload/plugin/account/HellshareCz.py b/pyload/plugin/account/HellshareCz.py new file mode 100644 index 000000000..94467b375 --- /dev/null +++ b/pyload/plugin/account/HellshareCz.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class HellshareCz(Account): +    __name__    = "HellshareCz" +    __type__    = "account" +    __version__ = "0.16" + +    __description__ = """Hellshare.cz account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    CREDIT_LEFT_PATTERN = r'<div class="credit-link">\s*<table>\s*<tr>\s*<th>(\d+|\d\d\.\d\d\.)</th>' + + +    def loadAccountInfo(self, user, req): +        self.relogin(user) +        html = req.load("http://www.hellshare.com/") + +        m = re.search(self.CREDIT_LEFT_PATTERN, html) +        if m is None: +            trafficleft = None +            validuntil = None +            premium = False +        else: +            credit = m.group(1) +            premium = True +            try: +                if "." in credit: +                    #Time-based account +                    vt = [int(x) for x in credit.split('.')[:2]] +                    lt = time.localtime() +                    year = lt.tm_year + int(vt[1] < lt.tm_mon or (vt[1] == lt.tm_mon and vt[0] < lt.tm_mday)) +                    validuntil = time.mktime(time.strptime("%s%d 23:59:59" % (credit, year), "%d.%m.%Y %H:%M:%S")) +                    trafficleft = -1 +                else: +                    #Traffic-based account +                    trafficleft = self.parseTraffic(credit + "MB") +                    validuntil = -1 +            except Exception, e: +                self.logError(_("Unable to parse credit info"), e) +                validuntil = -1 +                trafficleft = -1 + +        return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium} + + +    def login(self, user, data, req): +        html = req.load('http://www.hellshare.com/', decode=True) +        if req.lastEffectiveURL != 'http://www.hellshare.com/': +            #Switch to English +            self.logDebug("Switch lang - URL: %s" % req.lastEffectiveURL) + +            json = req.load("%s?do=locRouter-show" % req.lastEffectiveURL) +            hash = re.search(r"(\-\-[0-9a-f]+\-)", json).group(1) + +            self.logDebug("Switch lang - HASH: %s" % hash) + +            html = req.load('http://www.hellshare.com/%s/' % hash, decode=True) + +        if re.search(self.CREDIT_LEFT_PATTERN, html): +            self.logDebug("Already logged in") +            return + +        html = req.load('http://www.hellshare.com/login?do=loginForm-submit', +                        post={"login": "Log in", +                              "password": data['password'], +                              "username": user, +                              "perm_login": "on"}, +                        decode=True) + +        if "<p>You input a wrong user name or wrong password</p>" in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/Http.py b/pyload/plugin/account/Http.py new file mode 100644 index 000000000..2571ef712 --- /dev/null +++ b/pyload/plugin/account/Http.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account + + +class Http(Account): +    __name__    = "Http" +    __type__    = "account" +    __version__ = "0.01" + +    __description__ = """Http dummy account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    login_timeout  = -1  #: Unlimited +    info_threshold = -1  #: Unlimited diff --git a/pyload/plugin/account/HugefilesNet.py b/pyload/plugin/account/HugefilesNet.py new file mode 100644 index 000000000..eb383fb17 --- /dev/null +++ b/pyload/plugin/account/HugefilesNet.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class HugefilesNet(XFSAccount): +    __name__    = "HugefilesNet" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """Hugefiles.net account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "hugefiles.net" diff --git a/pyload/plugin/account/HundredEightyUploadCom.py b/pyload/plugin/account/HundredEightyUploadCom.py new file mode 100644 index 000000000..72185a4bb --- /dev/null +++ b/pyload/plugin/account/HundredEightyUploadCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class HundredEightyUploadCom(XFSAccount): +    __name__    = "HundredEightyUploadCom" +    __type__    = "account" +    __version__ = "0.03" + +    __description__ = """180upload.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "180upload.com" diff --git a/pyload/plugin/account/JunkyvideoCom.py b/pyload/plugin/account/JunkyvideoCom.py new file mode 100644 index 000000000..3380b8dc7 --- /dev/null +++ b/pyload/plugin/account/JunkyvideoCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class JunkyvideoCom(XFSAccount): +    __name__    = "JunkyvideoCom" +    __type__    = "account" +    __version__ = "0.01" + +    __description__ = """Junkyvideo.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "junkyvideo.com" diff --git a/pyload/plugin/account/JunocloudMe.py b/pyload/plugin/account/JunocloudMe.py new file mode 100644 index 000000000..0ffa92eb6 --- /dev/null +++ b/pyload/plugin/account/JunocloudMe.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class JunocloudMe(XFSAccount): +    __name__    = "JunocloudMe" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """Junocloud.me account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("guidobelix", "guidobelix@hotmail.it")] + + +    HOSTER_DOMAIN = "junocloud.me" diff --git a/pyload/plugin/account/Keep2ShareCc.py b/pyload/plugin/account/Keep2ShareCc.py new file mode 100644 index 000000000..a3cc2c40d --- /dev/null +++ b/pyload/plugin/account/Keep2ShareCc.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class Keep2ShareCc(Account): +    __name__    = "Keep2ShareCc" +    __type__    = "account" +    __version__ = "0.05" + +    __description__ = """Keep2Share.cc account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("aeronaut", "aeronaut@pianoguy.de"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    VALID_UNTIL_PATTERN  = r'Premium expires:\s*<b>(.+?)<' +    TRAFFIC_LEFT_PATTERN = r'Available traffic \(today\):\s*<b><a href="/user/statistic.html">(.+?)<' + +    LOGIN_FAIL_PATTERN = r'Please fix the following input errors' + + +    def loadAccountInfo(self, user, req): +        validuntil  = None +        trafficleft = -1 +        premium     = False + +        html = req.load("http://keep2share.cc/site/profile.html", decode=True) + +        m = re.search(self.VALID_UNTIL_PATTERN, html) +        if m: +            expiredate = m.group(1).strip() +            self.logDebug("Expire date: " + expiredate) + +            if expiredate == "LifeTime": +                premium    = True +                validuntil = -1 +            else: +                try: +                    validuntil = time.mktime(time.strptime(expiredate, "%Y.%m.%d")) + +                except Exception, e: +                    self.logError(e) + +                else: +                    premium = True if validuntil > time.mktime(time.gmtime()) else False + +            m = re.search(self.TRAFFIC_LEFT_PATTERN, html) +            if m: +                try: +                    trafficleft = self.parseTraffic(m.group(1)) + +                except Exception, e: +                    self.logError(e) + +        return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium} + + +    def login(self, user, data, req): +        req.cj.setCookie("keep2share.cc", "lang", "en") + +        html = req.load("http://keep2share.cc/login.html", +                        post={'LoginForm[username]'  : user, +                              'LoginForm[password]'  : data['password'], +                              'LoginForm[rememberMe]': 1, +                              'yt0'                  : ""}, +                        decode=True) + +        if re.search(self.LOGIN_FAIL_PATTERN, html): +            self.wrongPassword() diff --git a/pyload/plugin/account/LetitbitNet.py b/pyload/plugin/account/LetitbitNet.py new file mode 100644 index 000000000..d0f08d0bb --- /dev/null +++ b/pyload/plugin/account/LetitbitNet.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +# from pyload.utils import json_loads, json_dumps + + +class LetitbitNet(Account): +    __name__    = "LetitbitNet" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """Letitbit.net account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    def loadAccountInfo(self, user, req): +        ## DISABLED BECAUSE IT GET 'key exausted' EVEN IF VALID ## +        # api_key = self.getAccountData(user)['password'] +        # json_data = [api_key, ['key/info']] +        # api_rep = req.load('http://api.letitbit.net/json', post={'r': json_dumps(json_data)}) +        # self.logDebug("API Key Info: " + api_rep) +        # api_rep = json_loads(api_rep) +        # +        # if api_rep['status'] == 'FAIL': +        #     self.logWarning(api_rep['data']) +        #     return {'valid': False, 'premium': False} + +        return {"premium": True} + + +    def login(self, user, data, req): +        # API_KEY is the username and the PREMIUM_KEY is the password +        self.logInfo(_("You must use your API KEY as username and the PREMIUM KEY as password")) diff --git a/pyload/plugin/account/LinestorageCom.py b/pyload/plugin/account/LinestorageCom.py new file mode 100644 index 000000000..7a5d63a47 --- /dev/null +++ b/pyload/plugin/account/LinestorageCom.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class LinestorageCom(XFSAccount): +    __name__    = "LinestorageCom" +    __type__    = "account" +    __version__ = "0.03" + +    __description__ = """Linestorage.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "linestorage.com" +    HOSTER_URL    = "http://linestorage.com/" diff --git a/pyload/plugin/account/LinksnappyCom.py b/pyload/plugin/account/LinksnappyCom.py new file mode 100644 index 000000000..0b1176ee9 --- /dev/null +++ b/pyload/plugin/account/LinksnappyCom.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +from hashlib import md5 + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class LinksnappyCom(Account): +    __name__    = "LinksnappyCom" +    __type__    = "account" +    __version__ = "0.05" +    __description__ = """Linksnappy.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    def loadAccountInfo(self, user, req): +        data = self.getAccountData(user) +        r = req.load('http://gen.linksnappy.com/lseAPI.php', +                     get={'act': 'USERDETAILS', 'username': user, 'password': md5(data['password']).hexdigest()}) + +        self.logDebug("JSON data: " + r) + +        j = json_loads(r) + +        if j['error']: +            return {"premium": False} + +        validuntil = j['return']['expire'] + +        if validuntil == 'lifetime': +            validuntil = -1 + +        elif validuntil == 'expired': +            return {"premium": False} + +        else: +            validuntil = float(validuntil) + +        if 'trafficleft' not in j['return'] or isinstance(j['return']['trafficleft'], str): +            trafficleft = -1 +        else: +            trafficleft = self.parseTraffic("%d MB" % j['return']['trafficleft']) + +        return {"premium": True, "validuntil": validuntil, "trafficleft": trafficleft} + + +    def login(self, user, data, req): +        r = req.load("http://gen.linksnappy.com/lseAPI.php", +                     get={'act'     : 'USERDETAILS', +                          'username': user, +                          'password': md5(data['password']).hexdigest()}, +                     decode=True) + +        if 'Invalid Account Details' in r: +            self.wrongPassword() diff --git a/pyload/plugin/account/MegaDebridEu.py b/pyload/plugin/account/MegaDebridEu.py new file mode 100644 index 000000000..c2e64bcc7 --- /dev/null +++ b/pyload/plugin/account/MegaDebridEu.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class MegaDebridEu(Account): +    __name__    = "MegaDebridEu" +    __type__    = "account" +    __version__ = "0.20" + +    __description__ = """mega-debrid.eu account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("D.Ducatel", "dducatel@je-geek.fr")] + + +    # Define the base URL of MegaDebrid api +    API_URL = "https://www.mega-debrid.eu/api.php" + + +    def loadAccountInfo(self, user, req): +        data = self.getAccountData(user) +        jsonResponse = req.load(self.API_URL, +                                get={'action': 'connectUser', 'login': user, 'password': data['password']}) +        res = json_loads(jsonResponse) + +        if res['response_code'] == "ok": +            return {"premium": True, "validuntil": float(res['vip_end']), "status": True} +        else: +            self.logError(res) +            return {"status": False, "premium": False} + + +    def login(self, user, data, req): +        jsonResponse = req.load(self.API_URL, +                                get={'action': 'connectUser', 'login': user, 'password': data['password']}) +        res = json_loads(jsonResponse) +        if res['response_code'] != "ok": +            self.wrongPassword() diff --git a/pyload/plugin/account/MegaRapidCz.py b/pyload/plugin/account/MegaRapidCz.py new file mode 100644 index 000000000..cdb5c732b --- /dev/null +++ b/pyload/plugin/account/MegaRapidCz.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class MegaRapidCz(Account): +    __name__    = "MegaRapidCz" +    __type__    = "account" +    __version__ = "0.35" + +    __description__ = """MegaRapid.cz account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("MikyWoW", "mikywow@seznam.cz"), +                       ("zoidberg", "zoidberg@mujmail.cz")] + + +    login_timeout = 60 + +    LIMITDL_PATTERN = ur'<td>Max. poÄet paralelnÃch stahovánÃ: </td><td>(\d+)' +    VALID_UNTIL_PATTERN = ur'<td>Paušálnà stahovánà aktivnÃ. Vypršà </td><td><strong>(.*?)</strong>' +    TRAFFIC_LEFT_PATTERN = r'<tr><td>Kredit</td><td>(.*?) GiB' + + +    def loadAccountInfo(self, user, req): +        htmll = req.load("http://megarapid.cz/mujucet/", decode=True) + +        m = re.search(self.LIMITDL_PATTERN, htmll) +        if m: +            data = self.getAccountData(user) +            data['options']['limitDL'] = [int(m.group(1))] + +        m = re.search(self.VALID_UNTIL_PATTERN, htmll) +        if m: +            validuntil = time.mktime(time.strptime(m.group(1), "%d.%m.%Y - %H:%M")) +            return {"premium": True, "trafficleft": -1, "validuntil": validuntil} + +        m = re.search(self.TRAFFIC_LEFT_PATTERN, htmll) +        if m: +            trafficleft = float(m.group(1)) * (1 << 20) +            return {"premium": True, "trafficleft": trafficleft, "validuntil": -1} + +        return {"premium": False, "trafficleft": None, "validuntil": None} + + +    def login(self, user, data, req): +        html = req.load("http://megarapid.cz/prihlaseni/", decode=True) + +        if "Heslo:" in html: +            start = html.index('id="inp_hash" name="hash" value="') +            html = html[start + 33:] +            hashes = html[0:32] +            html = req.load("http://megarapid.cz/prihlaseni/", +                           post={"hash": hashes, +                                 "login": user, +                                 "pass1": data['password'], +                                 "remember": 0, +                                 "sbmt": u"PÅihlásit"}) diff --git a/pyload/plugin/account/MegaRapidoNet.py b/pyload/plugin/account/MegaRapidoNet.py new file mode 100644 index 000000000..5d92a62d4 --- /dev/null +++ b/pyload/plugin/account/MegaRapidoNet.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class MegaRapidoNet(Account): +    __name__    = "MegaRapidoNet" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """MegaRapido.net account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Kagenoshin", "kagenoshin@gmx.ch")] + + +    VALID_UNTIL_PATTERN = r'<\s*?div[^>]*?class\s*?=\s*?[\'"]premium_index[\'"].*?>[^<]*?<[^>]*?b.*?>\s*?TEMPO\s*?PREMIUM.*?<[^>]*?/b.*?>\s*?(\d*)[^\d]*?DIAS[^\d]*?(\d*)[^\d]*?HORAS[^\d]*?(\d*)[^\d]*?MINUTOS[^\d]*?(\d*)[^\d]*?SEGUNDOS' +    USER_ID_PATTERN     = r'<\s*?div[^>]*?class\s*?=\s*?["\']checkbox_compartilhar["\'].*?>.*?<\s*?input[^>]*?name\s*?=\s*?["\']usar["\'].*?>.*?<\s*?input[^>]*?name\s*?=\s*?["\']user["\'][^>]*?value\s*?=\s*?["\'](.*?)\s*?["\']' + + +    def loadAccountInfo(self, user, req): +        validuntil  = None +        trafficleft = None +        premium     = False + +        html = req.load("http://megarapido.net/gerador", decode=True) + +        validuntil = re.search(self.VALID_UNTIL_PATTERN, html) +        if validuntil: +            #hier weitermachen!!! (mÃŒssen umbedingt die zeit richtig machen damit! (sollte aber möglich)) +            validuntil  = time.time() + int(validuntil.group(1)) * 24 * 3600 + int(validuntil.group(2)) * 3600 + int(validuntil.group(3)) * 60 + int(validuntil.group(4)) +            trafficleft = -1 +            premium     = True + +        return {'validuntil' : validuntil, +                'trafficleft': trafficleft, +                'premium'    : premium} + + +    def login(self, user, data, req): +        req.load("http://megarapido.net/login") +        req.load("http://megarapido.net/painel_user/ajax/logar.php", +                 post={'login': user, 'senha': data['password']}, +                 decode=True) + +        html = req.load("http://megarapido.net/gerador") + +        if "sair" not in html.lower(): +            self.wrongPassword() +        else: +            m = re.search(self.USER_ID_PATTERN, html) +            if m: +                data['uid'] = m.group(1) +            else: +                self.fail("Couldn't find the user ID") diff --git a/pyload/plugin/account/MegasharesCom.py b/pyload/plugin/account/MegasharesCom.py new file mode 100644 index 000000000..7c5d0d357 --- /dev/null +++ b/pyload/plugin/account/MegasharesCom.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class MegasharesCom(Account): +    __name__    = "MegasharesCom" +    __type__    = "account" +    __version__ = "0.03" + +    __description__ = """Megashares.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    VALID_UNTIL_PATTERN = r'<p class="premium_info_box">Period Ends: (\w{3} \d{1,2}, \d{4})</p>' + + +    def loadAccountInfo(self, user, req): +        #self.relogin(user) +        html = req.load("http://d01.megashares.com/myms.php", decode=True) + +        premium = False if '>Premium Upgrade<' in html else True + +        validuntil = trafficleft = -1 +        try: +            timestr = re.search(self.VALID_UNTIL_PATTERN, html).group(1) +            self.logDebug(timestr) +            validuntil = time.mktime(time.strptime(timestr, "%b %d, %Y")) +        except Exception, e: +            self.logError(e) + +        return {"validuntil": validuntil, "trafficleft": -1, "premium": premium} + + +    def login(self, user, data, req): +        html = req.load('http://d01.megashares.com/myms_login.php', +                        post={"httpref"       : "", +                              "myms_login"    : "Login", +                              "mymslogin_name": user, +                              "mymspassword"  : data['password']}, +                        decode=True) + +        if not '<span class="b ml">%s</span>' % user in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/MovReelCom.py b/pyload/plugin/account/MovReelCom.py new file mode 100644 index 000000000..4d2855de1 --- /dev/null +++ b/pyload/plugin/account/MovReelCom.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class MovReelCom(XFSAccount): +    __name__    = "MovReelCom" +    __type__    = "account" +    __version__ = "0.03" + +    __description__ = """Movreel.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")] + + +    login_timeout = 60 +    info_threshold = 30 + +    HOSTER_DOMAIN = "movreel.com" diff --git a/pyload/plugin/account/MultihostersCom.py b/pyload/plugin/account/MultihostersCom.py new file mode 100644 index 000000000..3f5d90c4e --- /dev/null +++ b/pyload/plugin/account/MultihostersCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.account.ZeveraCom import ZeveraCom + + +class MultihostersCom(ZeveraCom): +    __name__    = "MultihostersCom" +    __type__    = "account" +    __version__ = "0.03" + +    __description__ = """Multihosters.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("tjeh", "tjeh@gmx.net")] + + +    HOSTER_DOMAIN = "multihosters.com" diff --git a/pyload/plugin/account/MultishareCz.py b/pyload/plugin/account/MultishareCz.py new file mode 100644 index 000000000..169372aac --- /dev/null +++ b/pyload/plugin/account/MultishareCz.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Account import Account + + +class MultishareCz(Account): +    __name__    = "MultishareCz" +    __type__    = "account" +    __version__ = "0.05" + +    __description__ = """Multishare.cz account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    TRAFFIC_LEFT_PATTERN = r'<span class="profil-zvyrazneni">Kredit:</span>\s*<strong>(?P<S>[\d.,]+) (?P<U>[\w^_]+)</strong>' +    ACCOUNT_INFO_PATTERN = r'<input type="hidden" id="(u_ID|u_hash)" name=".+?" value="(.+?)">' + + +    def loadAccountInfo(self, user, req): +        #self.relogin(user) +        html = req.load("http://www.multishare.cz/profil/", decode=True) + +        m = re.search(self.TRAFFIC_LEFT_PATTERN, html) +        trafficleft = self.parseTraffic(m.group('S') + m.group('U')) if m else 0 +        self.premium = True if trafficleft else False + +        html = req.load("http://www.multishare.cz/", decode=True) +        mms_info = dict(re.findall(self.ACCOUNT_INFO_PATTERN, html)) + +        return dict(mms_info, **{"validuntil": -1, "trafficleft": trafficleft}) + + +    def login(self, user, data, req): +        html = req.load('http://www.multishare.cz/html/prihlaseni_process.php', +                        post={"akce" : "PÅihlásit", +                              "heslo": data['password'], +                              "jmeno": user}, +                        decode=True) + +        if '<div class="akce-chyba akce">' in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/MyfastfileCom.py b/pyload/plugin/account/MyfastfileCom.py new file mode 100644 index 000000000..350d77c57 --- /dev/null +++ b/pyload/plugin/account/MyfastfileCom.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- + +import time + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class MyfastfileCom(Account): +    __name__    = "MyfastfileCom" +    __type__    = "account" +    __version__ = "0.04" + +    __description__ = """Myfastfile.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    def loadAccountInfo(self, user, req): +        if 'days_left' in self.json_data: +            validuntil = time.time() + self.json_data['days_left'] * 24 * 60 * 60 +            return {"premium": True, "validuntil": validuntil, "trafficleft": -1} +        else: +            self.logError(_("Unable to get account information")) + + +    def login(self, user, data, req): +        # Password to use is the API-Password written in http://myfastfile.com/myaccount +        html = req.load("http://myfastfile.com/api.php", +                        get={"user": user, "pass": data['password']}) + +        self.logDebug("JSON data: " + html) + +        self.json_data = json_loads(html) +        if self.json_data['status'] != 'ok': +            self.logError(_('Invalid login. The password to use is the API-Password you find in your "My Account" page')) +            self.wrongPassword() diff --git a/pyload/plugin/account/NetloadIn.py b/pyload/plugin/account/NetloadIn.py new file mode 100644 index 000000000..bec4690a8 --- /dev/null +++ b/pyload/plugin/account/NetloadIn.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class NetloadIn(Account): +    __name__    = "NetloadIn" +    __type__    = "account" +    __version__ = "0.24" + +    __description__ = """Netload.in account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    def api_response(self, id, password, req): +        return req.load("http://api.netload.in/user_info.php", +                        get={'auth'         : "BVm96BWDSoB4WkfbEhn42HgnjIe1ilMt", +                             'user_id'      : id, +                             'user_password': password}).strip() + + +    def loadAccountInfo(self, user, req): +        validuntil  = None +        trafficleft = -1 +        premium     = False + +        html = self.api_response(user, self.getAccountData(user)['password'], req) + +        if html == "-1": +            premium = True + +        elif html == "0": +            validuntil = -1 + +        else: +            try: +                validuntil = time.mktime(time.strptime(html, "%Y-%m-%d %H:%M")) + +            except Exception, e: +                self.logError(e) + +            else: +                self.logDebug("Valid until: %s" % validuntil) + +                if validuntil > time.mktime(time.gmtime()): +                    premium = True +                else: +                    validuntil = -1 + +        return {'validuntil' : validuntil, +                'trafficleft': trafficleft, +                'premium'    : premium} + + +    def login(self, user, data, req): +        html = self.api_response(user, data['password'], req) + +        if not html or re.search(r'disallowed_agent|unknown_auth|login_failed', html): +            self.wrongPassword() diff --git a/pyload/plugin/account/NoPremiumPl.py b/pyload/plugin/account/NoPremiumPl.py new file mode 100644 index 000000000..49f20b1f2 --- /dev/null +++ b/pyload/plugin/account/NoPremiumPl.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- + +import datetime +import hashlib +import time + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class NoPremiumPl(Account): +    __name__ = "NoPremiumPl" +    __version__ = "0.01" +    __type__ = "account" +    __description__ = "NoPremium.pl account plugin" +    __license__ = "GPLv3" +    __authors__ = [("goddie", "dev@nopremium.pl")] + +    _api_url = "http://crypt.nopremium.pl" + +    _api_query = { +        "site": "nopremium", +        "username": "", +        "password": "", +        "output": "json", +        "loc": "1", +        "info": "1" +    } + +    _req = None +    _usr = None +    _pwd = None + +    def loadAccountInfo(self, name, req): +        self._req = req +        try: +            result = json_loads(self.runAuthQuery()) +        except Exception: +            # todo: return or let it be thrown? +            return + +        premium = False +        valid_untill = -1 + +        if "expire" in result.keys() and result["expire"]: +            premium = True +            valid_untill = time.mktime(datetime.datetime.fromtimestamp(int(result["expire"])).timetuple()) +        traffic_left = result["balance"] * 1024 + +        return ({ +                    "validuntil": valid_untill, +                    "trafficleft": traffic_left, +                    "premium": premium +                }) + +    def login(self, user, data, req): +        self._usr = user +        self._pwd = hashlib.sha1(hashlib.md5(data["password"]).hexdigest()).hexdigest() +        self._req = req + +        try: +            response = json_loads(self.runAuthQuery()) +        except Exception: +            self.wrongPassword() + +        if "errno" in response.keys(): +            self.wrongPassword() +        data['usr'] = self._usr +        data['pwd'] = self._pwd + +    def createAuthQuery(self): +        query = self._api_query +        query["username"] = self._usr +        query["password"] = self._pwd + +        return query + +    def runAuthQuery(self): +        data = self._req.load(self._api_url, post=self.createAuthQuery()) + +        return data
\ No newline at end of file diff --git a/pyload/plugin/account/NosuploadCom.py b/pyload/plugin/account/NosuploadCom.py new file mode 100644 index 000000000..7fc8b49de --- /dev/null +++ b/pyload/plugin/account/NosuploadCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class NosuploadCom(XFSAccount): +    __name__    = "NosuploadCom" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """Nosupload.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "nosupload.com" diff --git a/pyload/plugin/account/NovafileCom.py b/pyload/plugin/account/NovafileCom.py new file mode 100644 index 000000000..71a7dc2dc --- /dev/null +++ b/pyload/plugin/account/NovafileCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class NovafileCom(XFSAccount): +    __name__    = "NovafileCom" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """Novafile.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "novafile.com" diff --git a/pyload/plugin/account/NowVideoSx.py b/pyload/plugin/account/NowVideoSx.py new file mode 100644 index 000000000..3c63149ae --- /dev/null +++ b/pyload/plugin/account/NowVideoSx.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class NowVideoSx(Account): +    __name__    = "NowVideoSx" +    __type__    = "account" +    __version__ = "0.03" + +    __description__ = """NowVideo.at account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    VALID_UNTIL_PATTERN = r'>Your premium membership expires on: (.+?)<' + + +    def loadAccountInfo(self, user, req): +        validuntil  = None +        trafficleft = -1 +        premium     = None + +        html = req.load("http://www.nowvideo.sx/premium.php") + +        m = re.search(self.VALID_UNTIL_PATTERN, html) +        if m: +            expiredate = m.group(1).strip() +            self.logDebug("Expire date: " + expiredate) + +            try: +                validuntil = time.mktime(time.strptime(expiredate, "%Y-%b-%d")) + +            except Exception, e: +                self.logError(e) + +            else: +                if validuntil > time.mktime(time.gmtime()): +                    premium = True +                else: +                    premium = False +                    validuntil = -1 + +        return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium} + + +    def login(self, user, data, req): +        html = req.load("http://www.nowvideo.sx/login.php", +                        post={'user': user, 'pass': data['password']}, +                        decode=True) + +        if re.search(r'>Log In<', html): +            self.wrongPassword() diff --git a/pyload/plugin/account/OboomCom.py b/pyload/plugin/account/OboomCom.py new file mode 100644 index 000000000..163692399 --- /dev/null +++ b/pyload/plugin/account/OboomCom.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- + +import time + +try: +    from beaker.crypto.pbkdf2 import PBKDF2 + +except ImportError: +    from beaker.crypto.pbkdf2 import pbkdf2 +    from binascii import b2a_hex +    class PBKDF2(object): +        def __init__(self, passphrase, salt, iterations=1000): +            self.passphrase = passphrase +            self.salt = salt +            self.iterations = iterations + +        def hexread(self, octets): +            return b2a_hex(pbkdf2(self.passphrase, self.salt, self.iterations, octets)) + +from pyload.utils import json_loads +from pyload.plugin.Account import Account + + +class OboomCom(Account): +    __name__    = "OboomCom" +    __type__    = "account" +    __version__ = "0.24" + +    __description__ = """Oboom.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stanley", "stanley.foerster@gmail.com")] + + +    def loadAccountData(self, user, req): +        passwd = self.getAccountData(user)['password'] +        salt   = passwd[::-1] +        pbkdf2 = PBKDF2(passwd, salt, 1000).hexread(16) + +        result = json_loads(req.load("https://www.oboom.com/1/login", get={"auth": user, "pass": pbkdf2})) + +        if not result[0] == 200: +            self.logWarning(_("Failed to log in: %s") % result[1]) +            self.wrongPassword() + +        return result[1] + + +    def loadAccountInfo(self, name, req): +        accountData = self.loadAccountData(name, req) + +        userData = accountData['user'] + +        if userData['premium'] == "null": +            premium = False +        else: +            premium = True + +        if userData['premium_unix'] == "null": +            validUntil = -1 +        else: +            validUntil = float(userData['premium_unix']) + +        traffic = userData['traffic'] + +        trafficLeft = traffic['current'] / 1024  #@TODO: Remove `/ 1024` in 0.4.10 +        maxTraffic = traffic['max'] / 1024  #@TODO: Remove `/ 1024` in 0.4.10 + +        session = accountData['session'] + +        return {'premium'    : premium, +                'validuntil' : validUntil, +                'trafficleft': trafficLeft, +                'maxtraffic' : maxTraffic, +                'session'    : session} + + +    def login(self, user, data, req): +        self.loadAccountData(user, req) diff --git a/pyload/plugin/account/OneFichierCom.py b/pyload/plugin/account/OneFichierCom.py new file mode 100644 index 000000000..0c37654ca --- /dev/null +++ b/pyload/plugin/account/OneFichierCom.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pycurl import REFERER + +from pyload.plugin.Account import Account + + +class OneFichierCom(Account): +    __name__    = "OneFichierCom" +    __type__    = "account" +    __version__ = "0.12" + +    __description__ = """1fichier.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Elrick69", "elrick69[AT]rocketmail[DOT]com"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    VALID_UNTIL_PATTERN = r'Your Premium Status will end the (\d+/\d+/\d+)' + + +    def loadAccountInfo(self, user, req): +        validuntil = None +        trafficleft = -1 +        premium = None + +        html = req.load("https://1fichier.com/console/abo.pl") + +        m = re.search(self.VALID_UNTIL_PATTERN, html) +        if m: +            expiredate = m.group(1) +            self.logDebug("Expire date: " + expiredate) + +            try: +                validuntil = time.mktime(time.strptime(expiredate, "%d/%m/%Y")) +            except Exception, e: +                self.logError(e) +            else: +                premium = True + +        return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium or False} + + +    def login(self, user, data, req): +        req.http.c.setopt(REFERER, "https://1fichier.com/login.pl?lg=en") + +        html = req.load("https://1fichier.com/login.pl?lg=en", +                        post={'mail'   : user, +                              'pass'   : data['password'], +                              'It'     : "on", +                              'purge'  : "off", +                              'valider': "Send"}, +                        decode=True) + +        if '>Invalid email address' in html or '>Invalid password' in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/OverLoadMe.py b/pyload/plugin/account/OverLoadMe.py new file mode 100644 index 000000000..8677ecf47 --- /dev/null +++ b/pyload/plugin/account/OverLoadMe.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class OverLoadMe(Account): +    __name__    = "OverLoadMe" +    __type__    = "account" +    __version__ = "0.04" + +    __description__ = """Over-Load.me account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("marley", "marley@over-load.me")] + + +    def loadAccountInfo(self, user, req): +        https = "https" if self.getConfig('ssl') else "http" +        data  = self.getAccountData(user) +        html  = req.load(https + "://api.over-load.me/account.php", +                         get={'user': user, +                              'auth': data['password']}).strip() + +        data = json_loads(html) +        self.logDebug(data) + +        # Check for premium +        if data['membership'] == "Free": +            return {'premium': False, 'validuntil': None, 'trafficleft': None} +        else: +            return {'premium': True, 'validuntil': data['expirationunix'], 'trafficleft': -1} + + +    def login(self, user, data, req): +        https    = "https" if self.getConfig('ssl') else "http" +        jsondata = req.load(https + "://api.over-load.me/account.php", +                            get={'user': user, +                                 'auth': data['password']}).strip() + +        data = json_loads(jsondata) + +        if data['err'] == 1: +            self.wrongPassword() diff --git a/pyload/plugin/account/PremiumTo.py b/pyload/plugin/account/PremiumTo.py new file mode 100644 index 000000000..04bbc10d5 --- /dev/null +++ b/pyload/plugin/account/PremiumTo.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account + + +class PremiumTo(Account): +    __name__    = "PremiumTo" +    __type__    = "account" +    __version__ = "0.08" + +    __description__ = """Premium.to account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org"), +                       ("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + + +    def loadAccountInfo(self, user, req): +        traffic = req.load("http://premium.to/api/straffic.php", +                           get={'username': self.username, 'password': self.password}) + +        if "wrong username" not in traffic: +            trafficleft = sum(map(float, traffic.split(';'))) / 1024  #@TODO: Remove `/ 1024` in 0.4.10 +            return {'premium': True, 'trafficleft': trafficleft, 'validuntil': -1} +        else: +            return {'premium': False, 'trafficleft': None, 'validuntil': None} + + +    def login(self, user, data, req): +        self.username = user +        self.password = data['password'] +        authcode = req.load("http://premium.to/api/getauthcode.php", +                            get={'username': user, 'password': self.password}, +                            decode=True) + +        if "wrong username" in authcode: +            self.wrongPassword() diff --git a/pyload/plugin/account/PremiumizeMe.py b/pyload/plugin/account/PremiumizeMe.py new file mode 100644 index 000000000..3cd15ce23 --- /dev/null +++ b/pyload/plugin/account/PremiumizeMe.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account + +from pyload.utils import json_loads + + +class PremiumizeMe(Account): +    __name__    = "PremiumizeMe" +    __type__    = "account" +    __version__ = "0.13" + +    __description__ = """Premiumize.me account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Florian Franzen", "FlorianFranzen@gmail.com")] + + +    def loadAccountInfo(self, user, req): +        # Get user data from premiumize.me +        status = self.getAccountStatus(user, req) +        self.logDebug(status) + +        # Parse account info +        account_info = {"validuntil": float(status['result']['expires']), +                        "trafficleft": max(0, status['result']['trafficleft_bytes'] / 1024)}  #@TODO: Remove `/ 1024` in 0.4.10 + +        if status['result']['type'] == 'free': +            account_info['premium'] = False + +        return account_info + + +    def login(self, user, data, req): +        # Get user data from premiumize.me +        status = self.getAccountStatus(user, req) + +        # Check if user and password are valid +        if status['status'] != 200: +            self.wrongPassword() + + +    def getAccountStatus(self, user, req): +        # Use premiumize.me API v1 (see https://secure.premiumize.me/?show=api) +        # to retrieve account info and return the parsed json answer +        answer = req.load("https://api.premiumize.me/pm-api/v1.php", +                           get={'method'       : "accountstatus", +                                'params[login]': user, +                                'params[pass]' : self.getAccountData(user)['password']}) +        return json_loads(answer) diff --git a/pyload/plugin/account/PutdriveCom.py b/pyload/plugin/account/PutdriveCom.py new file mode 100644 index 000000000..b30fb6565 --- /dev/null +++ b/pyload/plugin/account/PutdriveCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.account.ZeveraCom import ZeveraCom + + +class PutdriveCom(ZeveraCom): +    __name__    = "PutdriveCom" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """Putdrive.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "putdrive.com" diff --git a/pyload/plugin/account/QuickshareCz.py b/pyload/plugin/account/QuickshareCz.py new file mode 100644 index 000000000..2bcde1c9d --- /dev/null +++ b/pyload/plugin/account/QuickshareCz.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Account import Account + + +class QuickshareCz(Account): +    __name__    = "QuickshareCz" +    __type__    = "account" +    __version__ = "0.03" + +    __description__ = """Quickshare.cz account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    TRAFFIC_LEFT_PATTERN = r'Stav kreditu: <strong>(.+?)</strong>' + + +    def loadAccountInfo(self, user, req): +        html = req.load("http://www.quickshare.cz/premium", decode=True) + +        m = re.search(self.TRAFFIC_LEFT_PATTERN, html) +        if m: +            trafficleft = self.parseTraffic(m.group(1)) +            premium = True if trafficleft else False +        else: +            trafficleft = None +            premium = False + +        return {"validuntil": -1, "trafficleft": trafficleft, "premium": premium} + + +    def login(self, user, data, req): +        html = req.load('http://www.quickshare.cz/html/prihlaseni_process.php', +                        post={"akce": u'PÅihlásit', +                              "heslo": data['password'], +                              "jmeno": user}, +                        decode=True) + +        if u'>TakovÜ uÅŸivatel neexistuje.<' in html or u'>Å patné heslo.<' in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/RPNetBiz.py b/pyload/plugin/account/RPNetBiz.py new file mode 100644 index 000000000..e0b35b68c --- /dev/null +++ b/pyload/plugin/account/RPNetBiz.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class RPNetBiz(Account): +    __name__    = "RPNetBiz" +    __type__    = "account" +    __version__ = "0.12" + +    __description__ = """RPNet.biz account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Dman", "dmanugm@gmail.com")] + + +    def loadAccountInfo(self, user, req): +        # Get account information from rpnet.biz +        res = self.getAccountStatus(user, req) +        try: +            if res['accountInfo']['isPremium']: +                # Parse account info. Change the trafficleft later to support per host info. +                account_info = {"validuntil": float(res['accountInfo']['premiumExpiry']), +                                "trafficleft": -1, "premium": True} +            else: +                account_info = {"validuntil": None, "trafficleft": None, "premium": False} + +        except KeyError: +            #handle wrong password exception +            account_info = {"validuntil": None, "trafficleft": None, "premium": False} + +        return account_info + + +    def login(self, user, data, req): +        # Get account information from rpnet.biz +        res = self.getAccountStatus(user, req) + +        # If we have an error in the res, we have wrong login information +        if 'error' in res: +            self.wrongPassword() + + +    def getAccountStatus(self, user, req): +        # Using the rpnet API, check if valid premium account +        res = req.load("https://premium.rpnet.biz/client_api.php", +                            get={"username": user, "password": self.getAccountData(user)['password'], +                                 "action": "showAccountInformation"}) +        self.logDebug("JSON data: %s" % res) + +        return json_loads(res) diff --git a/pyload/plugin/account/RapideoPl.py b/pyload/plugin/account/RapideoPl.py new file mode 100644 index 000000000..f14e222c3 --- /dev/null +++ b/pyload/plugin/account/RapideoPl.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- + +import datetime +import hashlib +import time + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class RapideoPl(Account): +    __name__ = "RapideoPl" +    __version__ = "0.01" +    __type__ = "account" +    __description__ = "Rapideo.pl account plugin" +    __license__ = "GPLv3" +    __authors__ = [("goddie", "dev@rapideo.pl")] + +    _api_url = "http://enc.rapideo.pl" + +    _api_query = { +        "site": "newrd", +        "username": "", +        "password": "", +        "output": "json", +        "loc": "1", +        "info": "1" +    } + +    _req = None +    _usr = None +    _pwd = None + +    def loadAccountInfo(self, name, req): +        self._req = req +        try: +            result = json_loads(self.runAuthQuery()) +        except Exception: +            # todo: return or let it be thrown? +            return + +        premium = False +        valid_untill = -1 +        if "expire" in result.keys() and result["expire"]: +            premium = True +            valid_untill = time.mktime(datetime.datetime.fromtimestamp(int(result["expire"])).timetuple()) + +        traffic_left = result["balance"] + +        return ({ +                    "validuntil": valid_untill, +                    "trafficleft": traffic_left, +                    "premium": premium +                }) + +    def login(self, user, data, req): +        self._usr = user +        self._pwd = hashlib.md5(data["password"]).hexdigest() +        self._req = req +        try: +            response = json_loads(self.runAuthQuery()) +        except Exception: +            self.wrongPassword() + +        if "errno" in response.keys(): +            self.wrongPassword() +        data['usr'] = self._usr +        data['pwd'] = self._pwd + +    def createAuthQuery(self): +        query = self._api_query +        query["username"] = self._usr +        query["password"] = self._pwd + +        return query + +    def runAuthQuery(self): +        data = self._req.load(self._api_url, post=self.createAuthQuery()) + +        return data
\ No newline at end of file diff --git a/pyload/plugin/account/RapidfileshareNet.py b/pyload/plugin/account/RapidfileshareNet.py new file mode 100644 index 000000000..ec0bf8db4 --- /dev/null +++ b/pyload/plugin/account/RapidfileshareNet.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class RapidfileshareNet(XFSAccount): +    __name__    = "RapidfileshareNet" +    __type__    = "account" +    __version__ = "0.05" + +    __description__ = """Rapidfileshare.net account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("guidobelix", "guidobelix@hotmail.it")] + + +    HOSTER_DOMAIN = "rapidfileshare.net" + +    TRAFFIC_LEFT_PATTERN = r'>Traffic available today:</TD><TD><label for="name">\s*(?P<S>[\d.,]+)\s*(?:(?P<U>[\w^_]+))?' diff --git a/pyload/plugin/account/RapidgatorNet.py b/pyload/plugin/account/RapidgatorNet.py new file mode 100644 index 000000000..7643f07d2 --- /dev/null +++ b/pyload/plugin/account/RapidgatorNet.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class RapidgatorNet(Account): +    __name__    = "RapidgatorNet" +    __type__    = "account" +    __version__ = "0.09" + +    __description__ = """Rapidgator.net account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    API_URL = "http://rapidgator.net/api/user" + + +    def loadAccountInfo(self, user, req): +        validuntil  = None +        trafficleft = None +        premium     = False +        sid         = None + +        try: +            sid = self.getAccountData(user).get('sid') +            assert sid + +            html = req.load("%s/info" % self.API_URL, get={'sid': sid}) + +            self.logDebug("API:USERINFO", html) + +            json = json_loads(html) + +            if json['response_status'] == 200: +                if "reset_in" in json['response']: +                    self.scheduleRefresh(user, json['response']['reset_in']) + +                validuntil  = json['response']['expire_date'] +                trafficleft = float(json['response']['traffic_left']) / 1024  #@TODO: Remove `/ 1024` in 0.4.10 +                premium     = True +            else: +                self.logError(json['response_details']) + +        except Exception, e: +            self.logError(e) + +        return {'validuntil' : validuntil, +                'trafficleft': trafficleft, +                'premium'    : premium, +                'sid'        : sid} + + +    def login(self, user, data, req): +        try: +            html = req.load('%s/login' % self.API_URL, post={"username": user, "password": data['password']}) + +            self.logDebug("API:LOGIN", html) + +            json = json_loads(html) + +            if json['response_status'] == 200: +                data['sid'] = str(json['response']['session_id']) +                return +            else: +                self.logError(json['response_details']) + +        except Exception, e: +            self.logError(e) + +        self.wrongPassword() diff --git a/pyload/plugin/account/RapiduNet.py b/pyload/plugin/account/RapiduNet.py new file mode 100644 index 000000000..b03bde6e0 --- /dev/null +++ b/pyload/plugin/account/RapiduNet.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class RapiduNet(Account): +    __name__    = "RapiduNet" +    __type__    = "account" +    __version__ = "0.05" + +    __description__ = """Rapidu.net account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("prOq", None), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    PREMIUM_PATTERN = r'>Account: <b>Premium' + +    VALID_UNTIL_PATTERN = r'>Account: <b>\w+ \((\d+)' + +    TRAFFIC_LEFT_PATTERN = r'class="tipsyS"><b>(.+?)<' + + +    def loadAccountInfo(self, user, req): +        validuntil  = None +        trafficleft = -1 +        premium     = False + +        html = req.load("https://rapidu.net/", decode=True) + +        if re.search(self.PREMIUM_PATTERN, html): +            premium = True + +        m = re.search(self.VALID_UNTIL_PATTERN, html) +        if m: +            validuntil = time.time() + (86400 * int(m.group(1))) + +        m = re.search(self.TRAFFIC_LEFT_PATTERN, html) +        if m: +            trafficleft = self.parseTraffic(m.group(1)) + +        return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium} + + +    def login(self, user, data, req): +        req.load("https://rapidu.net/ajax.php", +                 get={'a': "getChangeLang"}, +                 post={'_go' : "", +                       'lang': "en"}) + +        json = json_loads(req.load("https://rapidu.net/ajax.php", +                                   get={'a': "getUserLogin"}, +                                   post={'_go'     : "", +                                         'login'   : user, +                                         'pass'    : data['password'], +                                         'remember': "1"})) + +        self.logDebug(json) + +        if not json['message'] == "success": +            self.wrongPassword() diff --git a/pyload/plugin/account/RarefileNet.py b/pyload/plugin/account/RarefileNet.py new file mode 100644 index 000000000..1dc93681c --- /dev/null +++ b/pyload/plugin/account/RarefileNet.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class RarefileNet(XFSAccount): +    __name__    = "RarefileNet" +    __type__    = "account" +    __version__ = "0.04" + +    __description__ = """RareFile.net account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    HOSTER_DOMAIN = "rarefile.net" diff --git a/pyload/plugin/account/RealdebridCom.py b/pyload/plugin/account/RealdebridCom.py new file mode 100644 index 000000000..07ff70496 --- /dev/null +++ b/pyload/plugin/account/RealdebridCom.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +import xml.dom.minidom as dom + +from pyload.plugin.Account import Account + + +class RealdebridCom(Account): +    __name__    = "RealdebridCom" +    __type__    = "account" +    __version__ = "0.45" + +    __description__ = """Real-Debrid.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Devirex Hazzard", "naibaf_11@yahoo.de")] + + +    def loadAccountInfo(self, user, req): +        if self.pin_code: +            return {"premium": False} +        html = req.load("https://real-debrid.com/api/account.php") +        xml = dom.parseString(html) +        account_info = {"validuntil": float(xml.getElementsByTagName("expiration")[0].childNodes[0].nodeValue), +                        "trafficleft": -1} + +        return account_info + + +    def login(self, user, data, req): +        self.pin_code = False +        html = req.load("https://real-debrid.com/ajax/login.php", +                        get={"user": user, "pass": data['password']}, +                        decode=True) + +        if "Your login informations are incorrect" in html: +            self.wrongPassword() + +        elif "PIN Code required" in html: +            self.logWarning(_("PIN code required. Please login to https://real-debrid.com using the PIN or disable the double authentication in your control panel on https://real-debrid.com")) +            self.pin_code = True diff --git a/pyload/plugin/account/RehostTo.py b/pyload/plugin/account/RehostTo.py new file mode 100644 index 000000000..d62e1918a --- /dev/null +++ b/pyload/plugin/account/RehostTo.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account + + +class RehostTo(Account): +    __name__    = "RehostTo" +    __type__    = "account" +    __version__ = "0.16" + +    __description__ = """Rehost.to account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org")] + + +    def loadAccountInfo(self, user, req): +        premium     = False +        trafficleft = None +        validuntil  = -1 +        session     = "" + +        html = req.load("http://rehost.to/api.php", +                        get={'cmd' : "login", 'user': user, +                             'pass': self.getAccountData(user)['password']}) +        try: +            session = html.split(",")[1].split("=")[1] + +            html = req.load("http://rehost.to/api.php", +                            get={'cmd': "get_premium_credits", 'long_ses': session}) + +            if html.strip() == "0,0" or "ERROR" in html: +                self.logDebug(html) +            else: +                traffic, valid = html.split(",") + +                premium     = True +                trafficleft = self.parseTraffic(traffic + "MB") +                validuntil  = float(valid) + +        finally: +            return {'premium'    : premium, +                    'trafficleft': trafficleft, +                    'validuntil' : validuntil, +                    'session'    : session} + + +    def login(self, user, data, req): +        html = req.load("http://rehost.to/api.php", +                        get={'cmd': "login", 'user': user, 'pass': data['password']}, +                        decode=True) + +        if "ERROR" in html: +            self.logDebug(html) +            self.wrongPassword() diff --git a/pyload/plugin/account/RyushareCom.py b/pyload/plugin/account/RyushareCom.py new file mode 100644 index 000000000..466d971f6 --- /dev/null +++ b/pyload/plugin/account/RyushareCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class RyushareCom(XFSAccount): +    __name__    = "RyushareCom" +    __type__    = "account" +    __version__ = "0.06" + +    __description__ = """Ryushare.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "ryushare.com" diff --git a/pyload/plugin/account/SafesharingEu.py b/pyload/plugin/account/SafesharingEu.py new file mode 100644 index 000000000..f5cbf050e --- /dev/null +++ b/pyload/plugin/account/SafesharingEu.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class SafesharingEu(XFSAccount): +    __name__    = "SafesharingEu" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """Safesharing.eu account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("guidobelix", "guidobelix@hotmail.it")] + + +    HOSTER_DOMAIN = "safesharing.eu" diff --git a/pyload/plugin/account/SecureUploadEu.py b/pyload/plugin/account/SecureUploadEu.py new file mode 100644 index 000000000..bb47bcba3 --- /dev/null +++ b/pyload/plugin/account/SecureUploadEu.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class SecureUploadEu(XFSAccount): +    __name__    = "SecureUploadEu" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """SecureUpload.eu account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "secureupload.eu" diff --git a/pyload/plugin/account/SendmywayCom.py b/pyload/plugin/account/SendmywayCom.py new file mode 100644 index 000000000..d64658de3 --- /dev/null +++ b/pyload/plugin/account/SendmywayCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class SendmywayCom(XFSAccount): +    __name__    = "SendmywayCom" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """Sendmyway.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "sendmyway.com" diff --git a/pyload/plugin/account/ShareonlineBiz.py b/pyload/plugin/account/ShareonlineBiz.py new file mode 100644 index 000000000..28bc3b9bc --- /dev/null +++ b/pyload/plugin/account/ShareonlineBiz.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Account import Account + + +class ShareonlineBiz(Account): +    __name__    = "ShareonlineBiz" +    __type__    = "account" +    __version__ = "0.31" + +    __description__ = """Share-online.biz account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    def api_response(self, user, req): +        return req.load("http://api.share-online.biz/cgi-bin", +                        get={'q'       : "userdetails", +                             'aux'     : "traffic", +                             'username': user, +                             'password': self.getAccountData(user)['password']}) + + +    def loadAccountInfo(self, user, req): +        premium     = False +        validuntil  = None +        trafficleft = -1 +        maxtraffic  = 100 * 1024 * 1024 * 1024  #: 100 GB + +        api = {} +        for line in self.api_response(user, req).splitlines(): +            if "=" in line: +                key, value = line.split("=") +                api[key] = value + +        self.logDebug(api) + +        if api['a'].lower() != "not_available": +            req.cj.setCookie("share-online.biz", 'a', api['a']) + +            premium = api['group'] in ("Premium", "PrePaid") + +            validuntil = float(api['expire_date']) + +            traffic     = float(api['traffic_1d'].split(";")[0]) +            maxtraffic  = max(maxtraffic, traffic) +            trafficleft = maxtraffic - traffic + +        maxtraffic  /= 1024  #@TODO: Remove `/ 1024` in 0.4.10 +        trafficleft /= 1024  #@TODO: Remove `/ 1024` in 0.4.10 + +        return {'premium'    : premium, +                'validuntil' : validuntil, +                'trafficleft': trafficleft, +                'maxtraffic' : maxtraffic} + + +    def login(self, user, data, req): +        html = self.api_response(user, req) +        err  = re.search(r'\*\*(.+?)\*\*', html) +        if err: +            self.logError(err.group(1)) +            self.wrongPassword() diff --git a/pyload/plugin/account/SimplyPremiumCom.py b/pyload/plugin/account/SimplyPremiumCom.py new file mode 100644 index 000000000..298ad8d59 --- /dev/null +++ b/pyload/plugin/account/SimplyPremiumCom.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +from pyload.utils import json_loads +from pyload.plugin.Account import Account + + +class SimplyPremiumCom(Account): +    __name__    = "SimplyPremiumCom" +    __type__    = "account" +    __version__ = "0.05" + +    __description__ = """Simply-Premium.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("EvolutionClip", "evolutionclip@live.de")] + + +    def loadAccountInfo(self, user, req): +        premium     = False +        validuntil  = -1 +        trafficleft = None + +        json_data = req.load('http://www.simply-premium.com/api/user.php?format=json') + +        self.logDebug("JSON data: %s" % json_data) + +        json_data = json_loads(json_data) + +        if 'vip' in json_data['result'] and json_data['result']['vip']: +            premium = True + +        if 'timeend' in json_data['result'] and json_data['result']['timeend']: +            validuntil = float(json_data['result']['timeend']) + +        if 'remain_traffic' in json_data['result'] and json_data['result']['remain_traffic']: +            trafficleft = float(json_data['result']['remain_traffic']) / 1024  #@TODO: Remove `/ 1024` in 0.4.10 + +        return {"premium": premium, "validuntil": validuntil, "trafficleft": trafficleft} + + +    def login(self, user, data, req): +        req.cj.setCookie("simply-premium.com", "lang", "EN") + +        html = req.load("http://www.simply-premium.com/login.php", +                        post={'key': user} if not data['password'] else {'login_name': user, 'login_pass': data['password']}, +                        decode=True) + +        if 'logout' not in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/SimplydebridCom.py b/pyload/plugin/account/SimplydebridCom.py new file mode 100644 index 000000000..1d5ba201c --- /dev/null +++ b/pyload/plugin/account/SimplydebridCom.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +import time + +from pyload.plugin.Account import Account + + +class SimplydebridCom(Account): +    __name__    = "SimplydebridCom" +    __type__    = "account" +    __version__ = "0.11" + +    __description__ = """Simply-Debrid.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Kagenoshin", "kagenoshin@gmx.ch")] + + +    def loadAccountInfo(self, user, req): +        get_data = {'login': 2, 'u': self.loginname, 'p': self.password} +        res = req.load("http://simply-debrid.com/api.php", get=get_data, decode=True) +        data = [x.strip() for x in res.split(";")] +        if str(data[0]) != "1": +            return {"premium": False} +        else: +            return {"trafficleft": -1, "validuntil": time.mktime(time.strptime(str(data[2]), "%d/%m/%Y"))} + + +    def login(self, user, data, req): +        self.loginname = user +        self.password  = data['password'] +        get_data       = {'login': 1, 'u': self.loginname, 'p': self.password} + +        res = req.load("http://simply-debrid.com/api.php", get=get_data, decode=True) +        if res != "02: loggin success": +            self.wrongPassword() diff --git a/pyload/plugin/account/SmoozedCom.py b/pyload/plugin/account/SmoozedCom.py new file mode 100644 index 000000000..dd11a35f2 --- /dev/null +++ b/pyload/plugin/account/SmoozedCom.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- + +import hashlib +import time + +try: +    from beaker.crypto.pbkdf2 import PBKDF2 + +except ImportError: +    from beaker.crypto.pbkdf2 import pbkdf2 +    from binascii import b2a_hex +    class PBKDF2(object): +        def __init__(self, passphrase, salt, iterations=1000): +            self.passphrase = passphrase +            self.salt = salt +            self.iterations = iterations + +        def hexread(self, octets): +            return b2a_hex(pbkdf2(self.passphrase, self.salt, self.iterations, octets)) + +from pyload.utils import json_loads +from pyload.plugin.Account import Account + + +class SmoozedCom(Account): +    __name__    = "SmoozedCom" +    __type__    = "account" +    __version__ = "0.04" + +    __description__ = """Smoozed.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("", "")] + + +    def loadAccountInfo(self, user, req): +        # Get user data from premiumize.me +        status = self.getAccountStatus(user, req) + +        self.logDebug(status) + +        if status['state'] != 'ok': +            info = {'validuntil' : None, +                    'trafficleft': None, +                    'premium'    : False} +        else: +            # Parse account info +            info = {'validuntil' : float(status["data"]["user"]["user_premium"]), +                    'trafficleft': max(0, status["data"]["traffic"][1] - status["data"]["traffic"][0]), +                    'session'    : status["data"]["session_key"], +                    'hosters'    : [hoster["name"] for hoster in status["data"]["hoster"]]} + +            if info['validuntil'] < time.time(): +                info['premium'] = False +            else: +                info['premium'] = True + +        return info + + +    def login(self, user, data, req): +        # Get user data from premiumize.me +        status = self.getAccountStatus(user, req) + +        # Check if user and password are valid +        if status['state'] != 'ok': +            self.wrongPassword() + + +    def getAccountStatus(self, user, req): +        password  = self.getAccountData(user)['password'] +        salt      = hashlib.sha256(password).hexdigest() +        encrypted = PBKDF2(password, salt, iterations=1000).hexread(32) + +        return json_loads(req.load("http://www2.smoozed.com/api/login", +                                   get={'auth': user, 'password': encrypted})) diff --git a/pyload/plugin/account/StahnuTo.py b/pyload/plugin/account/StahnuTo.py new file mode 100644 index 000000000..ed8df3b77 --- /dev/null +++ b/pyload/plugin/account/StahnuTo.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Account import Account + + +class StahnuTo(Account): +    __name__    = "StahnuTo" +    __type__    = "account" +    __version__ = "0.05" + +    __description__ = """StahnuTo account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    def loadAccountInfo(self, user, req): +        html = req.load("http://www.stahnu.to/") + +        m = re.search(r'>VIP: (\d+.*)<', html) +        trafficleft = self.parseTraffic(m.group(1)) if m else 0 + +        return {"premium": trafficleft > 512, "trafficleft": trafficleft, "validuntil": -1} + + +    def login(self, user, data, req): +        html = req.load("http://www.stahnu.to/login.php", +                        post={"username": user, +                              "password": data['password'], +                              "submit": "Login"}, +                        decode=True) + +        if not '<a href="logout.php">' in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/StreamcloudEu.py b/pyload/plugin/account/StreamcloudEu.py new file mode 100644 index 000000000..3ac74fbd0 --- /dev/null +++ b/pyload/plugin/account/StreamcloudEu.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class StreamcloudEu(XFSAccount): +    __name__    = "StreamcloudEu" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """Streamcloud.eu account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "streamcloud.eu" diff --git a/pyload/plugin/account/TurbobitNet.py b/pyload/plugin/account/TurbobitNet.py new file mode 100644 index 000000000..d00f4c0e4 --- /dev/null +++ b/pyload/plugin/account/TurbobitNet.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class TurbobitNet(Account): +    __name__    = "TurbobitNet" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """TurbobitNet account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    def loadAccountInfo(self, user, req): +        html = req.load("http://turbobit.net") + +        m = re.search(r'<u>Turbo Access</u> to ([\d.]+)', html) +        if m: +            premium = True +            validuntil = time.mktime(time.strptime(m.group(1), "%d.%m.%Y")) +        else: +            premium = False +            validuntil = -1 + +        return {"premium": premium, "trafficleft": -1, "validuntil": validuntil} + + +    def login(self, user, data, req): +        req.cj.setCookie("turbobit.net", "user_lang", "en") + +        html = req.load("http://turbobit.net/user/login", +                        post={"user[login]": user, +                              "user[pass]": data['password'], +                              "user[submit]": "Login"}, +                        decode=True) + +        if not '<div class="menu-item user-name">' in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/TusfilesNet.py b/pyload/plugin/account/TusfilesNet.py new file mode 100644 index 000000000..48f70b4f3 --- /dev/null +++ b/pyload/plugin/account/TusfilesNet.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class TusfilesNet(XFSAccount): +    __name__    = "TusfilesNet" +    __type__    = "account" +    __version__ = "0.06" + +    __description__ = """Tusfile.net account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("guidobelix", "guidobelix@hotmail.it")] + + +    HOSTER_DOMAIN = "tusfiles.net" + +    VALID_UNTIL_PATTERN = r'<span class="label label-default">([^<]+)</span>' +    TRAFFIC_LEFT_PATTERN = r'<td><img src="//www\.tusfiles\.net/i/icon/meter\.png" alt=""/></td>\n<td> (?P<S>[\d.,]+)' diff --git a/pyload/plugin/account/UlozTo.py b/pyload/plugin/account/UlozTo.py new file mode 100644 index 000000000..135f4b6c6 --- /dev/null +++ b/pyload/plugin/account/UlozTo.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from pyload.plugin.Account import Account + + +class UlozTo(Account): +    __name__    = "UlozTo" +    __type__    = "account" +    __version__ = "0.10" + +    __description__ = """Uloz.to account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("pulpe", "")] + + +    TRAFFIC_LEFT_PATTERN = r'<li class="menu-kredit"><a .*?title=".+?GB = ([\d.]+) MB"' + + +    def loadAccountInfo(self, user, req): +        html = req.load("http://www.ulozto.net/", decode=True) + +        m = re.search(self.TRAFFIC_LEFT_PATTERN, html) + +        trafficleft = float(m.group(1).replace(' ', '').replace(',', '.')) * 1000 * 1.048 if m else 0 +        premium     = True if trafficleft else False + +        return {'validuntil': -1, 'trafficleft': trafficleft, 'premium': premium} + + +    def login(self, user, data, req): +        login_page = req.load('http://www.ulozto.net/?do=web-login', decode=True) +        action     = re.findall('<form action="(.+?)"', login_page)[1].replace('&', '&') +        token      = re.search('_token_" value="(.+?)"', login_page).group(1) + +        html = req.load(urljoin("http://www.ulozto.net/", action), +                        post={'_token_' : token, +                              'do'      : "loginForm-submit", +                              'login'   : u"PÅihlásit", +                              'password': data['password'], +                              'username': user, +                              'remember': "on"}, +                        decode=True) + +        if '<div class="flash error">' in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/UnrestrictLi.py b/pyload/plugin/account/UnrestrictLi.py new file mode 100644 index 000000000..d8d7789bb --- /dev/null +++ b/pyload/plugin/account/UnrestrictLi.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account +from pyload.utils import json_loads + + +class UnrestrictLi(Account): +    __name__    = "UnrestrictLi" +    __type__    = "account" +    __version__ = "0.05" + +    __description__ = """Unrestrict.li account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    def loadAccountInfo(self, user, req): +        json_data = req.load('http://unrestrict.li/api/jdownloader/user.php?format=json') +        self.logDebug("JSON data: " + json_data) +        json_data = json_loads(json_data) + +        if 'vip' in json_data['result'] and json_data['result']['vip'] == 0: +            return {"premium": False} + +        validuntil = json_data['result']['expires'] +        trafficleft = float(json_data['result']['traffic'] / 1024)  #@TODO: Remove `/ 1024` in 0.4.10 + +        return {"premium": True, "validuntil": validuntil, "trafficleft": trafficleft} + + +    def login(self, user, data, req): +        req.cj.setCookie("unrestrict.li", "lang", "EN") +        html = req.load("https://unrestrict.li/sign_in", decode=True) + +        if 'solvemedia' in html: +            self.logError(_("A Captcha is required. Go to http://unrestrict.li/sign_in and login, then retry")) +            return + +        post_data = {"username": user, "password": data['password'], +                     "remember_me": "remember", "signin": "Sign in"} +        html = req.load("https://unrestrict.li/sign_in", post=post_data, decode=True) + +        if 'sign_out' not in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/UploadableCh.py b/pyload/plugin/account/UploadableCh.py new file mode 100644 index 000000000..9406118cd --- /dev/null +++ b/pyload/plugin/account/UploadableCh.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Account import Account + + +class UploadableCh(Account): +    __name__    = "UploadableCh" +    __type__    = "account" +    __version__ = "0.03" + +    __description__ = """Uploadable.ch account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Sasch", "gsasch@gmail.com")] + + +    def loadAccountInfo(self, user, req): +        html = req.load("http://www.uploadable.ch/login.php") + +        premium     = '<a href="/logout.php"' in html +        trafficleft = -1 if premium else None + +        return {'validuntil': None, 'trafficleft': trafficleft, 'premium': premium}  #@TODO: validuntil + + +    def login(self, user, data, req): +        html = req.load("http://www.uploadable.ch/login.php", +                        post={'userName'     : user, +                              'userPassword' : data["password"], +                              'autoLogin'    : "1", +                              'action__login': "normalLogin"}, +                        decode=True) + +        if "Login failed" in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/UploadcCom.py b/pyload/plugin/account/UploadcCom.py new file mode 100644 index 000000000..66863c456 --- /dev/null +++ b/pyload/plugin/account/UploadcCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class UploadcCom(XFSAccount): +    __name__    = "UploadcCom" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """Uploadc.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "uploadc.com" diff --git a/pyload/plugin/account/UploadedTo.py b/pyload/plugin/account/UploadedTo.py new file mode 100644 index 000000000..37900c7d3 --- /dev/null +++ b/pyload/plugin/account/UploadedTo.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account + + +class UploadedTo(Account): +    __name__    = "UploadedTo" +    __type__    = "account" +    __version__ = "0.30" + +    __description__ = """Uploaded.to account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    PREMIUM_PATTERN = r'<em>Premium</em>' +    VALID_UNTIL_PATTERN = r'<td>Duration:</td>\s*<th>(.+?)<' +    TRAFFIC_LEFT_PATTERN = r'<b class="cB">(?P<S>[\d.,]+) (?P<U>[\w^_]+)' + + +    def loadAccountInfo(self, user, req): +        validuntil  = None +        trafficleft = None +        premium     = None + +        html = req.load("http://uploaded.net/me") + +        premium = True if re.search(self.PREMIUM_PATTERN, html) else False + +        m = re.search(self.VALID_UNTIL_PATTERN, html, re.M) +        if m: +            expiredate = m.group(1).lower().strip() + +            if expiredate == "unlimited": +                validuntil = -1 +            else: +                m = re.findall(r'(\d+) (week|day|hour)', expiredate) +                if m: +                    validuntil = time.time() +                    for n, u in m: +                        validuntil += float(n) * 60 * 60 * {'week': 168, 'day': 24, 'hour': 1}[u] + +        m = re.search(self.TRAFFIC_LEFT_PATTERN, html) +        if m: +            traffic = m.groupdict() +            size    = traffic['S'].replace('.', '') +            unit    = traffic['U'].lower() + +            if unit.startswith('t'):  #@NOTE: Remove in 0.4.10 +                trafficleft = float(size.replace(',', '.')) / 1024 +                trafficleft *= 1 << 40 +            else: +                trafficleft = self.parseTraffic(size + unit) + +        return {'validuntil' : validuntil, +                'trafficleft': trafficleft, +                'premium'    : premium} + + +    def login(self, user, data, req): +        # req.cj.setCookie("uploaded.net", "lang", "en") + +        html = req.load("http://uploaded.net/io/login", +                        post={'id': user, 'pw': data['password'], '_': ""}, +                        decode=True) + +        if '"err"' in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/UploadheroCom.py b/pyload/plugin/account/UploadheroCom.py new file mode 100644 index 000000000..c73fc30f5 --- /dev/null +++ b/pyload/plugin/account/UploadheroCom.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +import re +import datetime +import time + +from pyload.plugin.Account import Account + + +class UploadheroCom(Account): +    __name__    = "UploadheroCom" +    __type__    = "account" +    __version__ = "0.21" + +    __description__ = """Uploadhero.co account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("mcmyst", "mcmyst@hotmail.fr")] + + +    def loadAccountInfo(self, user, req): +        premium_pattern = re.compile('Il vous reste <span class="bleu">(\d+)</span> jours premium') + +        data = self.getAccountData(user) +        html = req.load("http://uploadhero.co/my-account") + +        if premium_pattern.search(html): +            end_date = datetime.date.today() + datetime.timedelta(days=int(premium_pattern.search(html).group(1))) +            end_date = time.mktime(future.timetuple()) +            account_info = {"validuntil": end_date, "trafficleft": -1, "premium": True} +        else: +            account_info = {"validuntil": -1, "trafficleft": -1, "premium": False} + +        return account_info + + +    def login(self, user, data, req): +        html = req.load("http://uploadhero.co/lib/connexion.php", +                        post={"pseudo_login": user, "password_login": data['password']}, +                        decode=True) + +        if "mot de passe invalide" in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/UploadingCom.py b/pyload/plugin/account/UploadingCom.py new file mode 100644 index 000000000..e1f0d07f7 --- /dev/null +++ b/pyload/plugin/account/UploadingCom.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Account import Account +from pyload.plugin.internal.SimpleHoster import set_cookies + + +class UploadingCom(Account): +    __name__    = "UploadingCom" +    __type__    = "account" +    __version__ = "0.12" + +    __description__ = """Uploading.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de")] + + +    PREMIUM_PATTERN = r'UPGRADE TO PREMIUM' +    VALID_UNTIL_PATTERN = r'Valid Until:(.+?)<' + + +    def loadAccountInfo(self, user, req): +        validuntil  = None +        trafficleft = None +        premium     = None + +        html = req.load("http://uploading.com/") + +        premium = False if re.search(self.PREMIUM_PATTERN, html) else True + +        m = re.search(self.VALID_UNTIL_PATTERN, html) +        if m: +            expiredate = m.group(1).strip() +            self.logDebug("Expire date: " + expiredate) + +            try: +                validuntil = time.mktime(time.strptime(expiredate, "%b %d, %Y")) + +            except Exception, e: +                self.logError(e) + +            else: +                if validuntil > time.mktime(time.gmtime()): +                    premium    = True +                else: +                    premium    = False +                    validuntil = None + +        return {'validuntil' : validuntil, +                'trafficleft': trafficleft, +                'premium'    : premium} + + +    def login(self, user, data, req): +        set_cookies(req.cj, +                    [("uploading.com", "lang"    , "1" ), +                     ("uploading.com", "language", "1" ), +                     ("uploading.com", "setlang" , "en"), +                     ("uploading.com", "_lang"   , "en")]) + +        req.load("http://uploading.com/") +        req.load("http://uploading.com/general/login_form/?JsHttpRequest=%s-xml" % long(time.time() * 1000), +                 post={'email': user, 'password': data['password'], 'remember': "on"}) diff --git a/pyload/plugin/account/UptoboxCom.py b/pyload/plugin/account/UptoboxCom.py new file mode 100644 index 000000000..f7cb7a82e --- /dev/null +++ b/pyload/plugin/account/UptoboxCom.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class UptoboxCom(XFSAccount): +    __name__    = "UptoboxCom" +    __type__    = "account" +    __version__ = "0.08" + +    __description__ = """DDLStorage.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    HOSTER_DOMAIN = "uptobox.com" +    HOSTER_URL    = "https://uptobox.com/" +    LOGIN_URL     = "https://login.uptobox.com/" diff --git a/pyload/plugin/account/VidPlayNet.py b/pyload/plugin/account/VidPlayNet.py new file mode 100644 index 000000000..390520a00 --- /dev/null +++ b/pyload/plugin/account/VidPlayNet.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class VidPlayNet(XFSAccount): +    __name__    = "VidPlayNet" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """VidPlay.net account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "vidplay.net" diff --git a/pyload/plugin/account/WebshareCz.py b/pyload/plugin/account/WebshareCz.py new file mode 100644 index 000000000..5384134ad --- /dev/null +++ b/pyload/plugin/account/WebshareCz.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from hashlib import md5, sha1 +from passlib.hash import md5_crypt + +from pyload.plugin.Account import Account + + +class WebshareCz(Account): +    __name__    = "WebshareCz" +    __type__    = "account" +    __version__ = "0.07" + +    __description__ = """Webshare.cz account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("rush", "radek.senfeld@gmail.com")] + + +    VALID_UNTIL_PATTERN = r'<vip_until>(.+)</vip_until>' + +    TRAFFIC_LEFT_PATTERN = r'<bytes>(.+)</bytes>' + + +    def loadAccountInfo(self, user, req): +        html = req.load("https://webshare.cz/api/user_data/", +                        post={'wst': self.infos['wst']}, +                        decode=True) + +        self.logDebug("Response: " + html) + +        expiredate = re.search(self.VALID_UNTIL_PATTERN, html).group(1) +        self.logDebug("Expire date: " + expiredate) + +        validuntil  = time.mktime(time.strptime(expiredate, "%Y-%m-%d %H:%M:%S")) +        trafficleft = self.parseTraffic(re.search(self.TRAFFIC_LEFT_PATTERN, html).group(1)) +        premium     = validuntil > time.time() + +        return {'validuntil': validuntil, 'trafficleft': -1, 'premium': premium} + + +    def login(self, user, data, req): +        salt = req.load("https://webshare.cz/api/salt/", +                        post={'username_or_email': user, +                              'wst'              : ""}, +                        decode=True) + +        if "<status>OK</status>" not in salt: +            self.wrongPassword() + +        salt     = re.search('<salt>(.+)</salt>', salt).group(1) +        password = sha1(md5_crypt.encrypt(data["password"], salt=salt)).hexdigest() +        digest   = md5(user + ":Webshare:" + password).hexdigest() + +        login = req.load("https://webshare.cz/api/login/", +                         post={'digest'           : digest, +                               'keep_logged_in'   : 1, +                               'password'         : password, +                               'username_or_email': user, +                               'wst'              : ""}, +                         decode=True) + +        if "<status>OK</status>" not in login: +            self.wrongPassword() + +        self.infos['wst'] = re.search('<token>(.+)</token>', login).group(1) diff --git a/pyload/plugin/account/XFileSharingPro.py b/pyload/plugin/account/XFileSharingPro.py new file mode 100644 index 000000000..216af5385 --- /dev/null +++ b/pyload/plugin/account/XFileSharingPro.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSAccount import XFSAccount + + +class XFileSharingPro(XFSAccount): +    __name__    = "XFileSharingPro" +    __type__    = "account" +    __version__ = "0.06" + +    __description__ = """XFileSharingPro multi-purpose account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = None + + +    def init(self): +        if self.HOSTER_DOMAIN: +            return super(XFileSharingPro, self).init() + + +    def loadAccountInfo(self, user, req): +        return super(XFileSharingPro if self.HOSTER_DOMAIN else XFSAccount, self).loadAccountInfo(user, req) + + +    def login(self, user, data, req): +        if self.HOSTER_DOMAIN: +            try: +                return super(XFileSharingPro, self).login(user, data, req) +            except Exception: +                self.HOSTER_URL = self.HOSTER_URL.replace("www.", "") +                return super(XFileSharingPro, self).login(user, data, req) diff --git a/pyload/plugin/account/YibaishiwuCom.py b/pyload/plugin/account/YibaishiwuCom.py new file mode 100644 index 000000000..150b0d931 --- /dev/null +++ b/pyload/plugin/account/YibaishiwuCom.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Account import Account + + +class YibaishiwuCom(Account): +    __name__    = "YibaishiwuCom" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """115.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    ACCOUNT_INFO_PATTERN = r'var USER_PERMISSION = {(.*?)}' + + +    def loadAccountInfo(self, user, req): +        #self.relogin(user) +        html = req.load("http://115.com/", decode=True) + +        m = re.search(self.ACCOUNT_INFO_PATTERN, html, re.S) +        premium = True if m and 'is_vip: 1' in m.group(1) else False +        validuntil = trafficleft = (-1 if m else 0) +        return dict({"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium}) + + +    def login(self, user, data, req): +        html = req.load("http://passport.115.com/?ac=login", +                        post={"back": "http://www.115.com/", +                              "goto": "http://115.com/", +                              "login[account]": user, +                              "login[passwd]": data['password']}, +                        decode=True) + +        if not 'var USER_PERMISSION = {' in html: +            self.wrongPassword() diff --git a/pyload/plugin/account/ZeveraCom.py b/pyload/plugin/account/ZeveraCom.py new file mode 100644 index 000000000..0e6d50a9b --- /dev/null +++ b/pyload/plugin/account/ZeveraCom.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- + +import time + +from pyload.plugin.Account import Account + + +class ZeveraCom(Account): +    __name__    = "ZeveraCom" +    __type__    = "account" +    __version__ = "0.26" + +    __description__ = """Zevera.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "zevera.com" + + +    def __init__(self, manager, accounts):  #@TODO: remove in 0.4.10 +        self.init() +        return super(ZeveraCom, self).__init__(manager, accounts) + + +    def init(self): +        if not self.HOSTER_DOMAIN: +            self.logError(_("Missing HOSTER_DOMAIN")) + +        if not hasattr(self, "API_URL"): +            self.API_URL = "http://api.%s/jDownloader.ashx" % (self.HOSTER_DOMAIN or "") + + +    def loadAccountInfo(self, user, req): +        validuntil  = None +        trafficleft = None +        premium     = False + +        api = self.api_response(req) + +        if "No trafic" not in api and api['endsubscriptiondate'] != "Expired!": +            validuntil  = time.mktime(time.strptime(api['endsubscriptiondate'], "%Y/%m/%d %H:%M:%S")) +            trafficleft = float(api['availabletodaytraffic']) * 1024 if api['orondaytrafficlimit'] != '0' else -1 +            premium     = True + +        return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium} + + +    def login(self, user, data, req): +        self.user     = user +        self.password = data['password'] + +        if self.api_response(req) == "No trafic": +            self.wrongPassword() + + +    def api_response(self, req, just_header=False, **kwargs): +        get_data = {'cmd'  : "accountinfo", +                    'login': self.user, +                    'pass' : self.password} + +        get_data.update(kwargs) + +        res = req.load(self.API_URL, +                       get=get_data, +                       just_header=just_header, +                       decode=True) + +        self.logDebug(res) + +        if ':' in res: +            if not just_header: +                res = res.replace(',', '\n') +            return dict((y.strip().lower(), z.strip()) for (y, z) in +                        [x.split(':', 1) for x in res.splitlines() if ':' in x]) +        else: +            return res diff --git a/pyload/plugin/account/__init__.py b/pyload/plugin/account/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/pyload/plugin/account/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/pyload/plugin/addon/AndroidPhoneNotify.py b/pyload/plugin/addon/AndroidPhoneNotify.py new file mode 100644 index 000000000..d3b390e6e --- /dev/null +++ b/pyload/plugin/addon/AndroidPhoneNotify.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- + +import time + +from pyload.network.RequestFactory import getURL +from pyload.plugin.Addon import Addon, Expose + + +class AndroidPhoneNotify(Addon): +    __name__    = "AndroidPhoneNotify" +    __type__    = "addon" +    __version__ = "0.07" + +    __config__ = [("apikey"         , "str" , "API key"                                  , ""   ), +                  ("notifycaptcha"  , "bool", "Notify captcha request"                   , True ), +                  ("notifypackage"  , "bool", "Notify package finished"                  , True ), +                  ("notifyprocessed", "bool", "Notify packages processed"                , True ), +                  ("notifyupdate"   , "bool", "Notify plugin updates"                    , True ), +                  ("notifyexit"     , "bool", "Notify pyLoad shutdown"                   , True ), +                  ("sendtimewait"   , "int" , "Timewait in seconds between notifications", 5    ), +                  ("sendpermin"     , "int" , "Max notifications per minute"             , 12   ), +                  ("ignoreclient"   , "bool", "Send notifications if client is connected", False)] + +    __description__ = """Send push notifications to your Android Phone (using notifymyandroid.com)""" +    __license__     = "GPLv3" +    __authors__     = [("Steven Kosyra" , "steven.kosyra@gmail.com"), +                       ("Walter Purcaro", "vuolter@gmail.com"      )] + + +    event_list = ["allDownloadsProcessed", "plugin_updated"] + + +    def setup(self): +        self.last_notify   = 0 +        self.notifications = 0 + + +    def plugin_updated(self, type_plugins): +        if not self.getConfig('notifyupdate'): +            return + +        self.notify(_("Plugins updated"), str(type_plugins)) + + +    def exit(self): +        if not self.getConfig('notifyexit'): +            return + +        if self.core.do_restart: +            self.notify(_("Restarting pyLoad")) +        else: +            self.notify(_("Exiting pyLoad")) + + +    def newCaptchaTask(self, task): +        if not self.getConfig('notifycaptcha'): +            return + +        self.notify(_("Captcha"), _("New request waiting user input")) + + +    def packageFinished(self, pypack): +        if self.getConfig('notifypackage'): +            self.notify(_("Package finished"), pypack.name) + + +    def allDownloadsProcessed(self): +        if not self.getConfig('notifyprocessed'): +            return + +        if any(True for pdata in self.core.api.getQueue() if pdata.linksdone < pdata.linkstotal): +            self.notify(_("Package failed"), _("One or more packages was not completed successfully")) +        else: +            self.notify(_("All packages finished")) + + +    @Expose +    def notify(self, +               event, +               msg="", +               key=self.getConfig('apikey')): + +        if not key: +            return + +        if self.core.isClientConnected() and not self.getConfig('ignoreclient'): +            return + +        elapsed_time = time.time() - self.last_notify + +        if elapsed_time < self.getConf("sendtimewait"): +            return + +        if elapsed_time > 60: +            self.notifications = 0 + +        elif self.notifications >= self.getConf("sendpermin"): +            return + + +        getURL("http://www.notifymyandroid.com/publicapi/notify", +               get={'apikey'     : key, +                    'application': "pyLoad", +                    'event'      : event, +                    'description': msg}) + +        self.last_notify    = time.time() +        self.notifications += 1 diff --git a/pyload/plugin/addon/AntiVirus.py b/pyload/plugin/addon/AntiVirus.py new file mode 100644 index 000000000..2213cddc1 --- /dev/null +++ b/pyload/plugin/addon/AntiVirus.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- + +import os +import shutil +import subprocess + +from pyload.plugin.Addon import Addon, Expose, threaded +from pyload.utils import fs_encode, fs_join + + +class AntiVirus(Addon): +    __name__    = "AntiVirus" +    __type__    = "addon" +    __version__ = "0.07" + +    #@TODO: add trash option (use Send2Trash lib) +    __config__ = [("action"    , "Antivirus default;Delete;Quarantine", "Manage infected files"                     , "Antivirus default"), +                  ("quardir"   , "folder"                             , "Quarantine folder"                         , ""                 ), +                  ("deltotrash", "bool"                               , "Move to trash (recycle bin) instead delete", True               ), +                  ("scanfailed", "bool"                               , "Scan incompleted files (failed downloads)" , False              ), +                  ("cmdfile"   , "file"                               , "Antivirus executable"                      , ""                 ), +                  ("cmdargs"   , "str"                                , "Scan options"                              , ""                 ), +                  ("ignore-err", "bool"                               , "Ignore scan errors"                        , False              )] + +    __description__ = """Scan downloaded files with antivirus program""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    interval = 0  #@TODO: Remove in 0.4.10 + + +    def setup(self): +        self.info = {}  #@TODO: Remove in 0.4.10 + +        try: +            import send2trash + +        except ImportError: +            self.logDebug("Send2Trash lib not found") +            self.trashable = False + +        else: +            self.trashable = True + + +    @Expose +    @threaded +    def scan(self, pyfile, thread): +        file     = fs_encode(pyfile.plugin.lastDownload) +        filename = os.path.basename(pyfile.plugin.lastDownload) +        cmdfile  = fs_encode(self.getConfig('cmdfile')) +        cmdargs  = fs_encode(self.getConfig('cmdargs').strip()) + +        if not os.path.isfile(file) or not os.path.isfile(cmdfile): +            return + +        thread.addActive(pyfile) +        pyfile.setCustomStatus(_("virus scanning")) +        pyfile.setProgress(0) + +        try: +            p = subprocess.Popen([cmdfile, cmdargs, file], bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + +            out, err = map(str.strip, p.communicate()) + +            if out: +                self.logInfo(filename, out) + +            if err: +                self.logWarning(filename, err) +                if not self.getConfig('ignore-err'): +                    self.logDebug("Delete/Quarantine task is aborted") +                    return + +            if p.returncode: +                pyfile.error = _("infected file") +                action = self.getConfig('action') +                try: +                    if action == "Delete": +                        if not self.getConfig('deltotrash'): +                            os.remove(file) + +                        elif self.trashable: +                            send2trash.send2trash(file) + +                        else: +                            self.logWarning(_("Unable to move file to trash, move to quarantine instead")) +                            pyfile.setCustomStatus(_("file moving")) +                            shutil.move(file, self.getConfig('quardir')) + +                    elif action == "Quarantine": +                        pyfile.setCustomStatus(_("file moving")) +                        shutil.move(file, self.getConfig('quardir')) + +                except (IOError, shutil.Error), e: +                    self.logError(filename, action + " action failed!", e) + +            elif not out and not err: +                self.logDebug(filename, "No infected file found") + +        finally: +            pyfile.setProgress(100) +            thread.finishFile(pyfile) + + +    def downloadFinished(self, pyfile): +        return self.scan(pyfile) + + +    def downloadFailed(self, pyfile): +        #: Check if pyfile is still "failed", +        #  maybe might has been restarted in meantime +        if pyfile.status == 8 and self.getConfig('scanfailed'): +            return self.scan(pyfile) diff --git a/pyload/plugin/addon/Checksum.py b/pyload/plugin/addon/Checksum.py new file mode 100644 index 000000000..4b1380506 --- /dev/null +++ b/pyload/plugin/addon/Checksum.py @@ -0,0 +1,197 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import hashlib +import re +import zlib + +from os import remove +from os.path import getsize, isfile, splitext + +from pyload.plugin.Addon import Addon +from pyload.utils import fs_join, fs_encode + + +def computeChecksum(local_file, algorithm): +    if algorithm in getattr(hashlib, "algorithms", ("md5", "sha1", "sha224", "sha256", "sha384", "sha512")): +        h = getattr(hashlib, algorithm)() + +        with open(local_file, 'rb') as f: +            for chunk in iter(lambda: f.read(128 * h.block_size), ''): +                h.update(chunk) + +        return h.hexdigest() + +    elif algorithm in ("adler32", "crc32"): +        hf = getattr(zlib, algorithm) +        last = 0 + +        with open(local_file, 'rb') as f: +            for chunk in iter(lambda: f.read(8192), ''): +                last = hf(chunk, last) + +        return "%x" % last + +    else: +        return None + + +class Checksum(Addon): +    __name__    = "Checksum" +    __type__    = "addon" +    __version__ = "0.16" + +    __config__ = [("activated"     , "bool"              , "Activated"                                            , True   ), +                ("check_checksum", "bool"              , "Check checksum? (If False only size will be verified)", True   ), +                ("check_action"  , "fail;retry;nothing", "What to do if check fails?"                           , "retry"), +                ("max_tries"     , "int"               , "Number of retries"                                    , 2      ), +                ("retry_action"  , "fail;nothing"      , "What to do if all retries fail?"                      , "fail" ), +                ("wait_time"     , "int"               , "Time to wait before each retry (seconds)"             , 1      )] + +    __description__ = """Verify downloaded file size and checksum""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg"      , "zoidberg@mujmail.cz"), +                       ("Walter Purcaro", "vuolter@gmail.com"  ), +                       ("stickell"      , "l.stickell@yahoo.it")] + + +    methods  = {'sfv' : 'crc32', +                'crc' : 'crc32', +                'hash': 'md5'} +    regexps  = {'sfv'    : r'^(?P<NAME>[^;].+)\s+(?P<HASH>[0-9A-Fa-f]{8})$', +                'md5'    : r'^(?P<NAME>[0-9A-Fa-f]{32})  (?P<FILE>.+)$', +                'crc'    : r'filename=(?P<NAME>.+)\nsize=(?P<SIZE>\d+)\ncrc32=(?P<HASH>[0-9A-Fa-f]{8})$', +                'default': r'^(?P<HASH>[0-9A-Fa-f]+)\s+\*?(?P<NAME>.+)$'} + + +    def activate(self): +        if not self.getConfig('check_checksum'): +            self.logInfo(_("Checksum validation is disabled in plugin configuration")) + + +    def setup(self): +        self.algorithms = sorted( +            getattr(hashlib, "algorithms", ("md5", "sha1", "sha224", "sha256", "sha384", "sha512")), reverse=True) + +        self.algorithms.extend(["crc32", "adler32"]) + +        self.formats = self.algorithms + ["sfv", "crc", "hash"] + + +    def downloadFinished(self, pyfile): +        """ +        Compute checksum for the downloaded file and compare it with the hash provided by the hoster. +        pyfile.plugin.check_data should be a dictionary which can contain: +        a) if known, the exact filesize in bytes (e.g. "size": 123456789) +        b) hexadecimal hash string with algorithm name as key (e.g. "md5": "d76505d0869f9f928a17d42d66326307") +        """ +        if hasattr(pyfile.plugin, "check_data") and isinstance(pyfile.plugin.check_data, dict): +            data = pyfile.plugin.check_data.copy() + +        elif hasattr(pyfile.plugin, "api_data") and isinstance(pyfile.plugin.api_data, dict): +            data = pyfile.plugin.api_data.copy() + +        elif hasattr(pyfile.plugin, "info") and isinstance(pyfile.plugin.info, dict): +            data = pyfile.plugin.info.copy() +            data.pop('size', None)  #@NOTE: Don't check file size until a similary matcher will be implemented + +        else: +            return + +        self.logDebug(data) + +        if not pyfile.plugin.lastDownload: +            self.checkFailed(pyfile, None, "No file downloaded") + +        local_file = fs_encode(pyfile.plugin.lastDownload) +        #download_folder = self.config['general']['download_folder'] +        #local_file = fs_encode(fs_join(download_folder, pyfile.package().folder, pyfile.name)) + +        if not isfile(local_file): +            self.checkFailed(pyfile, None, "File does not exist") + +        # validate file size +        if "size" in data: +            api_size  = int(data['size']) +            file_size = getsize(local_file) + +            if api_size != file_size: +                self.logWarning(_("File %s has incorrect size: %d B (%d expected)") % (pyfile.name, file_size, api_size)) +                self.checkFailed(pyfile, local_file, "Incorrect file size") + +            data.pop('size', None) + +        # validate checksum +        if data and self.getConfig('check_checksum'): + +            if not 'md5' in data: +                for type in ("checksum", "hashsum", "hash"): +                    if type in data: +                        data['md5'] = data[type]  #@NOTE: What happens if it's not an md5 hash? +                        break + +            for key in self.algorithms: +                if key in data: +                    checksum = computeChecksum(local_file, key.replace("-", "").lower()) +                    if checksum: +                        if checksum == data[key].lower(): +                            self.logInfo(_('File integrity of "%s" verified by %s checksum (%s)') % +                                        (pyfile.name, key.upper(), checksum)) +                            break +                        else: +                            self.logWarning(_("%s checksum for file %s does not match (%s != %s)") % +                                           (key.upper(), pyfile.name, checksum, data[key])) +                            self.checkFailed(pyfile, local_file, "Checksums do not match") +                    else: +                        self.logWarning(_("Unsupported hashing algorithm"), key.upper()) +            else: +                self.logWarning(_("Unable to validate checksum for file: ") + pyfile.name) + + +    def checkFailed(self, pyfile, local_file, msg): +        check_action = self.getConfig('check_action') +        if check_action == "retry": +            max_tries = self.getConfig('max_tries') +            retry_action = self.getConfig('retry_action') +            if pyfile.plugin.retries < max_tries: +                if local_file: +                    remove(local_file) +                pyfile.plugin.retry(max_tries, self.getConfig('wait_time'), msg) +            elif retry_action == "nothing": +                return +        elif check_action == "nothing": +            return +        pyfile.plugin.fail(reason=msg) + + +    def packageFinished(self, pypack): +        download_folder = fs_join(self.config['general']['download_folder'], pypack.folder, "") + +        for link in pypack.getChildren().itervalues(): +            file_type = splitext(link['name'])[1][1:].lower() + +            if file_type not in self.formats: +                continue + +            hash_file = fs_encode(fs_join(download_folder, link['name'])) +            if not isfile(hash_file): +                self.logWarning(_("File not found"), link['name']) +                continue + +            with open(hash_file) as f: +                text = f.read() + +            for m in re.finditer(self.regexps.get(file_type, self.regexps['default']), text): +                data = m.groupdict() +                self.logDebug(link['name'], data) + +                local_file = fs_encode(fs_join(download_folder, data['NAME'])) +                algorithm = self.methods.get(file_type, file_type) +                checksum = computeChecksum(local_file, algorithm) +                if checksum == data['HASH']: +                    self.logInfo(_('File integrity of "%s" verified by %s checksum (%s)') % +                                (data['NAME'], algorithm, checksum)) +                else: +                    self.logWarning(_("%s checksum for file %s does not match (%s != %s)") % +                                   (algorithm, data['NAME'], checksum, data['HASH'])) diff --git a/pyload/plugin/addon/ClickAndLoad.py b/pyload/plugin/addon/ClickAndLoad.py new file mode 100644 index 000000000..73976d7e2 --- /dev/null +++ b/pyload/plugin/addon/ClickAndLoad.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- + +import socket +import time + +from threading import Lock + +from pyload.plugin.Addon import Addon, threaded + + +def forward(source, destination): +    try: +        bufsize = 1024 +        bufdata = source.recv(bufsize) +        while bufdata: +            destination.sendall(bufdata) +            bufdata = source.recv(bufsize) +    finally: +        destination.shutdown(socket.SHUT_WR) +        # destination.close() + + +#@TODO: IPv6 support +class ClickAndLoad(Addon): +    __name__    = "ClickAndLoad" +    __type__    = "addon" +    __version__ = "0.41" + +    __config__ = [("activated", "bool", "Activated"                             , True), +                  ("port"     , "int" , "Port"                                  , 9666), +                  ("extern"   , "bool", "Listen on the public network interface", True)] + +    __description__ = """Click'n'Load addon plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN"         , "RaNaN@pyload.de"  ), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    def activate(self): +        if not self.core.config['webui']['activated']: +            return + +        ip      = "" if self.getConfig('extern') else "127.0.0.1" +        webport = self.core.config['webui']['port'] +        cnlport = self.getConfig('port') + +        self.proxy(ip, webport, cnlport) + + +    @threaded +    def proxy(self, ip, webport, cnlport): +        time.sleep(10)  #@TODO: Remove in 0.4.10 (implement addon delay on startup) + +        self.logInfo(_("Proxy listening on %s:%s") % (ip or "0.0.0.0", cnlport)) + +        self._server(ip, webport, cnlport) + +        lock = Lock() +        lock.acquire() +        lock.acquire() + + +    @threaded +    def _server(self, ip, webport, cnlport): +        try: +            dock_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +            dock_socket.bind((ip, cnlport)) +            dock_socket.listen(5) + +            while True: +                client_socket, client_addr = dock_socket.accept() +                self.logDebug("Connection from %s:%s" % client_addr) + +                server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +                server_socket.connect(("127.0.0.1", webport)) + +                self.manager.startThread(forward, client_socket, server_socket) +                self.manager.startThread(forward, server_socket, client_socket) + +        except socket.timeout: +            self.logDebug("Connection timed out, retrying...") +            return self._server(ip, webport, cnlport) + +        except socket.error, e: +            self.logError(e) +            time.sleep(240) +            return self._server(ip, webport, cnlport) diff --git a/pyload/plugin/addon/DeleteFinished.py b/pyload/plugin/addon/DeleteFinished.py new file mode 100644 index 000000000..801e48ed6 --- /dev/null +++ b/pyload/plugin/addon/DeleteFinished.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- + +from pyload.database import style +from pyload.plugin.Addon import Addon + + +class DeleteFinished(Addon): +    __name__    = "DeleteFinished" +    __type__    = "addon" +    __version__ = "1.12" + +    __config__ = [("interval"  , "int" , "Check interval in hours"          , 72   ), +                  ("deloffline", "bool", "Delete package with offline links", False)] + +    __description__ = """Automatically delete all finished packages from queue""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    # event_list = ["pluginConfigChanged"] + +    MIN_CHECK_INTERVAL = 1 * 60 * 60  #: 1 hour + + +    ## overwritten methods ## +    def setup(self): +        self.interval = self.MIN_CHECK_INTERVAL + + +    def periodical(self): +        if not self.info['sleep']: +            deloffline = self.getConfig('deloffline') +            mode = '0,1,4' if deloffline else '0,4' +            msg = _('delete all finished packages in queue list (%s packages with offline links)') +            self.logInfo(msg % (_('including') if deloffline else _('excluding'))) +            self.deleteFinished(mode) +            self.info['sleep'] = True +            self.addEvent('packageFinished', self.wakeup) + + +    # def pluginConfigChanged(self, plugin, name, value): +        # if name == "interval" and value != self.interval: +            # self.interval = value * 3600 +            # self.initPeriodical() + + +    def deactivate(self): +        self.manager.removeEvent('packageFinished', self.wakeup) + + +    def activate(self): +        self.info['sleep'] = True +        # interval = self.getConfig('interval') +        # self.pluginConfigChanged(self.__class__.__name__, 'interval', interval) +        self.interval = max(self.MIN_CHECK_INTERVAL, self.getConfig('interval') * 60 * 60) +        self.addEvent('packageFinished', self.wakeup) +        self.initPeriodical() + + +    ## own methods ## +    @style.queue +    def deleteFinished(self, mode): +        self.c.execute('DELETE FROM packages WHERE NOT EXISTS(SELECT 1 FROM links WHERE package=packages.id AND status NOT IN (%s))' % mode) +        self.c.execute('DELETE FROM links WHERE NOT EXISTS(SELECT 1 FROM packages WHERE id=links.package)') + + +    def wakeup(self, pypack): +        self.manager.removeEvent('packageFinished', self.wakeup) +        self.info['sleep'] = False + + +    ## event managing ## +    def addEvent(self, event, func): +        """Adds an event listener for event name""" +        if event in self.manager.events: +            if func in self.manager.events[event]: +                self.logDebug("Function already registered", func) +            else: +                self.manager.events[event].append(func) +        else: +            self.manager.events[event] = [func] diff --git a/pyload/plugin/addon/DownloadScheduler.py b/pyload/plugin/addon/DownloadScheduler.py new file mode 100644 index 000000000..de961cc1f --- /dev/null +++ b/pyload/plugin/addon/DownloadScheduler.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.Addon import Addon + + +class DownloadScheduler(Addon): +    __name__    = "DownloadScheduler" +    __type__    = "addon" +    __version__ = "0.22" + +    __config__ = [("timetable", "str" , "List time periods as hh:mm full or number(kB/s)"      , "0:00 full, 7:00 250, 10:00 0, 17:00 150"), +                ("abort"    , "bool", "Abort active downloads when start period with speed 0", False                                    )] + +    __description__ = """Download Scheduler""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    def activate(self): +        self.updateSchedule() + + +    def updateSchedule(self, schedule=None): +        if schedule is None: +            schedule = self.getConfig('timetable') + +        schedule = re.findall("(\d{1,2}):(\d{2})[\s]*(-?\d+)", +                              schedule.lower().replace("full", "-1").replace("none", "0")) +        if not schedule: +            self.logError(_("Invalid schedule")) +            return + +        t0  = time.localtime() +        now = (t0.tm_hour, t0.tm_min, t0.tm_sec, "X") +        schedule = sorted([(int(x[0]), int(x[1]), 0, int(x[2])) for x in schedule] + [now]) + +        self.logDebug("Schedule", schedule) + +        for i, v in enumerate(schedule): +            if v[3] == "X": +                last, next = schedule[i - 1], schedule[(i + 1) % len(schedule)] +                self.logDebug("Now/Last/Next", now, last, next) + +                self.setDownloadSpeed(last[3]) + +                next_time = (((24 + next[0] - now[0]) * 60 + next[1] - now[1]) * 60 + next[2] - now[2]) % 86400 +                self.core.scheduler.removeJob(self.cb) +                self.cb = self.core.scheduler.addJob(next_time, self.updateSchedule, threaded=False) + + +    def setDownloadSpeed(self, speed): +        if speed == 0: +            abort = self.getConfig('abort') +            self.logInfo(_("Stopping download server. (Running downloads will %sbe aborted.)") % '' if abort else _('not ')) +            self.core.api.pauseServer() +            if abort: +                self.core.api.stopAllDownloads() +        else: +            self.core.api.unpauseServer() + +            if speed > 0: +                self.logInfo(_("Setting download speed to %d kB/s") % speed) +                self.core.api.setConfigValue("download", "limit_speed", 1) +                self.core.api.setConfigValue("download", "max_speed", speed) +            else: +                self.logInfo(_("Setting download speed to FULL")) +                self.core.api.setConfigValue("download", "limit_speed", 0) +                self.core.api.setConfigValue("download", "max_speed", -1) diff --git a/pyload/plugin/addon/ExternalScripts.py b/pyload/plugin/addon/ExternalScripts.py new file mode 100644 index 000000000..05b1d7b65 --- /dev/null +++ b/pyload/plugin/addon/ExternalScripts.py @@ -0,0 +1,216 @@ +# -*- coding: utf-8 -*- + +import os +import subprocess + +from pyload.plugin.Addon import Addon +from pyload.utils import fs_encode, fs_join + + +class ExternalScripts(Addon): +    __name__    = "ExternalScripts" +    __type__    = "addon" +    __version__ = "0.39" + +    __config__ = [("activated", "bool", "Activated"         , True ), +                  ("waitend"  , "bool", "Wait script ending", False)] + +    __description__ = """Run external scripts""" +    __license__     = "GPLv3" +    __authors__     = [("mkaay"         , "mkaay@mkaay.de"   ), +                       ("RaNaN"         , "ranan@pyload.org" ), +                       ("spoob"         , "spoob@pyload.org" ), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    event_list = ["archive_extract_failed", "archive_extracted"     , +                  "package_extract_failed", "package_extracted"     , +                  "all_archives_extracted", "all_archives_processed", +                  "allDownloadsFinished"  , "allDownloadsProcessed" , +                  "packageDeleted"] + + +    def setup(self): +        self.info    = {'oldip': None} +        self.scripts = {} + +        folders = ["pyload_start", "pyload_restart", "pyload_stop", +                   "before_reconnect", "after_reconnect", +                   "download_preparing", "download_failed", "download_finished", +                   "archive_extract_failed", "archive_extracted", +                   "package_finished", "package_deleted", "package_extract_failed", "package_extracted", +                   "all_downloads_processed", "all_downloads_finished",  #@TODO: Invert `all_downloads_processed`, `all_downloads_finished` order in 0.4.10 +                   "all_archives_extracted", "all_archives_processed"] + +        for folder in folders: +            self.scripts[folder] = [] +            for dir in (pypath, ''): +                self.initPluginType(folder, os.path.join(dir, 'scripts', folder)) + +        for script_type, names in self.scripts.iteritems(): +            if names: +                self.logInfo(_("Installed scripts for: ") + script_type, ", ".join(map(os.path.basename, names))) + +        self.pyload_start() + + +    def initPluginType(self, name, dir): +        if not os.path.isdir(dir): +            try: +                os.makedirs(dir) + +            except OSError, e: +                self.logDebug(e) +                return + +        for filename in os.listdir(dir): +            file = fs_join(dir, filename) + +            if not os.path.isfile(file): +                continue + +            if filename[0] in ("#", "_") or filename.endswith("~") or filename.endswith(".swp"): +                continue + +            if not os.access(file, os.X_OK): +                self.logWarning(_("Script not executable:") + " %s/%s" % (name, filename)) + +            self.scripts[name].append(file) + + +    def callScript(self, script, *args): +        try: +            cmd_args = [fs_encode(str(x) if not isinstance(x, basestring) else x) for x in args] +            cmd      = [script] + cmd_args + +            self.logDebug("Executing: %s" % os.path.abspath(script), "Args: " + ' '.join(cmd_args)) + +            p = subprocess.Popen(cmd, bufsize=-1)  #@NOTE: output goes to pyload +            if self.getConfig('waitend'): +                p.communicate() + +        except Exception, e: +            try: +                self.logError(_("Runtime error: %s") % os.path.abspath(script), e) +            except Exception: +                self.logError(_("Runtime error: %s") % os.path.abspath(script), _("Unknown error")) + + +    def pyload_start(self): +        for script in self.scripts['pyload_start']: +            self.callScript(script) + + +    def exit(self): +        for script in self.scripts['pyload_restart' if self.core.do_restart else 'pyload_stop']: +            self.callScript(script) + + +    def beforeReconnecting(self, ip): +        for script in self.scripts['before_reconnect']: +            self.callScript(script, ip) +        self.info['oldip'] = ip + + +    def afterReconnecting(self, ip): +        for script in self.scripts['after_reconnect']: +            self.callScript(script, ip, self.info['oldip'])  #@TODO: Use built-in oldip in 0.4.10 + + +    def downloadPreparing(self, pyfile): +        for script in self.scripts['download_preparing']: +            self.callScript(script, pyfile.id, pyfile.name, None, pyfile.pluginname, pyfile.url) + + +    def downloadFailed(self, pyfile): +        if self.config['general']['folder_per_package']: +            download_folder = fs_join(self.config['general']['download_folder'], pyfile.package().folder) +        else: +            download_folder = self.config['general']['download_folder'] + +        for script in self.scripts['download_failed']: +            file = fs_join(download_folder, pyfile.name) +            self.callScript(script, pyfile.id, pyfile.name, file, pyfile.pluginname, pyfile.url) + + +    def downloadFinished(self, pyfile): +        if self.config['general']['folder_per_package']: +            download_folder = fs_join(self.config['general']['download_folder'], pyfile.package().folder) +        else: +            download_folder = self.config['general']['download_folder'] + +        for script in self.scripts['download_finished']: +            file = fs_join(download_folder, pyfile.name) +            self.callScript(script, pyfile.id, pyfile.name, file, pyfile.pluginname, pyfile.url) + + +    def archive_extract_failed(self, pyfile, archive): +        for script in self.scripts['archive_extract_failed']: +            self.callScript(script, pyfile.id, pyfile.name, archive.filename, archive.out, archive.files) + + +    def archive_extracted(self, pyfile, archive): +        for script in self.scripts['archive_extracted']: +            self.callScript(script, pyfile.id, pyfile.name, archive.filename, archive.out, archive.files) + + +    def packageFinished(self, pypack): +        if self.config['general']['folder_per_package']: +            download_folder = fs_join(self.config['general']['download_folder'], pypack.folder) +        else: +            download_folder = self.config['general']['download_folder'] + +        for script in self.scripts['package_finished']: +            self.callScript(script, pypack.id, pypack.name, download_folder, pypack.password) + + +    def packageDeleted(self, pid): +        pack = self.core.api.getPackageInfo(pid) + +        if self.config['general']['folder_per_package']: +            download_folder = fs_join(self.config['general']['download_folder'], pack.folder) +        else: +            download_folder = self.config['general']['download_folder'] + +        for script in self.scripts['package_deleted']: +            self.callScript(script, pack.id, pack.name, download_folder, pack.password) + + +    def package_extract_failed(self, pypack): +        if self.config['general']['folder_per_package']: +            download_folder = fs_join(self.config['general']['download_folder'], pypack.folder) +        else: +            download_folder = self.config['general']['download_folder'] + +        for script in self.scripts['package_extract_failed']: +            self.callScript(script, pypack.id, pypack.name, download_folder, pypack.password) + + +    def package_extracted(self, pypack): +        if self.config['general']['folder_per_package']: +            download_folder = fs_join(self.config['general']['download_folder'], pypack.folder) +        else: +            download_folder = self.config['general']['download_folder'] + +        for script in self.scripts['package_extracted']: +            self.callScript(script, pypack.id, pypack.name, download_folder) + + +    def allDownloadsFinished(self): +        for script in self.scripts['all_downloads_finished']: +            self.callScript(script) + + +    def allDownloadsProcessed(self): +        for script in self.scripts['all_downloads_processed']: +            self.callScript(script) + + +    def all_archives_extracted(self): +        for script in self.scripts['all_archives_extracted']: +            self.callScript(script) + + +    def all_archives_processed(self): +        for script in self.scripts['all_archives_processed']: +            self.callScript(script) diff --git a/pyload/plugin/addon/ExtractArchive.py b/pyload/plugin/addon/ExtractArchive.py new file mode 100644 index 000000000..07b388ecd --- /dev/null +++ b/pyload/plugin/addon/ExtractArchive.py @@ -0,0 +1,564 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import os +import sys +import traceback + +from copy import copy + +# monkey patch bug in python 2.6 and lower +# http://bugs.python.org/issue6122 , http://bugs.python.org/issue1236 , http://bugs.python.org/issue1731717 +if sys.version_info < (2, 7) and os.name != "nt": +    import errno +    import subprocess + +    def _eintr_retry_call(func, *args): +        while True: +            try: +                return func(*args) + +            except OSError, e: +                if e.errno == errno.EINTR: +                    continue +                raise + + +    # unsued timeout option for older python version +    def wait(self, timeout=0): +        """Wait for child process to terminate.  Returns returncode +        attribute.""" +        if self.returncode is None: +            try: +                pid, sts = _eintr_retry_call(os.waitpid, self.pid, 0) + +            except OSError, e: +                if e.errno != errno.ECHILD: +                    raise +                    # This happens if SIGCLD is set to be ignored or waiting +                # for child processes has otherwise been disabled for our +                # process.  This child is dead, we can't get the status. +                sts = 0 +            self._handle_exitstatus(sts) +        return self.returncode + +    subprocess.Popen.wait = wait + +if os.name != "nt": +    from grp import getgrnam +    from pwd import getpwnam + +from pyload.plugin.Addon import Addon, threaded, Expose +from pyload.plugin.Extractor import ArchiveError, CRCError, PasswordError +from pyload.plugin.internal.SimpleHoster import replace_patterns +from pyload.utils import fs_encode, fs_join, uniqify + + +class ArchiveQueue(object): + +    def __init__(self, plugin, storage): +        self.plugin  = plugin +        self.storage = storage + + +    def get(self): +        try: +            return [int(pid) for pid in self.plugin.getStorage("ExtractArchive:%s" % self.storage, "").decode('base64').split()] +        except Exception: +            return [] + + +    def set(self, value): +        if isinstance(value, list): +            item = str(value)[1:-1].replace(' ', '').replace(',', ' ') +        else: +            item = str(value).strip() +        return self.plugin.setStorage("ExtractArchive:%s" % self.storage, item.encode('base64')[:-1]) + + +    def delete(self): +        return self.plugin.delStorage("ExtractArchive:%s" % self.storage) + + +    def add(self, item): +        queue = self.get() +        if item not in queue: +            return self.set(queue + [item]) +        else: +            return True + + +    def remove(self, item): +        queue = self.get() +        try: +            queue.remove(item) + +        except ValueError: +            pass + +        if queue == []: +            return self.delete() + +        return self.set(queue) + + +class ExtractArchive(Addon): +    __name__    = "ExtractArchive" +    __type__    = "addon" +    __version__ = "1.41" + +    __config__ = [("activated"      , "bool"              , "Activated"                                 , True                                                                     ), +                  ("fullpath"       , "bool"              , "Extract with full paths"                   , True                                                                     ), +                  ("overwrite"      , "bool"              , "Overwrite files"                           , False                                                                    ), +                  ("keepbroken"     , "bool"              , "Try to extract broken archives"            , False                                                                    ), +                  ("repair"         , "bool"              , "Repair broken archives (RAR required)"     , False                                                                    ), +                  ("test"           , "bool"              , "Test archive before extracting"            , False                                                                    ), +                  ("usepasswordfile", "bool"              , "Use password file"                         , True                                                                     ), +                  ("passwordfile"   , "file"              , "Password file"                             , "archive_password.txt"                                                   ), +                  ("delete"         , "bool"              , "Delete archive after extraction"           , True                                                                     ), +                  ("deltotrash"     , "bool"              , "Move to trash (recycle bin) instead delete", True                                                                     ), +                  ("subfolder"      , "bool"              , "Create subfolder for each package"         , False                                                                    ), +                  ("destination"    , "folder"            , "Extract files to folder"                   , ""                                                                       ), +                  ("extensions"     , "str"               , "Extract archives ending with extension"    , "7z,bz2,bzip2,gz,gzip,lha,lzh,lzma,rar,tar,taz,tbz,tbz2,tgz,xar,xz,z,zip"), +                  ("excludefiles"   , "str"               , "Don't extract the following files"         , "*.nfo,*.DS_Store,index.dat,thumb.db"                                    ), +                  ("recursive"      , "bool"              , "Extract archives in archives"              , True                                                                     ), +                  ("waitall"        , "bool"              , "Run after all downloads was processed"     , False                                                                    ), +                  ("renice"         , "int"               , "CPU priority"                              , 0                                                                        )] + +    __description__ = """Extract different kind of archives""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com"), +                       ("Immenz"        , "immenz@gmx.net"   )] + + +    event_list = ["allDownloadsProcessed","packageDeleted"] + +    NAME_REPLACEMENTS = [(r'\.part\d+\.rar$', ".part.rar")] + + +    def setup(self): +        self.queue  = ArchiveQueue(self, "Queue") +        self.failed = ArchiveQueue(self, "Failed") + +        self.interval    = 60 +        self.extracting  = False +        self.lastPackage = False +        self.extractors  = [] +        self.passwords   = [] +        self.repair      = False + +        try: +            import send2trash + +        except ImportError: +            self.logDebug("Send2Trash lib not found") +            self.trashable = False + +        else: +            self.trashable = True + + +    def activate(self): +        for p in ("UnRar", "SevenZip", "UnZip"): +            try: +                module = self.core.pluginManager.loadModule("extractor", p) +                klass  = getattr(module, p) +                if klass.isUsable(): +                    self.extractors.append(klass) +                if klass.REPAIR: +                    self.repair = self.getConfig('repair') + +            except OSError, e: +                if e.errno == 2: +                    self.logWarning(_("No %s installed") % p) +                else: +                    self.logWarning(_("Could not activate: %s") % p, e) +                    if self.core.debug: +                        traceback.print_exc() + +            except Exception, e: +                self.logWarning(_("Could not activate: %s") % p, e) +                if self.core.debug: +                    traceback.print_exc() + +        if self.extractors: +            self.logDebug(*["Found %s %s" % (Extractor.__name__, Extractor.VERSION) for Extractor in self.extractors]) +            self.extractQueued()  #: Resume unfinished extractions +        else: +            self.logInfo(_("No Extract plugins activated")) + + +    @threaded +    def extractQueued(self, thread): +        packages = self.queue.get() +        while packages: +            if self.lastPackage:  #: called from allDownloadsProcessed +                self.lastPackage = False +                if self.extract(packages, thread):  #@NOTE: check only if all gone fine, no failed reporting for now +                    self.manager.dispatchEvent("all_archives_extracted") +                self.manager.dispatchEvent("all_archives_processed") +            else: +                if self.extract(packages, thread):  #@NOTE: check only if all gone fine, no failed reporting for now +                    pass + +            packages = self.queue.get()  #: check for packages added during extraction + + +    @Expose +    def extractPackage(self, *ids): +        """ Extract packages with given id""" +        for id in ids: +            self.queue.add(id) +        if not self.getConfig('waitall') and not self.extracting: +            self.extractQueued() + + +    def packageDeleted(self, pid): +        self.queue.remove(pid) + + +    def packageFinished(self, pypack): +        self.queue.add(pypack.id) +        if not self.getConfig('waitall') and not self.extracting: +            self.extractQueued() + + +    def allDownloadsProcessed(self): +        self.lastPackage = True +        if not self.extracting: +            self.extractQueued() + + +    @Expose +    def extract(self, ids, thread=None):  #@TODO: Use pypack, not pid to improve method usability +        if not ids: +            return False + +        self.extracting = True + +        processed = [] +        extracted = [] +        failed    = [] + +        toList = lambda string: string.replace(' ', '').replace(',', '|').replace(';', '|').split('|') + +        destination = self.getConfig('destination') +        subfolder   = self.getConfig('subfolder') +        fullpath    = self.getConfig('fullpath') +        overwrite   = self.getConfig('overwrite') +        renice      = self.getConfig('renice') +        recursive   = self.getConfig('recursive') +        delete      = self.getConfig('delete') +        keepbroken  = self.getConfig('keepbroken') + +        extensions   = [x.lstrip('.').lower() for x in toList(self.getConfig('extensions'))] +        excludefiles = toList(self.getConfig('excludefiles')) + +        if extensions: +            self.logDebug("Use for extensions: %s" % "|.".join(extensions)) + +        # reload from txt file +        self.reloadPasswords() + +        download_folder = self.config['general']['download_folder'] + +        # iterate packages -> extractors -> targets +        for pid in ids: +            pypack = self.core.files.getPackage(pid) + +            if not pypack: +                self.queue.remove(pid) +                continue + +            self.logInfo(_("Check package: %s") % pypack.name) + +            # determine output folder +            out = fs_join(download_folder, pypack.folder, destination, "")  #: force trailing slash + +            if subfolder: +                out = fs_join(out, pypack.folder) + +            if not os.path.exists(out): +                os.makedirs(out) + +            matched   = False +            success   = True +            files_ids = dict((pylink['name'],((fs_join(download_folder, pypack.folder, pylink['name'])), pylink['id'], out)) for pylink \ +                        in sorted(pypack.getChildren().itervalues(), key=lambda k: k['name'])).values()  #: remove duplicates + +            # check as long there are unseen files +            while files_ids: +                new_files_ids = [] + +                if extensions: +                    files_ids = [(fname, fid, fout) for fname, fid, fout in files_ids \ +                                 if filter(lambda ext: fname.lower().endswith(ext), extensions)] + +                for Extractor in self.extractors: +                    targets = Extractor.getTargets(files_ids) +                    if targets: +                        self.logDebug("Targets for %s: %s" % (Extractor.__class__.__name__, targets)) +                        matched = True + +                    for fname, fid, fout in targets: +                        name = os.path.basename(fname) + +                        if not os.path.exists(fname): +                            self.logDebug(name, "File not found") +                            continue + +                        self.logInfo(name, _("Extract to: %s") % fout) +                        try: +                            pyfile  = self.core.files.getFile(fid) +                            archive = Extractor(self, +                                                fname, +                                                fout, +                                                fullpath, +                                                overwrite, +                                                excludefiles, +                                                renice, +                                                delete, +                                                keepbroken, +                                                fid) + +                            thread.addActive(pyfile) +                            archive.init() + +                            try: +                                new_files = self._extract(pyfile, archive, pypack.password) + +                            finally: +                                pyfile.setProgress(100) +                                thread.finishFile(pyfile) + +                        except Exception, e: +                            self.logError(name, e) +                            success = False +                            continue + +                        # remove processed file and related multiparts from list +                        files_ids = [(fname, fid, fout) for fname, fid, fout in files_ids \ +                                    if fname not in archive.getDeleteFiles()] +                        self.logDebug("Extracted files: %s" % new_files) +                        self.setPermissions(new_files) + +                        for filename in new_files: +                            file = fs_encode(fs_join(os.path.dirname(archive.filename), filename)) +                            if not os.path.exists(file): +                                self.logDebug("New file %s does not exists" % filename) +                                continue + +                            if recursive and os.path.isfile(file): +                                new_files_ids.append((filename, fid, os.path.dirname(filename)))  #: append as new target + +                        self.manager.dispatchEvent("archive_extracted", pyfile, archive) + +                files_ids = new_files_ids  #: also check extracted files + +            if matched: +                if success: +                    extracted.append(pid) +                    self.manager.dispatchEvent("package_extracted", pypack) + +                else: +                    failed.append(pid) +                    self.manager.dispatchEvent("package_extract_failed", pypack) + +                    self.failed.add(pid) +            else: +                self.logInfo(_("No files found to extract")) + +            if not matched or not success and subfolder: +                try: +                    os.rmdir(out) + +                except OSError: +                    pass + +            self.queue.remove(pid) + +        self.extracting = False +        return True if not failed else False + + +    def _extract(self, pyfile, archive, password): +        name   = os.path.basename(archive.filename) + +        pyfile.setStatus("processing") + +        encrypted = False +        try: +            self.logDebug("Password: %s" % (password or "None provided")) +            passwords = uniqify([password] + self.getPasswords(False)) if self.getConfig('usepasswordfile') else [password] +            for pw in passwords: +                try: +                    if self.getConfig('test') or self.repair: +                        pyfile.setCustomStatus(_("archive testing")) +                        if pw: +                            self.logDebug("Testing with password: %s" % pw) +                        pyfile.setProgress(0) +                        archive.verify(pw) +                        pyfile.setProgress(100) +                    else: +                        archive.check(pw) + +                    self.addPassword(pw) +                    break + +                except PasswordError: +                    if not encrypted: +                        self.logInfo(name, _("Password protected")) +                        encrypted = True + +                except CRCError, e: +                    self.logDebug(name, e) +                    self.logInfo(name, _("CRC Error")) + +                    if self.repair: +                        self.logWarning(name, _("Repairing...")) + +                        pyfile.setCustomStatus(_("archive repairing")) +                        pyfile.setProgress(0) +                        repaired = archive.repair() +                        pyfile.setProgress(100) + +                        if not repaired and not self.getConfig('keepbroken'): +                            raise CRCError("Archive damaged") + +                        self.addPassword(pw) +                        break + +                    raise CRCError("Archive damaged") + +                except ArchiveError, e: +                    raise ArchiveError(e) + +            pyfile.setCustomStatus(_("extracting")) +            pyfile.setProgress(0) + +            if not encrypted or not self.getConfig('usepasswordfile'): +                self.logDebug("Extracting using password: %s" % (password or "None")) +                archive.extract(password) +            else: +                for pw in filter(None, uniqify([password] + self.getPasswords(False))): +                    try: +                        self.logDebug("Extracting using password: %s" % pw) + +                        archive.extract(pw) +                        self.addPassword(pw) +                        break + +                    except PasswordError: +                        self.logDebug("Password was wrong") +                else: +                    raise PasswordError + +            pyfile.setProgress(100) +            pyfile.setStatus("processing") + +            delfiles = archive.getDeleteFiles() +            self.logDebug("Would delete: " + ", ".join(delfiles)) + +            if self.getConfig('delete'): +                self.logInfo(_("Deleting %s files") % len(delfiles)) + +                deltotrash = self.getConfig('deltotrash') +                for f in delfiles: +                    file = fs_encode(f) +                    if not os.path.exists(file): +                        continue + +                    if not deltotrash: +                        os.remove(file) + +                    elif self.trashable: +                        send2trash.send2trash(file) + +                    else: +                        self.logWarning(_("Unable to move %s to trash") % os.path.basename(f)) + +            self.logInfo(name, _("Extracting finished")) +            extracted_files = archive.files or archive.list() + +            return extracted_files + +        except PasswordError: +            self.logError(name, _("Wrong password" if password else "No password found")) + +        except CRCError, e: +            self.logError(name, _("CRC mismatch"), e) + +        except ArchiveError, e: +            self.logError(name, _("Archive error"), e) + +        except Exception, e: +            self.logError(name, _("Unknown error"), e) +            if self.core.debug: +                traceback.print_exc() + +        self.manager.dispatchEvent("archive_extract_failed", pyfile, archive) + +        raise Exception(_("Extract failed")) + + +    @Expose +    def getPasswords(self, reload=True): +        """ List of saved passwords """ +        if reload: +            self.reloadPasswords() + +        return self.passwords + + +    def reloadPasswords(self): +        try: +            passwords = [] + +            file = fs_encode(self.getConfig('passwordfile')) +            with open(file) as f: +                for pw in f.read().splitlines(): +                    passwords.append(pw) + +        except IOError, e: +            self.logError(e) + +        else: +            self.passwords = passwords + + +    @Expose +    def addPassword(self, password): +        """  Adds a password to saved list""" +        try: +            self.passwords = uniqify([password] + self.passwords) + +            file = fs_encode(self.getConfig('passwordfile')) +            with open(file, "wb") as f: +                for pw in self.passwords: +                    f.write(pw + '\n') + +        except IOError, e: +            self.logError(e) + + +    def setPermissions(self, files): +        for f in files: +            if not os.path.exists(f): +                continue + +            try: +                if self.config['permission']['change_file']: +                    if os.path.isfile(f): +                        os.chmod(f, int(self.config['permission']['file'], 8)) + +                    elif os.path.isdir(f): +                        os.chmod(f, int(self.config['permission']['folder'], 8)) + +                if self.config['permission']['change_dl'] and os.name != "nt": +                    uid = getpwnam(self.config['permission']['user'])[2] +                    gid = getgrnam(self.config['permission']['group'])[2] +                    os.chown(f, uid, gid) + +            except Exception, e: +                self.logWarning(_("Setting User and Group failed"), e) diff --git a/pyload/plugin/addon/HotFolder.py b/pyload/plugin/addon/HotFolder.py new file mode 100644 index 000000000..0137514a8 --- /dev/null +++ b/pyload/plugin/addon/HotFolder.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import os +import time + +from shutil import move + +from pyload.plugin.Addon import Addon +from pyload.utils import fs_encode, fs_join + + +class HotFolder(Addon): +    __name__    = "HotFolder" +    __type__    = "addon" +    __version__ = "0.14" + +    __config__ = [("folder"    , "str" , "Folder to observe"    , "container"), +                ("watch_file", "bool", "Observe link file"    , False      ), +                ("keep"      , "bool", "Keep added containers", True       ), +                ("file"      , "str" , "Link file"            , "links.txt")] + +    __description__ = """Observe folder and file for changes and add container and links""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.de")] + + +    def setup(self): +        self.interval = 30 + + +    def activate(self): +        self.initPeriodical() + + +    def periodical(self): +        folder = fs_encode(self.getConfig('folder')) +        file   = fs_encode(self.getConfig('file')) + +        try: +            if not os.path.isdir(os.path.join(folder, "finished")): +                os.makedirs(os.path.join(folder, "finished")) + +            if self.getConfig('watch_file'): +                with open(file, "a+") as f: +                    f.seek(0) +                    content = f.read().strip() + +                if content: +                    f = open(file, "wb") +                    f.close() + +                    name = "%s_%s.txt" % (file, time.strftime("%H-%M-%S_%d%b%Y")) + +                    with open(fs_join(folder, "finished", name), "wb") as f: +                        f.write(content) + +                    self.core.api.addPackage(f.name, [f.name], 1) + +            for f in os.listdir(folder): +                path = os.path.join(folder, f) + +                if not os.path.isfile(path) or f.endswith("~") or f.startswith("#") or f.startswith("."): +                    continue + +                newpath = os.path.join(folder, "finished", f if self.getConfig('keep') else "tmp_" + f) +                move(path, newpath) + +                self.logInfo(_("Added %s from HotFolder") % f) +                self.core.api.addPackage(f, [newpath], 1) + +        except (IOError, OSError), e: +            self.logError(e) diff --git a/pyload/plugin/addon/IRCInterface.py b/pyload/plugin/addon/IRCInterface.py new file mode 100644 index 000000000..170055ee8 --- /dev/null +++ b/pyload/plugin/addon/IRCInterface.py @@ -0,0 +1,430 @@ +# -*- coding: utf-8 -*- + +import re +import socket +import ssl +import time +import traceback + +from pycurl import FORM_FILE +from select import select +from threading import Thread + +from pyload.api import PackageDoesNotExists, FileDoesNotExists +from pyload.network.RequestFactory import getURL +from pyload.plugin.Addon import Addon +from pyload.utils import formatSize + + +class IRCInterface(Thread, Addon): +    __name__    = "IRCInterface" +    __type__    = "addon" +    __version__ = "0.13" + +    __config__ = [("host"     , "str" , "IRC-Server Address"                           , "Enter your server here!"), +                ("port"     , "int" , "IRC-Server Port"                              , 6667                     ), +                ("ident"    , "str" , "Clients ident"                                , "pyload-irc"             ), +                ("realname" , "str" , "Realname"                                     , "pyload-irc"             ), +                ("ssl"      , "bool", "Use SSL"                                      , False                    ), +                ("nick"     , "str" , "Nickname the Client will take"                , "pyLoad-IRC"             ), +                ("owner"    , "str" , "Nickname the Client will accept commands from", "Enter your nick here!"  ), +                ("info_file", "bool", "Inform about every file finished"             , False                    ), +                ("info_pack", "bool", "Inform about every package finished"          , True                     ), +                ("captcha"  , "bool", "Send captcha requests"                        , True                     )] + +    __description__ = """Connect to irc and let owner perform different tasks""" +    __license__     = "GPLv3" +    __authors__     = [("Jeix", "Jeix@hasnomail.com")] + + +    def __init__(self, core, manager): +        Thread.__init__(self) +        Addon.__init__(self, core, manager) +        self.setDaemon(True) + + +    def activate(self): +        self.abort = False +        self.more = [] +        self.new_package = {} + +        self.start() + + +    def packageFinished(self, pypack): +        try: +            if self.getConfig('info_pack'): +                self.response(_("Package finished: %s") % pypack.name) +        except Exception: +            pass + + +    def downloadFinished(self, pyfile): +        try: +            if self.getConfig('info_file'): +                self.response( +                    _("Download finished: %(name)s @ %(plugin)s ") % {"name": pyfile.name, "plugin": pyfile.pluginname}) +        except Exception: +            pass + + +    def captchaTask(self, task): +        if self.getConfig('captcha') and task.isTextual(): +            task.handler.append(self) +            task.setWaiting(60) + +            html = getURL("http://www.freeimagehosting.net/upload.php", +                          post={"attached": (FORM_FILE, task.captchaFile)}, multipart=True) + +            url = re.search(r"\[img\]([^\[]+)\[/img\]\[/url\]", html).group(1) +            self.response(_("New Captcha Request: %s") % url) +            self.response(_("Answer with 'c %s text on the captcha'") % task.id) + + +    def run(self): +        # connect to IRC etc. +        self.sock = socket.socket() +        host = self.getConfig('host') +        self.sock.connect((host, self.getConfig('port'))) + +        if self.getConfig('ssl'): +            self.sock = ssl.wrap_socket(self.sock, cert_reqs=ssl.CERT_NONE)  #@TODO: support certificate + +        nick = self.getConfig('nick') +        self.sock.send("NICK %s\r\n" % nick) +        self.sock.send("USER %s %s bla :%s\r\n" % (nick, host, nick)) +        for t in self.getConfig('owner').split(): +            if t.strip().startswith("#"): +                self.sock.send("JOIN %s\r\n" % t.strip()) +        self.logInfo(_("Connected to"), host) +        self.logInfo(_("Switching to listening mode!")) +        try: +            self.main_loop() + +        except IRCError, ex: +            self.sock.send("QUIT :byebye\r\n") +            traceback.print_exc() +            self.sock.close() + + +    def main_loop(self): +        readbuffer = "" +        while True: +            time.sleep(1) +            fdset = select([self.sock], [], [], 0) +            if self.sock not in fdset[0]: +                continue + +            if self.abort: +                raise IRCError("quit") + +            readbuffer += self.sock.recv(1024) +            temp = readbuffer.split("\n") +            readbuffer = temp.pop() + +            for line in temp: +                line = line.rstrip() +                first = line.split() + +                if first[0] == "PING": +                    self.sock.send("PONG %s\r\n" % first[1]) + +                if first[0] == "ERROR": +                    raise IRCError(line) + +                msg = line.split(None, 3) +                if len(msg) < 4: +                    continue + +                msg = { +                    "origin": msg[0][1:], +                    "action": msg[1], +                    "target": msg[2], +                    "text": msg[3][1:] +                } + +                self.handle_events(msg) + + +    def handle_events(self, msg): +        if not msg['origin'].split("!", 1)[0] in self.getConfig('owner').split(): +            return + +        if msg['target'].split("!", 1)[0] != self.getConfig('nick'): +            return + +        if msg['action'] != "PRIVMSG": +            return + +        # HANDLE CTCP ANTI FLOOD/BOT PROTECTION +        if msg['text'] == "\x01VERSION\x01": +            self.logDebug("Sending CTCP VERSION") +            self.sock.send("NOTICE %s :%s\r\n" % (msg['origin'], "pyLoad! IRC Interface")) +            return +        elif msg['text'] == "\x01TIME\x01": +            self.logDebug("Sending CTCP TIME") +            self.sock.send("NOTICE %s :%d\r\n" % (msg['origin'], time.time())) +            return +        elif msg['text'] == "\x01LAG\x01": +            self.logDebug("Received CTCP LAG")  #: don't know how to answer +            return + +        trigger = "pass" +        args = None + +        try: +            temp = msg['text'].split() +            trigger = temp[0] +            if len(temp) > 1: +                args = temp[1:] +        except Exception: +            pass + +        handler = getattr(self, "event_%s" % trigger, self.event_pass) +        try: +            res = handler(args) +            for line in res: +                self.response(line, msg['origin']) +        except Exception, e: +            self.logError(e) + + +    def response(self, msg, origin=""): +        if origin == "": +            for t in self.getConfig('owner').split(): +                self.sock.send("PRIVMSG %s :%s\r\n" % (t.strip(), msg)) +        else: +            self.sock.send("PRIVMSG %s :%s\r\n" % (origin.split("!", 1)[0], msg)) + + +        #### Events + +    def event_pass(self, args): +        return [] + + +    def event_status(self, args): +        downloads = self.core.api.statusDownloads() +        if not downloads: +            return ["INFO: There are no active downloads currently."] + +        temp_progress = "" +        lines = ["ID - Name - Status - Speed - ETA - Progress"] +        for data in downloads: + +            if data.status == 5: +                temp_progress = data.format_wait +            else: +                temp_progress = "%d%% (%s)" % (data.percent, data.format_size) + +            lines.append("#%d - %s - %s - %s - %s - %s" % +                         ( +                             data.fid, +                             data.name, +                             data.statusmsg, +                             "%s/s" % formatSize(data.speed), +                             "%s" % data.format_eta, +                             temp_progress +                         )) +        return lines + + +    def event_queue(self, args): +        ps = self.core.api.getQueueData() + +        if not ps: +            return ["INFO: There are no packages in queue."] + +        lines = [] +        for pack in ps: +            lines.append('PACKAGE #%s: "%s" with %d links.' % (pack.pid, pack.name, len(pack.links))) + +        return lines + + +    def event_collector(self, args): +        ps = self.core.api.getCollectorData() +        if not ps: +            return ["INFO: No packages in collector!"] + +        lines = [] +        for pack in ps: +            lines.append('PACKAGE #%s: "%s" with %d links.' % (pack.pid, pack.name, len(pack.links))) + +        return lines + + +    def event_info(self, args): +        if not args: +            return ["ERROR: Use info like this: info <id>"] + +        info = None +        try: +            info = self.core.api.getFileData(int(args[0])) + +        except FileDoesNotExists: +            return ["ERROR: Link doesn't exists."] + +        return ['LINK #%s: %s (%s) [%s][%s]' % (info.fid, info.name, info.format_size, info.statusmsg, info.plugin)] + + +    def event_packinfo(self, args): +        if not args: +            return ["ERROR: Use packinfo like this: packinfo <id>"] + +        lines = [] +        pack = None +        try: +            pack = self.core.api.getPackageData(int(args[0])) + +        except PackageDoesNotExists: +            return ["ERROR: Package doesn't exists."] + +        id = args[0] + +        self.more = [] + +        lines.append('PACKAGE #%s: "%s" with %d links' % (id, pack.name, len(pack.links))) +        for pyfile in pack.links: +            self.more.append('LINK #%s: %s (%s) [%s][%s]' % (pyfile.fid, pyfile.name, pyfile.format_size, +                                                             pyfile.statusmsg, pyfile.plugin)) + +        if len(self.more) < 6: +            lines.extend(self.more) +            self.more = [] +        else: +            lines.extend(self.more[:6]) +            self.more = self.more[6:] +            lines.append("%d more links do display." % len(self.more)) + +        return lines + + +    def event_more(self, args): +        if not self.more: +            return ["No more information to display."] + +        lines = self.more[:6] +        self.more = self.more[6:] +        lines.append("%d more links do display." % len(self.more)) + +        return lines + + +    def event_start(self, args): +        self.core.api.unpauseServer() +        return ["INFO: Starting downloads."] + + +    def event_stop(self, args): +        self.core.api.pauseServer() +        return ["INFO: No new downloads will be started."] + + +    def event_add(self, args): +        if len(args) < 2: +            return ['ERROR: Add links like this: "add <packagename|id> links". ', +                    "This will add the link <link> to to the package <package> / the package with id <id>!"] + +        pack = args[0].strip() +        links = [x.strip() for x in args[1:]] + +        count_added = 0 +        count_failed = 0 +        try: +            id = int(pack) +            pack = self.core.api.getPackageData(id) +            if not pack: +                return ["ERROR: Package doesn't exists."] + +            #TODO add links + +            return ["INFO: Added %d links to Package %s [#%d]" % (len(links), pack['name'], id)] + +        except Exception: +            # create new package +            id = self.core.api.addPackage(pack, links, 1) +            return ["INFO: Created new Package %s [#%d] with %d links." % (pack, id, len(links))] + + +    def event_del(self, args): +        if len(args) < 2: +            return ["ERROR: Use del command like this: del -p|-l <id> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"] + +        if args[0] == "-p": +            ret = self.core.api.deletePackages(map(int, args[1:])) +            return ["INFO: Deleted %d packages!" % len(args[1:])] + +        elif args[0] == "-l": +            ret = self.core.api.delLinks(map(int, args[1:])) +            return ["INFO: Deleted %d links!" % len(args[1:])] + +        else: +            return ["ERROR: Use del command like this: del <-p|-l> <id> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"] + + +    def event_push(self, args): +        if not args: +            return ["ERROR: Push package to queue like this: push <package id>"] + +        id = int(args[0]) +        try: +            info = self.core.api.getPackageInfo(id) +        except PackageDoesNotExists: +            return ["ERROR: Package #%d does not exist." % id] + +        self.core.api.pushToQueue(id) +        return ["INFO: Pushed package #%d to queue." % id] + + +    def event_pull(self, args): +        if not args: +            return ["ERROR: Pull package from queue like this: pull <package id>."] + +        id = int(args[0]) +        if not self.core.api.getPackageData(id): +            return ["ERROR: Package #%d does not exist." % id] + +        self.core.api.pullFromQueue(id) +        return ["INFO: Pulled package #%d from queue to collector." % id] + + +    def event_c(self, args): +        """ captcha answer """ +        if not args: +            return ["ERROR: Captcha ID missing."] + +        task = self.core.captchaManager.getTaskByID(args[0]) +        if not task: +            return ["ERROR: Captcha Task with ID %s does not exists." % args[0]] + +        task.setResult(" ".join(args[1:])) +        return ["INFO: Result %s saved." % " ".join(args[1:])] + + +    def event_help(self, args): +        lines = ["The following commands are available:", +                 "add <package|packid> <links> [...] Adds link to package. (creates new package if it does not exist)", +                 "queue                       Shows all packages in the queue", +                 "collector                   Shows all packages in collector", +                 "del -p|-l <id> [...]        Deletes all packages|links with the ids specified", +                 "info <id>                   Shows info of the link with id <id>", +                 "packinfo <id>               Shows info of the package with id <id>", +                 "more                        Shows more info when the result was truncated", +                 "start                       Starts all downloads", +                 "stop                        Stops the download (but not abort active downloads)", +                 "push <id>                   Push package to queue", +                 "pull <id>                   Pull package from queue", +                 "status                      Show general download status", +                 "help                        Shows this help message"] +        return lines + + +class IRCError(Exception): + +    def __init__(self, value): +        self.value = value + + +    def __str__(self): +        return repr(self.value) diff --git a/pyload/plugin/addon/JustPremium.py b/pyload/plugin/addon/JustPremium.py new file mode 100644 index 000000000..b878f302d --- /dev/null +++ b/pyload/plugin/addon/JustPremium.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Addon import Addon + + +class JustPremium(Addon): +    __name__    = "JustPremium" +    __type__    = "addon" +    __version__ = "0.22" + +    __config__ = [("excluded", "str", "Exclude hosters (comma separated)", ""), +                  ("included", "str", "Include hosters (comma separated)", "")] + +    __description__ = """Remove not-premium links from added urls""" +    __license__     = "GPLv3" +    __authors__     = [("mazleu"        , "mazleica@gmail.com"), +                       ("Walter Purcaro", "vuolter@gmail.com" ), +                       ("immenz"        , "immenz@gmx.net"    )] + + +    event_list = ["linksAdded"] + + +    def linksAdded(self, links, pid): +        hosterdict = self.core.pluginManager.hosterPlugins +        linkdict   = self.core.api.checkURLs(links) + +        premiumplugins = set(account.type for account in self.core.api.getAccounts(False) \ +                             if account.valid and account.premium) +        multihosters   = set(hoster for hoster in self.core.pluginManager.hosterPlugins \ +                             if 'new_name' in hosterdict[hoster] \ +                             and hosterdict[hoster]['new_name'] in premiumplugins) + +        excluded = map(lambda domain: "".join(part.capitalize() for part in re.split(r'(\.|\d+)', domain) if part != '.'), +                       self.getConfig('excluded').replace(' ', '').replace(',', '|').replace(';', '|').split('|')) +        included = map(lambda domain: "".join(part.capitalize() for part in re.split(r'(\.|\d+)', domain) if part != '.'), +                       self.getConfig('included').replace(' ', '').replace(',', '|').replace(';', '|').split('|')) + +        hosterlist = (premiumplugins | multihosters).union(excluded).difference(included) + +        #: Found at least one hoster with account or multihoster +        if not any( True for pluginname in linkdict if pluginname in hosterlist ): +            return + +        for pluginname in set(linkdict.keys()) - hosterlist: +            self.logInfo(_("Remove links of plugin: %s") % pluginname) +            for link in linkdict[pluginname]: +                self.logDebug("Remove link: %s" % link) +                links.remove(link) diff --git a/pyload/plugin/addon/MergeFiles.py b/pyload/plugin/addon/MergeFiles.py new file mode 100644 index 000000000..ee6a86d9f --- /dev/null +++ b/pyload/plugin/addon/MergeFiles.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import os +import re +import traceback + +from pyload.plugin.Addon import Addon, threaded +from pyload.utils import fs_join + + +class MergeFiles(Addon): +    __name__    = "MergeFiles" +    __type__    = "addon" +    __version__ = "0.14" + +    __config__ = [("activated", "bool", "Activated", True)] + +    __description__ = """Merges parts splitted with hjsplit""" +    __license__     = "GPLv3" +    __authors__     = [("and9000", "me@has-no-mail.com")] + + +    BUFFER_SIZE = 4096 + + +    @threaded +    def packageFinished(self, pack): +        files = {} +        fid_dict = {} +        for fid, data in pack.getChildren().iteritems(): +            if re.search("\.\d{3}$", data['name']): +                if data['name'][:-4] not in files: +                    files[data['name'][:-4]] = [] +                files[data['name'][:-4]].append(data['name']) +                files[data['name'][:-4]].sort() +                fid_dict[data['name']] = fid + +        download_folder = self.config['general']['download_folder'] + +        if self.config['general']['folder_per_package']: +            download_folder = fs_join(download_folder, pack.folder) + +        for name, file_list in files.iteritems(): +            self.logInfo(_("Starting merging of"), name) + +            with open(fs_join(download_folder, name), "wb") as final_file: +                for splitted_file in file_list: +                    self.logDebug("Merging part", splitted_file) + +                    pyfile = self.core.files.getFile(fid_dict[splitted_file]) + +                    pyfile.setStatus("processing") + +                    try: +                        with open(fs_join(download_folder, splitted_file), "rb") as s_file: +                            size_written = 0 +                            s_file_size = int(os.path.getsize(os.path.join(download_folder, splitted_file))) + +                            while True: +                                f_buffer = s_file.read(self.BUFFER_SIZE) +                                if f_buffer: +                                    final_file.write(f_buffer) +                                    size_written += self.BUFFER_SIZE +                                    pyfile.setProgress((size_written * 100) / s_file_size) +                                else: +                                    break + +                        self.logDebug("Finished merging part", splitted_file) + +                    except Exception, e: +                        traceback.print_exc() + +                    finally: +                        pyfile.setProgress(100) +                        pyfile.setStatus("finished") +                        pyfile.release() + +            self.logInfo(_("Finished merging of"), name) diff --git a/pyload/plugin/addon/MultiHome.py b/pyload/plugin/addon/MultiHome.py new file mode 100644 index 000000000..03974d6c6 --- /dev/null +++ b/pyload/plugin/addon/MultiHome.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- + +import time + +from pyload.plugin.Addon import Addon + + +class MultiHome(Addon): +    __name__    = "MultiHome" +    __type__    = "addon" +    __version__ = "0.12" + +    __config__ = [("interfaces", "str", "Interfaces", "None")] + +    __description__ = """Ip address changer""" +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de")] + + +    def setup(self): +        self.register   = {} +        self.interfaces = [] + +        self.parseInterfaces(self.getConfig('interfaces').split(";")) + +        if not self.interfaces: +            self.parseInterfaces([self.config['download']['interface']]) +            self.setConfig("interfaces", self.toConfig()) + + +    def toConfig(self): +        return ";".join(i.adress for i in self.interfaces) + + +    def parseInterfaces(self, interfaces): +        for interface in interfaces: +            if not interface or str(interface).lower() == "none": +                continue +            self.interfaces.append(Interface(interface)) + + +    def activate(self): +        requestFactory = self.core.requestFactory +        oldGetRequest = requestFactory.getRequest + +        def getRequest(pluginName, account=None): +            iface = self.bestInterface(pluginName, account) +            if iface: +                iface.useFor(pluginName, account) +                requestFactory.iface = lambda: iface.adress +                self.logDebug("Using address", iface.adress) +            return oldGetRequest(pluginName, account) + +        requestFactory.getRequest = getRequest + + +    def bestInterface(self, pluginName, account): +        best = None +        for interface in self.interfaces: +            if not best or interface.lastPluginAccess(pluginName, account) < best.lastPluginAccess(pluginName, account): +                best = interface +        return best + + +class Interface(object): + +    def __init__(self, adress): +        self.adress = adress +        self.history = {} + + +    def lastPluginAccess(self, pluginName, account): +        if (pluginName, account) in self.history: +            return self.history[(pluginName, account)] +        return 0 + + +    def useFor(self, pluginName, account): +        self.history[(pluginName, account)] = time.time() + + +    def __repr__(self): +        return "<Interface - %s>" % self.adress diff --git a/pyload/plugin/addon/RestartFailed.py b/pyload/plugin/addon/RestartFailed.py new file mode 100644 index 000000000..0b8f4d077 --- /dev/null +++ b/pyload/plugin/addon/RestartFailed.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Addon import Addon + + +class RestartFailed(Addon): +    __name__    = "RestartFailed" +    __type__    = "addon" +    __version__ = "1.58" + +    __config__ = [("activated", "bool", "Activated"                , True), +                ("interval" , "int" , "Check interval in minutes", 90  )] + +    __description__ = """Restart all the failed downloads in queue""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    # event_list = ["pluginConfigChanged"] + +    MIN_CHECK_INTERVAL = 15 * 60  #: 15 minutes + + +    # def pluginConfigChanged(self, plugin, name, value): +        # if name == "interval": +            # interval = value * 60 +            # if self.MIN_CHECK_INTERVAL <= interval != self.interval: +                # self.core.scheduler.removeJob(self.cb) +                # self.interval = interval +                # self.initPeriodical() +            # else: +                # self.logDebug("Invalid interval value, kept current") + + +    def periodical(self): +        self.logDebug(_("Restart failed downloads")) +        self.core.api.restartFailed() + + +    def activate(self): +        # self.pluginConfigChanged(self.__class__.__name__, "interval", self.getConfig('interval')) +        self.interval = max(self.MIN_CHECK_INTERVAL, self.getConfig('interval') * 60) +        self.initPeriodical() diff --git a/pyload/plugin/addon/SkipRev.py b/pyload/plugin/addon/SkipRev.py new file mode 100644 index 000000000..1c42ddfd8 --- /dev/null +++ b/pyload/plugin/addon/SkipRev.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- + +import re + +from types import MethodType +from urllib import unquote +from urlparse import urlparse + +from pyload.datatype.File import PyFile +from pyload.plugin.Addon import Addon +from pyload.plugin.Plugin import SkipDownload + + +class SkipRev(Addon): +    __name__    = "SkipRev" +    __type__    = "addon" +    __version__ = "0.29" + +    __config__ = [("mode"     , "Auto;Manual", "Choose recovery archives to skip"               , "Auto"), +                  ("revtokeep", "int"        , "Number of recovery archives to keep for package", 0     )] + +    __description__ = """Skip recovery archives (.rev)""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    @staticmethod +    def _setup(self): +        self.pyfile.plugin._setup() +        if self.pyfile.hasStatus("skipped"): +            raise SkipDownload(self.pyfile.statusname or self.pyfile.pluginname) + + +    def _name(self, pyfile): +        if hasattr(pyfile.pluginmodule, "getInfo"):  #@NOTE: getInfo is deprecated in 0.4.10 +            return pyfile.pluginmodule.getInfo([pyfile.url]).next()[0] +        else: +            self.logWarning("Unable to grab file name") +            return urlparse(unquote(pyfile.url)).path.split('/')[-1] + + +    def _pyfile(self, link): +        return PyFile(self.core.files, +                      link.fid, +                      link.url, +                      link.name, +                      link.size, +                      link.status, +                      link.error, +                      link.plugin, +                      link.packageID, +                      link.order) + + +    def downloadPreparing(self, pyfile): +        name = self._name(pyfile) + +        if pyfile.statusname is _("unskipped") or not name.endswith(".rev") or not ".part" in name: +            return + +        revtokeep = -1 if self.getConfig('mode') == "Auto" else self.getConfig('revtokeep') + +        if revtokeep: +            status_list = (1, 4, 8, 9, 14) if revtokeep < 0 else (1, 3, 4, 8, 9, 14) +            pyname      = re.compile(r'%s\.part\d+\.rev$' % name.rsplit('.', 2)[0].replace('.', '\.')) + +            queued = [True for link in self.core.api.getPackageData(pyfile.package().id).links \ +                      if link.status not in status_list and pyname.match(link.name)].count(True) + +            if not queued or queued < revtokeep:  #: keep one rev at least in auto mode +                return + +        pyfile.setCustomStatus("SkipRev", "skipped") + +        if not hasattr(pyfile.plugin, "_setup"): +            # Work-around: inject status checker inside the preprocessing routine of the plugin +            pyfile.plugin._setup = pyfile.plugin.setup +            pyfile.plugin.setup  = MethodType(self._setup, pyfile.plugin) + + +    def downloadFailed(self, pyfile): +        #: Check if pyfile is still "failed", +        #  maybe might has been restarted in meantime +        if pyfile.status != 8 or pyfile.name.rsplit('.', 1)[-1].strip() not in ("rar", "rev"): +            return + +        revtokeep = -1 if self.getConfig('mode') == "Auto" else self.getConfig('revtokeep') + +        if not revtokeep: +            return + +        pyname = re.compile(r'%s\.part\d+\.rev$' % pyfile.name.rsplit('.', 2)[0].replace('.', '\.')) + +        for link in self.core.api.getPackageData(pyfile.package().id).links: +            if link.status is 4 and pyname.match(link.name): +                pylink = self._pyfile(link) + +                if revtokeep > -1 or pyfile.name.endswith(".rev"): +                    pylink.setStatus("queued") +                else: +                    pylink.setCustomStatus(_("unskipped"), "queued") + +                self.core.files.save() +                pylink.release() +                return diff --git a/pyload/plugin/addon/UnSkipOnFail.py b/pyload/plugin/addon/UnSkipOnFail.py new file mode 100644 index 000000000..f81066daa --- /dev/null +++ b/pyload/plugin/addon/UnSkipOnFail.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- + +from pyload.datatype.File import PyFile +from pyload.plugin.Addon import Addon + + +class UnSkipOnFail(Addon): +    __name__    = "UnSkipOnFail" +    __type__    = "addon" +    __version__ = "0.05" + +    __config__ = [("activated", "bool", "Activated", True)] + +    __description__ = """Restart skipped duplicates when download fails""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    def downloadFailed(self, pyfile): +        #: Check if pyfile is still "failed", +        #  maybe might has been restarted in meantime +        if pyfile.status != 8: +            return + +        msg = _("Looking for skipped duplicates of: %s (pid:%s)") +        self.logInfo(msg % (pyfile.name, pyfile.package().id)) + +        dup = self.findDuplicate(pyfile) +        if dup: +            self.logInfo(_("Queue found duplicate: %s (pid:%s)") % (dup.name, dup.packageID)) + +            #: Change status of "link" to "new_status". +            #  "link" has to be a valid FileData object, +            #  "new_status" has to be a valid status name +            #  (i.e. "queued" for this Plugin) +            #  It creates a temporary PyFile object using +            #  "link" data, changes its status, and tells +            #  the core.files-manager to save its data. +            pylink = _pyfile(link) + +            pylink.setCustomStatus(_("unskipped"), "queued") + +            self.core.files.save() +            pylink.release() + +        else: +            self.logInfo(_("No duplicates found")) + + +    def findDuplicate(self, pyfile): +        """ Search all packages for duplicate links to "pyfile". +            Duplicates are links that would overwrite "pyfile". +            To test on duplicity the package-folder and link-name +            of twolinks are compared (link.name). +            So this method returns a list of all links with equal +            package-folders and filenames as "pyfile", but except +            the data for "pyfile" iotselöf. +            It does MOT check the link's status. +        """ +        queue = self.core.api.getQueue()  #: get packages (w/o files, as most file data is useless here) + +        for package in queue: +            #: check if package-folder equals pyfile's package folder +            if package.folder != pyfile.package().folder: +                continue + +            #: now get packaged data w/ files/links +            pdata = self.core.api.getPackageData(package.pid) +            for link in pdata.links: +                #: check if link is "skipped" +                if link.status != 4: +                    continue + +                #: check if link name collides with pdata's name +                #: AND at last check if it is not pyfile itself +                if link.name == pyfile.name and link.fid != pyfile.id: +                    return link + + +    def _pyfile(self, link): +        return PyFile(self.core.files, +                      link.fid, +                      link.url, +                      link.name, +                      link.size, +                      link.status, +                      link.error, +                      link.plugin, +                      link.packageID, +                      link.order) diff --git a/pyload/plugin/addon/UpdateManager.py b/pyload/plugin/addon/UpdateManager.py new file mode 100644 index 000000000..c5bee16a1 --- /dev/null +++ b/pyload/plugin/addon/UpdateManager.py @@ -0,0 +1,308 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import os +import re +import sys +import time + +from operator import itemgetter + +from pyload.network.RequestFactory import getURL +from pyload.plugin.Addon import Expose, Addon, threaded +from pyload.utils import fs_join +from pyload import __status_code__ as release_status + + +# Case-sensitive os.path.exists +def exists(path): +    if os.path.exists(path): +        if os.name == 'nt': +            dir, name = os.path.split(path) +            return name in os.listdir(dir) +        else: +            return True +    else: +        return False + + +class UpdateManager(Addon): +    __name__    = "UpdateManager" +    __type__    = "addon" +    __version__ = "0.50" + +    __config__  = [("activated", "bool", "Activated", True), +                 ("checkinterval", "int", "Check interval in hours", 8), +                 ("autorestart", "bool", +                  "Auto-restart pyLoad when required", True), +                 ("checkonstart", "bool", "Check for updates on startup", True), +                 ("checkperiod", "bool", +                  "Check for updates periodically", True), +                 ("reloadplugins", "bool", +                  "Monitor plugin code changes in debug mode", True), +                 ("nodebugupdate", "bool", "Don't update plugins in debug mode", False)] + +    __description__ = """ Check for updates """ +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + +    SERVER_URL         = "http://updatemanager.pyload.org" if release_status == 5 else None +    MIN_CHECK_INTERVAL = 3 * 60 * 60  #: 3 hours + +    def activate(self): +        if self.checkonstart: +            self.update() + +        self.initPeriodical() + +    def setup(self): +        self.interval = 10 +        self.info     = {'pyload': False, 'version': None, 'plugins': False, 'last_check': time.time()} +        self.mtimes   = {}  #: store modification time for each plugin + +        if self.getConfig('checkonstart'): +            self.core.api.pauseServer() +            self.checkonstart = True +        else: +            self.checkonstart = False + +    def periodical(self): +        if self.core.debug: +            if self.getConfig('reloadplugins'): +                self.autoreloadPlugins() + +            if self.getConfig('nodebugupdate'): +                return + +        if self.getConfig('checkperiod') \ +           and time.time() - max(self.MIN_CHECK_INTERVAL, self.getConfig('checkinterval') * 60 * 60) > self.info['last_check']: +            self.update() + +    @Expose +    def autoreloadPlugins(self): +        """ reload and reindex all modified plugins """ +        modules = filter( +            lambda m: m and (m.__name__.startswith("module.plugins.") or +                             m.__name__.startswith("userplugins.")) and +            m.__name__.count(".") >= 2, sys.modules.itervalues() +        ) + +        reloads = [] + +        for m in modules: +            root, type, name = m.__name__.rsplit(".", 2) +            id = (type, name) +            if type in self.core.pluginManager.plugins: +                f = m.__file__.replace(".pyc", ".py") +                if not os.path.isfile(f): +                    continue + +                mtime = os.stat(f).st_mtime + +                if id not in self.mtimes: +                    self.mtimes[id] = mtime +                elif self.mtimes[id] < mtime: +                    reloads.append(id) +                    self.mtimes[id] = mtime + +        return True if self.core.pluginManager.reloadPlugins(reloads) else False + +    def server_response(self): +        try: +            return getURL(self.SERVER_URL, get={'v': self.core.api.getServerVersion()}).splitlines() + +        except Exception: +            self.logWarning(_("Unable to retrieve server to get updates")) + +    @Expose +    @threaded +    def update(self): +        """ check for updates """ + +        self.core.api.pauseServer() + +        if self._update() is 2 and self.getConfig('autorestart'): +            self.core.api.restart() +        else: +            self.core.api.unpauseServer() + +    def _update(self): +        data = self.server_response() + +        self.info['last_check'] = time.time() + +        if not data: +            exitcode = 0 + +        elif data[0] == "None": +            self.logInfo(_("No new pyLoad version available")) +            exitcode = self._updatePlugins(data[1:]) + +        elif onlyplugin: +            exitcode = 0 + +        else: +            self.logInfo(_("***  New pyLoad Version %s available  ***") % data[0]) +            self.logInfo(_("***  Get it here: https://github.com/pyload/pyload/releases  ***")) +            self.info['pyload']  = True +            self.info['version'] = data[0] +            exitcode = 3 + +        # Exit codes: +        # -1 = No plugin updated, new pyLoad version available +        #  0 = No plugin updated +        #  1 = Plugins updated +        #  2 = Plugins updated, but restart required +        return exitcode + +    def _updatePlugins(self, data): +        """ check for plugin updates """ + +        exitcode = 0 +        updated  = [] + +        url    = data[0] +        schema = data[1].split('|') + +        VERSION = re.compile(r'__version__.*=.*("|\')([\d.]+)') + +        if "BLACKLIST" in data: +            blacklist  = data[data.index('BLACKLIST') + 1:] +            updatelist = data[2:data.index('BLACKLIST')] +        else: +            blacklist  = [] +            updatelist = data[2:] + +        updatelist = [dict(zip(schema, x.split('|'))) for x in updatelist] +        blacklist  = [dict(zip(schema, x.split('|'))) for x in blacklist] + +        if blacklist: +            type_plugins = [(plugin['type'], plugin['name'].rsplit('.', 1)[0]) for plugin in blacklist] + +            # Protect UpdateManager from self-removing +            try: +                type_plugins.remove(("addon", "UpdateManager")) +            except ValueError: +                pass + +            for t, n in type_plugins: +                for idx, plugin in enumerate(updatelist): +                    if n == plugin['name'] and t == plugin['type']: +                        updatelist.pop(idx) +                        break + +            for t, n in self.removePlugins(sorted(type_plugins)): +                self.logInfo(_("Removed blacklisted plugin: [%(type)s] %(name)s") % { +                    'type': t, +                    'name': n, +                }) + +        for plugin in sorted(updatelist, key=itemgetter("type", "name")): +            filename = plugin['name'] +            prefix   = plugin['type'] +            version  = plugin['version'] + +            if filename.endswith(".pyc"): +                name = filename[:filename.find("_")] +            else: +                name = filename.replace(".py", "") + +            #@TODO: Remove in 0.4.10 +            if prefix.endswith("s"): +                type = prefix[:-1] +            else: +                type = prefix + +            plugins = getattr(self.core.pluginManager, "%sPlugins" % type) + +            oldver = float(plugins[name]['version']) if name in plugins else None +            newver = float(version) + +            if not oldver: +                msg = "New plugin: [%(type)s] %(name)s (v%(newver).2f)" +            elif newver > oldver: +                msg = "New version of plugin: [%(type)s] %(name)s (v%(oldver).2f -> v%(newver).2f)" +            else: +                continue + +            self.logInfo(_(msg) % {'type': type, +                                   'name': name, +                                   'oldver': oldver, +                                   'newver': newver}) +            try: +                content = getURL(url % plugin) +                m = VERSION.search(content) + +                if m and m.group(2) == version: +                    with open(fs_join("userplugins", type, filename), "wb") as f: +                        f.write(content) + +                    updated.append((type, name)) +                else: +                    raise Exception, _("Version mismatch") + +            except Exception, e: +                self.logError(_("Error updating plugin: %s") % filename, e) + +        if updated: +            self.logInfo(_("*** Plugins updated ***")) + +            if self.core.pluginManager.reloadPlugins(updated): +                exitcode = 1 +            else: +                self.logWarning(_("Restart pyLoad to reload the updated plugins")) +                self.info['plugins'] = True +                exitcode = 2 + +            self.manager.dispatchEvent("plugin_updated", updated) +        else: +            self.logInfo(_("No plugin updates available")) + +        # Exit codes: +        # 0 = No plugin updated +        # 1 = Plugins updated +        # 2 = Plugins updated, but restart required +        return exitcode + +    @Expose +    def removePlugins(self, type_plugins): +        """ delete plugins from disk """ + +        if not type_plugins: +            return + +        removed = set() + +        self.logDebug("Requested deletion of plugins: %s" % type_plugins) + +        for type, name in type_plugins: +            rootplugins = os.path.join(pypath, "module", "plugins") + +            for dir in ("userplugins", rootplugins): +                py_filename  = fs_join(dir, type, name + ".py") +                pyc_filename = py_filename + "c" + +                if type == "addon": +                    try: +                        self.manager.deactivateAddon(name) + +                    except Exception, e: +                        self.logDebug(e) + +                for filename in (py_filename, pyc_filename): +                    if not exists(filename): +                        continue + +                    try: +                        os.remove(filename) + +                    except OSError, e: +                        self.logError(_("Error removing: %s") % filename, e) + +                    else: +                        id = (type, name) +                        removed.add(id) + +        #: return a list of the plugins successfully removed +        return list(removed) diff --git a/pyload/plugin/addon/WindowsPhoneNotify.py b/pyload/plugin/addon/WindowsPhoneNotify.py new file mode 100644 index 000000000..341e682b2 --- /dev/null +++ b/pyload/plugin/addon/WindowsPhoneNotify.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- + +import httplib +import time + +from pyload.plugin.Addon import Addon, Expose + + +class WindowsPhoneNotify(Addon): +    __name__    = "WindowsPhoneNotify" +    __type__    = "addon" +    __version__ = "0.09" + +    __config__ = [("id"             , "str" , "Push ID"                                  , ""   ), +                  ("url"            , "str" , "Push url"                                 , ""   ), +                  ("notifycaptcha"  , "bool", "Notify captcha request"                   , True ), +                  ("notifypackage"  , "bool", "Notify package finished"                  , True ), +                  ("notifyprocessed", "bool", "Notify packages processed"                , True ), +                  ("notifyupdate"   , "bool", "Notify plugin updates"                    , True ), +                  ("notifyexit"     , "bool", "Notify pyLoad shutdown"                   , True ), +                  ("sendtimewait"   , "int" , "Timewait in seconds between notifications", 5    ), +                  ("sendpermin"     , "int" , "Max notifications per minute"             , 12   ), +                  ("ignoreclient"   , "bool", "Send notifications if client is connected", False)] + +    __description__ = """Send push notifications to Windows Phone""" +    __license__     = "GPLv3" +    __authors__     = [("Andy Voigt"    , "phone-support@hotmail.de"), +                       ("Walter Purcaro", "vuolter@gmail.com"       )] + + +    event_list = ["allDownloadsProcessed", "plugin_updated"] + + +    def setup(self): +        self.last_notify   = 0 +        self.notifications = 0 + + +    def plugin_updated(self, type_plugins): +        if not self.getConfig('notifyupdate'): +            return + +        self.notify(_("Plugins updated"), str(type_plugins)) + + +    def exit(self): +        if not self.getConfig('notifyexit'): +            return + +        if self.core.do_restart: +            self.notify(_("Restarting pyLoad")) +        else: +            self.notify(_("Exiting pyLoad")) + + +    def newCaptchaTask(self, task): +        if not self.getConfig('notifycaptcha'): +            return + +        self.notify(_("Captcha"), _("New request waiting user input")) + + +    def packageFinished(self, pypack): +        if self.getConfig('notifypackage'): +            self.notify(_("Package finished"), pypack.name) + + +    def allDownloadsProcessed(self): +        if not self.getConfig('notifyprocessed'): +            return + +        if any(True for pdata in self.core.api.getQueue() if pdata.linksdone < pdata.linkstotal): +            self.notify(_("Package failed"), _("One or more packages was not completed successfully")) +        else: +            self.notify(_("All packages finished")) + + +    def getXmlData(self, msg): +        return ("<?xml version='1.0' encoding='utf-8'?> <wp:Notification xmlns:wp='WPNotification'> " +                "<wp:Toast> <wp:Text1>pyLoad</wp:Text1> <wp:Text2>%s</wp:Text2> " +                "</wp:Toast> </wp:Notification>" % msg) + + +    @Expose +    def notify(self, +               event, +               msg="", +               key=(self.getConfig('id'), self.getConfig('url'))): + +        id, url = key + +        if not id or not url: +            return + +        if self.core.isClientConnected() and not self.getConfig('ignoreclient'): +            return + +        elapsed_time = time.time() - self.last_notify + +        if elapsed_time < self.getConf("sendtimewait"): +            return + +        if elapsed_time > 60: +            self.notifications = 0 + +        elif self.notifications >= self.getConf("sendpermin"): +            return + + +        request    = self.getXmlData("%s: %s" % (event, msg) if msg else event) +        webservice = httplib.HTTP(url) + +        webservice.putrequest("POST", id) +        webservice.putheader("Host", url) +        webservice.putheader("Content-type", "text/xml") +        webservice.putheader("X-NotificationClass", "2") +        webservice.putheader("X-WindowsPhone-Target", "toast") +        webservice.putheader("Content-length", "%d" % len(request)) +        webservice.endheaders() +        webservice.send(request) +        webservice.close() + +        self.last_notify    = time.time() +        self.notifications += 1 diff --git a/pyload/plugin/addon/XMPPInterface.py b/pyload/plugin/addon/XMPPInterface.py new file mode 100644 index 000000000..c0c31c738 --- /dev/null +++ b/pyload/plugin/addon/XMPPInterface.py @@ -0,0 +1,252 @@ +# -*- coding: utf-8 -*- + +from pyxmpp import streamtls +from pyxmpp.all import JID, Message +from pyxmpp.interface import implements +from pyxmpp.interfaces import * +from pyxmpp.jabber.client import JabberClient + +from pyload.plugin.addon.IRCInterface import IRCInterface + + +class XMPPInterface(IRCInterface, JabberClient): +    __name__    = "XMPPInterface" +    __type__    = "addon" +    __version__ = "0.11" + +    __config__ = [("jid"      , "str" , "Jabber ID"                           , "user@exmaple-jabber-server.org"         ), +                ("pw"       , "str" , "Password"                            , ""                                       ), +                ("tls"      , "bool", "Use TLS"                             , False                                    ), +                ("owners"   , "str" , "List of JIDs accepting commands from", "me@icq-gateway.org;some@msn-gateway.org"), +                ("info_file", "bool", "Inform about every file finished"    , False                                    ), +                ("info_pack", "bool", "Inform about every package finished" , True                                     ), +                ("captcha"  , "bool", "Send captcha requests"               , True                                     )] + +    __description__ = """Connect to jabber and let owner perform different tasks""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org")] + + +    implements(IMessageHandlersProvider) + + +    def __init__(self, core, manager): +        IRCInterface.__init__(self, core, manager) + +        self.jid = JID(self.getConfig('jid')) +        password = self.getConfig('pw') + +        # if bare JID is provided add a resource -- it is required +        if not self.jid.resource: +            self.jid = JID(self.jid.node, self.jid.domain, "pyLoad") + +        if self.getConfig('tls'): +            tls_settings = streamtls.TLSSettings(require=True, verify_peer=False) +            auth = ("sasl:PLAIN", "sasl:DIGEST-MD5") +        else: +            tls_settings = None +            auth = ("sasl:DIGEST-MD5", "digest") + +        # setup client with provided connection information +        # and identity data +        JabberClient.__init__(self, self.jid, password, +                              disco_name="pyLoad XMPP Client", disco_type="bot", +                              tls_settings=tls_settings, auth_methods=auth) + +        self.interface_providers = [ +            VersionHandler(self), +            self, +        ] + + +    def activate(self): +        self.new_package = {} + +        self.start() + + +    def packageFinished(self, pypack): +        try: +            if self.getConfig('info_pack'): +                self.announce(_("Package finished: %s") % pypack.name) +        except Exception: +            pass + + +    def downloadFinished(self, pyfile): +        try: +            if self.getConfig('info_file'): +                self.announce( +                    _("Download finished: %(name)s @ %(plugin)s") % {"name": pyfile.name, "plugin": pyfile.pluginname}) +        except Exception: +            pass + + +    def run(self): +        # connect to IRC etc. +        self.connect() +        try: +            self.loop() +        except Exception, ex: +            self.logError(ex) + + +    def stream_state_changed(self, state, arg): +        """This one is called when the state of stream connecting the component +        to a server changes. This will usually be used to let the user +        know what is going on.""" +        self.logDebug("*** State changed: %s %r ***" % (state, arg)) + + +    def disconnected(self): +        self.logDebug("Client was disconnected") + + +    def stream_closed(self, stream): +        self.logDebug("Stream was closed", stream) + + +    def stream_error(self, err): +        self.logDebug("Stream Error", err) + + +    def get_message_handlers(self): +        """Return list of (message_type, message_handler) tuples. + +        The handlers returned will be called when matching message is received +        in a client session.""" +        return [("normal", self.message)] + + +    def message(self, stanza): +        """Message handler for the component.""" +        subject = stanza.get_subject() +        body = stanza.get_body() +        t = stanza.get_type() +        self.logDebug("Message from %s received." % unicode(stanza.get_from())) +        self.logDebug("Body: %s Subject: %s Type: %s" % (body, subject, t)) + +        if t == "headline": +            # 'headline' messages should never be replied to +            return True +        if subject: +            subject = u"Re: " + subject + +        to_jid = stanza.get_from() +        from_jid = stanza.get_to() + +        #j = JID() +        to_name = to_jid.as_utf8() +        from_name = from_jid.as_utf8() + +        names = self.getConfig('owners').split(";") + +        if to_name in names or to_jid.node + "@" + to_jid.domain in names: +            messages = [] + +            trigger = "pass" +            args = None + +            try: +                temp = body.split() +                trigger = temp[0] +                if len(temp) > 1: +                    args = temp[1:] +            except Exception: +                pass + +            handler = getattr(self, "event_%s" % trigger, self.event_pass) +            try: +                res = handler(args) +                for line in res: +                    m = Message( +                        to_jid=to_jid, +                        from_jid=from_jid, +                        stanza_type=stanza.get_type(), +                        subject=subject, +                        body=line) + +                    messages.append(m) +            except Exception, e: +                self.logError(e) + +            return messages + +        else: +            return True + + +    def response(self, msg, origin=""): +        return self.announce(msg) + + +    def announce(self, message): +        """ send message to all owners""" +        for user in self.getConfig('owners').split(";"): +            self.logDebug("Send message to", user) + +            to_jid = JID(user) + +            m = Message(from_jid=self.jid, +                        to_jid=to_jid, +                        stanza_type="chat", +                        body=message) + +            stream = self.get_stream() +            if not stream: +                self.connect() +                stream = self.get_stream() + +            stream.send(m) + + +    def beforeReconnecting(self, ip): +        self.disconnect() + + +    def afterReconnecting(self, ip): +        self.connect() + + +class VersionHandler(object): +    """Provides handler for a version query. + +    This class will answer version query and announce 'jabber:iq:version' namespace +    in the client's disco#info results.""" + +    implements(IIqHandlersProvider, IFeaturesProvider) + + +    def __init__(self, client): +        """Just remember who created this.""" +        self.client = client + + +    def get_features(self): +        """Return namespace which should the client include in its reply to a +        disco#info query.""" +        return ["jabber:iq:version"] + + +    def get_iq_get_handlers(self): +        """Return list of tuples (element_name, namespace, handler) describing +        handlers of <iq type='get'/> stanzas""" +        return [("query", "jabber:iq:version", self.get_version)] + + +    def get_iq_set_handlers(self): +        """Return empty list, as this class provides no <iq type='set'/> stanza handler.""" +        return [] + + +    def get_version(self, iq): +        """Handler for jabber:iq:version queries. + +        jabber:iq:version queries are not supported directly by PyXMPP, so the +        XML node is accessed directly through the libxml2 API.  This should be +        used very carefully!""" +        iq = iq.make_result_response() +        q = iq.new_query("jabber:iq:version") +        q.newTextChild(q.ns(), "name", "Echo component") +        q.newTextChild(q.ns(), "version", "1.0") +        return iq diff --git a/pyload/plugin/addon/__init__.py b/pyload/plugin/addon/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/pyload/plugin/addon/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/pyload/plugin/captcha/AdYouLike.py b/pyload/plugin/captcha/AdYouLike.py new file mode 100644 index 000000000..3051e351b --- /dev/null +++ b/pyload/plugin/captcha/AdYouLike.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Captcha import Captcha +from pyload.utils import json_loads + + +class AdYouLike(Captcha): +    __name__    = "AdYouLike" +    __type__    = "captcha" +    __version__ = "0.05" + +    __description__ = """AdYouLike captcha service plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + +    AYL_PATTERN      = r'Adyoulike\.create\s*\((.+?)\)' +    CALLBACK_PATTERN = r'(Adyoulike\.g\._jsonp_\d+)' + +    def detect_key(self, html=None): +        if not html: +            if hasattr(self.plugin, "html") and self.plugin.html: +                html = self.plugin.html +            else: +                errmsg = _("AdYouLike html not found") +                self.plugin.fail(errmsg) +                raise TypeError(errmsg) + +        m = re.search(self.AYL_PATTERN, html) +        n = re.search(self.CALLBACK_PATTERN, html) +        if m and n: +            self.key = (m.group(1).strip(), n.group(1).strip()) +            self.logDebug("Ayl|callback: %s | %s" % self.key) +            return self.key   #: key is the tuple(ayl, callback) +        else: +            self.logDebug("Ayl or callback not found") +            return None + +    def challenge(self, key=None, html=None): +        if not key: +            if self.detect_key(html): +                key = self.key +            else: +                errmsg = _("AdYouLike key not found") +                self.plugin.fail(errmsg) +                raise TypeError(errmsg) + +        ayl, callback = key + +        # {"adyoulike":{"key":"P~zQ~O0zV0WTiAzC-iw0navWQpCLoYEP"}, +        # "all":{"element_id":"ayl_private_cap_92300","lang":"fr","env":"prod"}} +        ayl = json_loads(ayl) + +        html = self.plugin.req.load("http://api-ayl.appspot.com/challenge", +                                    get={'key': ayl['adyoulike']['key'], +                                         'env': ayl['all']['env'], +                                         'callback': callback}) +        try: +            challenge = json_loads(re.search(callback + r'\s*\((.+?)\)', html).group(1)) + +        except AttributeError: +            errmsg = _("AdYouLike challenge pattern not found") +            self.plugin.fail(errmsg) +            raise AttributeError(errmsg) + +        self.logDebug("Challenge: %s" % challenge) + +        return self.result(ayl, challenge), challenge + +    def result(self, server, challenge): +        # Adyoulike.g._jsonp_5579316662423138 +        # ({"translations":{"fr":{"instructions_visual":"Recopiez « Soonnight » ci-dessous :"}}, +        # "site_under":true,"clickable":true,"pixels":{"VIDEO_050":[],"DISPLAY":[],"VIDEO_000":[],"VIDEO_100":[], +        # "VIDEO_025":[],"VIDEO_075":[]},"medium_type":"image/adyoulike", +        # "iframes":{"big":"<iframe src=\"http://www.soonnight.com/campagn.html\" scrolling=\"no\" +        # height=\"250\" width=\"300\" frameborder=\"0\"></iframe>"},"shares":{},"id":256, +        # "token":"e6QuI4aRSnbIZJg02IsV6cp4JQ9~MjA1","formats":{"small":{"y":300,"x":0,"w":300,"h":60}, +        # "big":{"y":0,"x":0,"w":300,"h":250},"hover":{"y":440,"x":0,"w":300,"h":60}}, +        # "tid":"SqwuAdxT1EZoi4B5q0T63LN2AkiCJBg5"}) + +        if isinstance(server, basestring): +            server = json_loads(server) + +        if isinstance(challenge, basestring): +            challenge = json_loads(challenge) + +        try: +            instructions_visual = challenge['translations'][server['all']['lang']]['instructions_visual'] +            result = re.search(u'«(.+?)»', instructions_visual).group(1).strip() + +        except AttributeError: +            errmsg = _("AdYouLike result not found") +            self.plugin.fail(errmsg) +            raise AttributeError(errmsg) + +        result = {'_ayl_captcha_engine': "adyoulike", +                  '_ayl_env': server['all']['env'], +                  '_ayl_tid': challenge['tid'], +                  '_ayl_token_challenge': challenge['token'], +                  '_ayl_response': response} + +        self.logDebug("Result: %s" % result) + +        return result diff --git a/pyload/plugin/captcha/AdsCaptcha.py b/pyload/plugin/captcha/AdsCaptcha.py new file mode 100644 index 000000000..5b23247c0 --- /dev/null +++ b/pyload/plugin/captcha/AdsCaptcha.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- + +import re + +from random import random + +from pyload.plugin.Captcha import Captcha + + +class AdsCaptcha(Captcha): +    __name__    = "AdsCaptcha" +    __type__    = "captcha" +    __version__ = "0.08" + +    __description__ = """AdsCaptcha captcha service plugin""" +    __license__     = "GPLv3" +    __authors__     = [("pyLoad Team", "admin@pyload.org")] + + +    CAPTCHAID_PATTERN  = r'api\.adscaptcha\.com/Get\.aspx\?[^"\']*CaptchaId=(\d+)' +    PUBLICKEY_PATTERN = r'api\.adscaptcha\.com/Get\.aspx\?[^"\']*PublicKey=([\w-]+)' + + +    def detect_key(self, html=None): +        if not html: +            if hasattr(self.plugin, "html") and self.plugin.html: +                html = self.plugin.html +            else: +                errmsg = _("AdsCaptcha html not found") +                self.plugin.fail(errmsg) +                raise TypeError(errmsg) + +        m = re.search(self.PUBLICKEY_PATTERN, html) +        n = re.search(self.CAPTCHAID_PATTERN, html) +        if m and n: +            self.key = (m.group(1).strip(), n.group(1).strip())  #: key is the tuple(PublicKey, CaptchaId) +            self.logDebug("Key|id: %s | %s" % self.key) +            return self.key +        else: +            self.logDebug("Key or id not found") +            return None + + +    def challenge(self, key=None, html=None): +        if not key: +            if self.detect_key(html): +                key = self.key +            else: +                errmsg = _("AdsCaptcha key not found") +                self.plugin.fail(errmsg) +                raise TypeError(errmsg) + +        PublicKey, CaptchaId = key + +        html = self.plugin.req.load("http://api.adscaptcha.com/Get.aspx", +                                    get={'CaptchaId': CaptchaId, +                                         'PublicKey': PublicKey}) +        try: +            challenge = re.search("challenge: '(.+?)',", html).group(1) +            server    = re.search("server: '(.+?)',", html).group(1) + +        except AttributeError: +            errmsg = _("AdsCaptcha challenge pattern not found") +            self.plugin.fail(errmsg) +            raise AttributeError(errmsg) + +        self.logDebug("Challenge: %s" % challenge) + +        return self.result(server, challenge), challenge + + +    def result(self, server, challenge): +        result = self.plugin.decryptCaptcha("%sChallenge.aspx" % server, +                                            get={'cid': challenge, 'dummy': random()}, +                                            cookies=True, +                                            imgtype="jpg") + +        self.logDebug("Result: %s" % result) + +        return result diff --git a/pyload/plugin/captcha/ReCaptcha.py b/pyload/plugin/captcha/ReCaptcha.py new file mode 100644 index 000000000..6dcb09f55 --- /dev/null +++ b/pyload/plugin/captcha/ReCaptcha.py @@ -0,0 +1,201 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from base64 import b64encode +from random import randint +from urlparse import urljoin, urlparse + +from pyload.plugin.Captcha import Captcha + + +class ReCaptcha(Captcha): +    __name__    = "ReCaptcha" +    __type__    = "captcha" +    __version__ = "0.14" + +    __description__ = """ReCaptcha captcha service plugin""" +    __license__     = "GPLv3" +    __authors__     = [("pyLoad Team", "admin@pyload.org"), +                     ("Walter Purcaro", "vuolter@gmail.com"), +                     ("zapp-brannigan", "fuerst.reinje@web.de")] + +    KEY_V2_PATTERN = r'(?:data-sitekey=["\']|["\']sitekey["\']:\s*["\'])([\w-]+)' +    KEY_V1_PATTERN = r'(?:recaptcha(?:/api|\.net)/(?:challenge|noscript)\?k=|Recaptcha\.create\s*\(\s*["\'])([\w-]+)' + +    def detect_key(self, html=None): +        if not html: +            if hasattr(self.plugin, "html") and self.plugin.html: +                html = self.plugin.html +            else: +                errmsg = _("ReCaptcha html not found") +                self.plugin.fail(errmsg) +                raise TypeError(errmsg) + +        m = re.search(self.KEY_V2_PATTERN, html) or re.search(self.KEY_V1_PATTERN, html) +        if m: +            self.key = m.group(1).strip() +            self.logDebug("Key: %s" % self.key) +            return self.key +        else: +            self.logDebug("Key not found") +            return None + +    def challenge(self, key=None, html=None, version=None): +        if not key: +            if self.detect_key(html): +                key = self.key +            else: +                errmsg = _("ReCaptcha key not found") +                self.plugin.fail(errmsg) +                raise TypeError(errmsg) + +        if version in (1, 2): +            return getattr(self, "_challenge_v%s" % version)(key) + +        elif not html and hasattr(self.plugin, "html") and self.plugin.html: +            version = 2 if re.search(self.KEY_V2_PATTERN, self.plugin.html) else 1 +            return self.challenge(key, self.plugin.html, version) + +        else: +            errmsg = _("ReCaptcha html not found") +            self.plugin.fail(errmsg) +            raise TypeError(errmsg) + +    def _challenge_v1(self, key): +        html = self.plugin.req.load("http://www.google.com/recaptcha/api/challenge", +                                    get={'k': key}) +        try: +            challenge = re.search("challenge : '(.+?)',", html).group(1) +            server    = re.search("server : '(.+?)',", html).group(1) + +        except AttributeError: +            errmsg = _("ReCaptcha challenge pattern not found") +            self.plugin.fail(errmsg) +            raise AttributeError(errmsg) + +        self.logDebug("Challenge: %s" % challenge) + +        return self.result(server, challenge), challenge + +    def result(self, server, challenge): +        result = self.plugin.decryptCaptcha("%simage" % server, +                                            get={'c': challenge}, +                                            cookies=True, +                                            forceUser=True, +                                            imgtype="jpg") + +        self.logDebug("Result: %s" % result) + +        return result + +    def _collectApiInfo(self): +        html = self.plugin.req.load("http://www.google.com/recaptcha/api.js") +        a    = re.search(r'po.src = \'(.*?)\';', html).group(1) +        vers = a.split("/")[5] + +        self.logDebug("API version: %s" % vers) + +        language = a.split("__")[1].split(".")[0] + +        self.logDebug("API language: %s" % language) + +        html = self.plugin.req.load("https://apis.google.com/js/api.js") +        b    = re.search(r'"h":"(.*?)","', html).group(1) +        jsh  = b.decode('unicode-escape') + +        self.logDebug("API jsh-string: %s" % jsh) + +        return vers, language, jsh + +    def _prepareTimeAndRpc(self): +        self.plugin.req.load("http://www.google.com/recaptcha/api2/demo") + +        millis = int(round(time.time() * 1000)) + +        self.logDebug("Time: %s" % millis) + +        rand = randint(1, 99999999) +        a    = "0.%s" % str(rand * 2147483647) +        rpc  = int(100000000 * float(a)) + +        self.logDebug("Rpc-token: %s" % rpc) + +        return millis, rpc + +    def _challenge_v2(self, key, parent=None): +        if parent is None: +            try: +                parent = urljoin("http://", urlparse(self.plugin.pyfile.url).netloc) + +            except Exception: +                parent = "" + +        botguardstring      = "!A" +        vers, language, jsh = self._collectApiInfo() +        millis, rpc         = self._prepareTimeAndRpc() + +        html = self.plugin.req.load("https://www.google.com/recaptcha/api2/anchor", +                                    get={'k'       : key, +                                         'hl'      : language, +                                         'v'       : vers, +                                         'usegapi' : "1", +                                         'jsh'     : "%s#id=IO_%s" % (jsh, millis), +                                         'parent'  : parent, +                                         'pfname'  : "", +                                         'rpctoken': rpc}) + +        token1 = re.search(r'id="recaptcha-token" value="(.*?)">', html) +        self.logDebug("Token #1: %s" % token1.group(1)) + +        html = self.plugin.req.load("https://www.google.com/recaptcha/api2/frame", +                                    get={'c'      : token1.group(1), +                                         'hl'     : language, +                                         'v'      : vers, +                                         'bg'     : botguardstring, +                                         'k'      : key, +                                         'usegapi': "1", +                                         'jsh'    : jsh}).decode('unicode-escape') + +        token2 = re.search(r'"finput","(.*?)",', html) +        self.logDebug("Token #2: %s" % token2.group(1)) + +        token3 = re.search(r'."asconf".\s,".*?".\s,"(.*?)".', html) +        self.logDebug("Token #3: %s" % token3.group(1)) + +        html = self.plugin.req.load("https://www.google.com/recaptcha/api2/reload", +                                    post={'k'     : key, +                                          'c'     : token2.group(1), +                                          'reason': "fi", +                                          'fbg'   : token3.group(1)}) + +        token4 = re.search(r'"rresp","(.*?)",', html) +        self.logDebug("Token #4: %s" % token4.group(1)) + +        millis_captcha_loading = int(round(time.time() * 1000)) +        captcha_response       = self.plugin.decryptCaptcha("https://www.google.com/recaptcha/api2/payload", +                                                            get={'c': token4.group(1), 'k': key}, +                                                            cookies=True, +                                                            forceUser=True) +        response               = b64encode('{"response":"%s"}' % captcha_response) + +        self.logDebug("Result: %s" % response) + +        timeToSolve     = int(round(time.time() * 1000)) - millis_captcha_loading +        timeToSolveMore = timeToSolve + int(float("0." + str(randint(1, 99999999))) * 500) + +        html = self.plugin.req.load("https://www.google.com/recaptcha/api2/userverify", +                                    post={'k'       : key, +                                          'c'       : token4.group(1), +                                          'response': response, +                                          't'       : timeToSolve, +                                          'ct'      : timeToSolveMore, +                                          'bg'      : botguardstring}) + +        token5 = re.search(r'"uvresp","(.*?)",', html) +        self.logDebug("Token #5: %s" % token5.group(1)) + +        result = token5.group(1) + +        return result, None diff --git a/pyload/plugin/captcha/SolveMedia.py b/pyload/plugin/captcha/SolveMedia.py new file mode 100644 index 000000000..7f421f490 --- /dev/null +++ b/pyload/plugin/captcha/SolveMedia.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Captcha import Captcha + + +class SolveMedia(Captcha): +    __name__    = "SolveMedia" +    __type__    = "captcha" +    __version__ = "0.12" + +    __description__ = """SolveMedia captcha service plugin""" +    __license__     = "GPLv3" +    __authors__     = [("pyLoad Team", "admin@pyload.org")] + + +    KEY_PATTERN = r'api\.solvemedia\.com/papi/challenge\.(?:no)?script\?k=(.+?)["\']' + + +    def detect_key(self, html=None): +        if not html: +            if hasattr(self.plugin, "html") and self.plugin.html: +                html = self.plugin.html +            else: +                errmsg = _("SolveMedia html not found") +                self.plugin.fail(errmsg) +                raise TypeError(errmsg) + +        m = re.search(self.KEY_PATTERN, html) +        if m: +            self.key = m.group(1).strip() +            self.logDebug("Key: %s" % self.key) +            return self.key +        else: +            self.logDebug("Key not found") +            return None + + +    def challenge(self, key=None, html=None): +        if not key: +            if self.detect_key(html): +                key = self.key +            else: +                errmsg = _("SolveMedia key not found") +                self.plugin.fail(errmsg) +                raise TypeError(errmsg) + +        html = self.plugin.req.load("http://api.solvemedia.com/papi/challenge.noscript", +                                    get={'k': key}) +        try: +            challenge = re.search(r'<input type=hidden name="adcopy_challenge" id="adcopy_challenge" value="([^"]+)">', +                                  html).group(1) +            server    = "http://api.solvemedia.com/papi/media" + +        except AttributeError: +            errmsg = _("SolveMedia challenge pattern not found") +            self.plugin.fail(errmsg) +            raise AttributeError(errmsg) + +        self.logDebug("Challenge: %s" % challenge) + +        result = self.result(server, challenge) + +        try: +            magic = re.search(r'name="magic" value="(.+?)"', html).group(1) + +        except AttributeError: +            self.logDebug("Magic code not found") + +        else: +            if not self._verify(key, magic, result, challenge): +                self.logDebug("Captcha code was invalid") + +        return result, challenge + + +    def _verify(self, key, magic, result, challenge, ref=None):  #@TODO: Clean up +        if ref is None: +            try: +                ref = self.plugin.pyfile.url + +            except Exception: +                ref = "" + +        html = self.plugin.req.load("http://api.solvemedia.com/papi/verify.noscript", +                                    post={'adcopy_response'  : result, +                                          'k'                : key, +                                          'l'                : "en", +                                          't'                : "img", +                                          's'                : "standard", +                                          'magic'            : magic, +                                          'adcopy_challenge' : challenge, +                                          'ref'              : ref}) +        try: +            html      = self.plugin.req.load(re.search(r'URL=(.+?)">', html).group(1)) +            gibberish = re.search(r'id=gibberish>(.+?)</textarea>', html).group(1) + +        except Exception: +            return False + +        else: +            return True + + +    def result(self, server, challenge): +        result = self.plugin.decryptCaptcha(server, +                                            get={'c': challenge}, +                                            cookies=True, +                                            imgtype="gif") + +        self.logDebug("Result: %s" % result) + +        return result diff --git a/pyload/plugin/captcha/__init__.py b/pyload/plugin/captcha/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/pyload/plugin/captcha/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/pyload/plugin/container/CCF.py b/pyload/plugin/container/CCF.py new file mode 100644 index 000000000..65f96033a --- /dev/null +++ b/pyload/plugin/container/CCF.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import re + +from urllib2 import build_opener + +from MultipartPostHandler import MultipartPostHandler + +from pyload.plugin.Container import Container +from pyload.utils import fs_encode, fs_join + + +class CCF(Container): +    __name__    = "CCF" +    __type__    = "container" +    __version__ = "0.23" + +    __pattern__ = r'.+\.ccf$' + +    __description__ = """CCF container decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Willnix", "Willnix@pyload.org"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    def decrypt(self, pyfile): +        fs_filename = fs_encode(pyfile.url.strip()) +        opener      = build_opener(MultipartPostHandler) + +        dlc_content = opener.open('http://service.jdownloader.net/dlcrypt/getDLC.php', +                                  {'src'     : "ccf", +                                   'filename': "test.ccf", +                                   'upload'  : open(fs_filename, "rb")}).read() + +        download_folder = self.config['general']['download_folder'] +        dlc_file        = fs_join(download_folder, "tmp_%s.dlc" % pyfile.name) + +        try: +            dlc = re.search(r'<dlc>(.+)</dlc>', dlc_content, re.S).group(1).decode('base64') + +        except AttributeError: +            self.fail(_("Container is corrupted")) + +        with open(dlc_file, "w") as tempdlc: +            tempdlc.write(dlc) + +        self.urls = [dlc_file] diff --git a/pyload/plugin/container/DLC.py b/pyload/plugin/container/DLC.py new file mode 100644 index 000000000..04dabb6b2 --- /dev/null +++ b/pyload/plugin/container/DLC.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import re +import xml.dom.minidom + +from Crypto.Cipher import AES + +from pyload.plugin.Container import Container +from pyload.utils import decode, fs_encode + + +class DLC(Container): +    __name__    = "DLC" +    __type__    = "container" +    __version__ = "0.24" + +    __pattern__ = r'.+\.dlc$' + +    __description__ = """DLC container decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org"), +                       ("spoob", "spoob@pyload.org"), +                       ("mkaay", "mkaay@mkaay.de"), +                       ("Schnusch", "Schnusch@users.noreply.github.com"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    KEY     = "cb99b5cbc24db398" +    IV      = "9bc24cb995cb8db3" +    API_URL = "http://service.jdownloader.org/dlcrypt/service.php?srcType=dlc&destType=pylo&data=%s" + + +    def decrypt(self, pyfile): +        fs_filename = fs_encode(pyfile.url.strip()) +        with open(fs_filename) as dlc: +            data = dlc.read().strip() + +        data += '=' * (-len(data) % 4) + +        dlc_key     = data[-88:] +        dlc_data    = data[:-88].decode('base64') +        dlc_content = self.load(self.API_URL % dlc_key) + +        try: +            rc = re.search(r'<rc>(.+)</rc>', dlc_content, re.S).group(1).decode('base64') + +        except AttributeError: +            self.fail(_("Container is corrupted")) + +        key = iv = AES.new(self.KEY, AES.MODE_CBC, self.IV).decrypt(rc) + +        self.data     = AES.new(key, AES.MODE_CBC, iv).decrypt(dlc_data).decode('base64') +        self.packages = [(name or pyfile.name, links, name or pyfile.name) \ +                         for name, links in self.getPackages()] + + +    def getPackages(self): +        root    = xml.dom.minidom.parseString(self.data).documentElement +        content = root.getElementsByTagName("content")[0] +        return self.parsePackages(content) + + +    def parsePackages(self, startNode): +        return [(decode(node.getAttribute("name")).decode('base64'), self.parseLinks(node)) \ +                for node in startNode.getElementsByTagName("package")] + + +    def parseLinks(self, startNode): +        return [node.getElementsByTagName("url")[0].firstChild.data.decode('base64') \ +                for node in startNode.getElementsByTagName("file")] diff --git a/pyload/plugin/container/RSDF.py b/pyload/plugin/container/RSDF.py new file mode 100644 index 000000000..e43eb4c2b --- /dev/null +++ b/pyload/plugin/container/RSDF.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import binascii +import re + +from Crypto.Cipher import AES + +from pyload.plugin.Container import Container +from pyload.utils import fs_encode + + +class RSDF(Container): +    __name__    = "RSDF" +    __type__    = "container" +    __version__ = "0.29" + +    __pattern__ = r'.+\.rsdf$' + +    __description__ = """RSDF container decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org"), +                       ("spoob", "spoob@pyload.org"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    KEY = "8C35192D964DC3182C6F84F3252239EB4A320D2500000000" +    IV  = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + + +    def decrypt(self, pyfile): +        KEY = binascii.unhexlify(self.KEY) +        IV  = binascii.unhexlify(self.IV) + +        iv     = AES.new(KEY, AES.MODE_ECB).encrypt(IV) +        cipher = AES.new(KEY, AES.MODE_CFB, iv) + +        try: +            fs_filename = fs_encode(pyfile.url.strip()) +            with open(fs_filename, 'r') as rsdf: +                data = rsdf.read() + +        except IOError, e: +            self.fail(e) + +        if re.search(r"<title>404 - Not Found</title>", data): +            pyfile.setStatus("offline") + +        else: +            try: +                raw_links = binascii.unhexlify(''.join(data.split())).splitlines() + +            except TypeError: +                self.fail(_("Container is corrupted")) + +            for link in raw_links: +                if not link: +                    continue +                link = cipher.decrypt(link.decode('base64')).replace('CCF: ', '') +                self.urls.append(link) diff --git a/pyload/plugin/container/TXT.py b/pyload/plugin/container/TXT.py new file mode 100644 index 000000000..75940f55d --- /dev/null +++ b/pyload/plugin/container/TXT.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- + +import codecs + +from pyload.plugin.Container import Container +from pyload.utils import fs_encode + + +class TXT(Container): +    __name__    = "TXT" +    __type__    = "container" +    __version__ = "0.15" + +    __pattern__ = r'.+\.(txt|text)$' +    __config__  = [("flush"   , "bool"  , "Flush list after adding", False  ), +                 ("encoding", "string", "File encoding"          , "utf-8")] + +    __description__ = """Read link lists in plain text formats""" +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org"), +                     ("jeix", "jeix@hasnomail.com")] + + +    def decrypt(self, pyfile): +        try: +            encoding = codecs.lookup(self.getConfig('encoding')).name + +        except Exception: +            encoding = "utf-8" + +        fs_filename = fs_encode(pyfile.url.strip()) +        txt         = codecs.open(fs_filename, 'r', encoding) +        curPack     = "Parsed links from %s" % pyfile.name +        packages    = {curPack:[],} + +        for link in txt.readlines(): +            link = link.strip() + +            if not link: +                continue + +            if link.startswith(";"): +                continue + +            if link.startswith("[") and link.endswith("]"): +                # new package +                curPack = link[1:-1] +                packages[curPack] = [] +                continue + +            packages[curPack].append(link) + +        txt.close() + +        # empty packages fix +        for key, value in packages.iteritems(): +            if not value: +                packages.pop(key, None) + +        if self.getConfig('flush'): +            try: +                txt = open(fs_filename, 'wb') +                txt.close() + +            except IOError: +                self.logWarning(_("Failed to flush list")) + +        for name, links in packages.iteritems(): +            self.packages.append((name, links, name)) diff --git a/pyload/plugin/container/__init__.py b/pyload/plugin/container/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/pyload/plugin/container/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/pyload/plugin/crypter/BitshareCom.py b/pyload/plugin/crypter/BitshareCom.py new file mode 100644 index 000000000..8458fac3e --- /dev/null +++ b/pyload/plugin/crypter/BitshareCom.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class BitshareCom(SimpleCrypter): +    __name__    = "BitshareCom" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?bitshare\.com/\?d=\w+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Bitshare.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    LINK_PATTERN = r'<a href="(http://bitshare\.com/files/.+)">.+</a></td>' +    NAME_PATTERN = r'View public folder "(?P<N>.+)"</h1>' diff --git a/pyload/plugin/crypter/C1NeonCom.py b/pyload/plugin/crypter/C1NeonCom.py new file mode 100644 index 000000000..dc1555b46 --- /dev/null +++ b/pyload/plugin/crypter/C1NeonCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class C1NeonCom(DeadCrypter): +    __name__    = "C1NeonCom" +    __type__    = "crypter" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?c1neon\.com/.+' +    __config__  = [] + +    __description__ = """C1neon.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("godofdream", "soilfiction@gmail.com")] diff --git a/pyload/plugin/crypter/ChipDe.py b/pyload/plugin/crypter/ChipDe.py new file mode 100644 index 000000000..b9f9a6c4d --- /dev/null +++ b/pyload/plugin/crypter/ChipDe.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +import re +from pyload.plugin.Crypter import Crypter + + +class ChipDe(Crypter): +    __name__    = "ChipDe" +    __type__    = "crypter" +    __version__ = "0.10" + +    __pattern__ = r'http://(?:www\.)?chip\.de/video/.+\.html' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Chip.de decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("4Christopher", "4Christopher@gmx.de")] + + +    def decrypt(self, pyfile): +        self.html = self.load(pyfile.url) +        try: +            f = re.search(r'"(http://video\.chip\.de/.+)"', self.html) +        except Exception: +            self.fail(_("Failed to find the URL")) +        else: +            self.urls = [f.group(1)] +            self.logDebug("The file URL is %s" % self.urls[0]) diff --git a/pyload/plugin/crypter/CloudzillaTo.py b/pyload/plugin/crypter/CloudzillaTo.py new file mode 100644 index 000000000..340892136 --- /dev/null +++ b/pyload/plugin/crypter/CloudzillaTo.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class CloudzillaTo(SimpleHoster): +    __name__    = "CloudzillaTo" +    __type__    = "crypter" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?cloudzilla\.to/share/folder/(?P<ID>[\w^_]+)' + +    __description__ = """Cloudzilla.to folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    INFO_PATTERN    = r'<span class="name" title="(?P<N>.+?)"' +    OFFLINE_PATTERN = r'>File not found...<' + +    LINK_PATTERN = r'<a href="(.+?)" class="item_href">' + +    PASSWORD_PATTERN = r'<div id="pwd_protected">' + + +    def checkErrors(self): +        m = re.search(self.PASSWORD_PATTERN, self.html) +        if m: +            self.html = self.load(self.pyfile.url, get={'key': self.getPassword()}) + +        if re.search(self.PASSWORD_PATTERN, self.html): +            self.retry(reason="Wrong password") diff --git a/pyload/plugin/crypter/CrockoCom.py b/pyload/plugin/crypter/CrockoCom.py new file mode 100644 index 000000000..bb93edca9 --- /dev/null +++ b/pyload/plugin/crypter/CrockoCom.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class CrockoCom(SimpleCrypter): +    __name__    = "CrockoCom" +    __type__    = "crypter" +    __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?crocko\.com/f/.+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Crocko.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    LINK_PATTERN = r'<td class="last"><a href="(.+?)">download</a>' + diff --git a/pyload/plugin/crypter/CryptItCom.py b/pyload/plugin/crypter/CryptItCom.py new file mode 100644 index 000000000..0d72cba61 --- /dev/null +++ b/pyload/plugin/crypter/CryptItCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class CryptItCom(DeadCrypter): +    __name__    = "CryptItCom" +    __type__    = "crypter" +    __version__ = "0.11" + +    __pattern__ = r'http://(?:www\.)?crypt-it\.com/(s|e|d|c)/\w+' +    __config__  = [] + +    __description__ = """Crypt-it.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de")] diff --git a/pyload/plugin/crypter/CzshareCom.py b/pyload/plugin/crypter/CzshareCom.py new file mode 100644 index 000000000..60e53ec39 --- /dev/null +++ b/pyload/plugin/crypter/CzshareCom.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +import re +from pyload.plugin.Crypter import Crypter + + +class CzshareCom(Crypter): +    __name__    = "CzshareCom" +    __type__    = "crypter" +    __version__ = "0.20" + +    __pattern__ = r'http://(?:www\.)?(czshare|sdilej)\.(com|cz)/folders/.+' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Czshare.com folder decrypter plugin, now Sdilej.cz""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    FOLDER_PATTERN = r'<tr class="subdirectory">\s*<td>\s*<table>(.*?)</table>' +    LINK_PATTERN = r'<td class="col2"><a href="(.+?)">info</a></td>' + + +    def decrypt(self, pyfile): +        html = self.load(pyfile.url) + +        m = re.search(self.FOLDER_PATTERN, html, re.S) +        if m is None: +            self.error(_("FOLDER_PATTERN not found")) + +        self.urls.extend(re.findall(self.LINK_PATTERN, m.group(1))) diff --git a/pyload/plugin/crypter/DailymotionComFolder.py b/pyload/plugin/crypter/DailymotionComFolder.py new file mode 100644 index 000000000..3c5000515 --- /dev/null +++ b/pyload/plugin/crypter/DailymotionComFolder.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from pyload.utils import json_loads +from pyload.plugin.Crypter import Crypter +from pyload.utils import fs_join + + +class DailymotionComFolder(Crypter): +    __name__    = "DailymotionComFolder" +    __type__    = "crypter" +    __version__ = "0.01" + +    __pattern__ = r'https?://(?:www\.)?dailymotion\.com/((playlists/)?(?P<TYPE>playlist|user)/)?(?P<ID>[\w^_]+)(?(TYPE)|#)' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Dailymotion.com channel & playlist decrypter""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    def api_response(self, ref, req=None): +        url  = urljoin("https://api.dailymotion.com/", ref) +        html = self.load(url, get=req) +        return json_loads(html) + + +    def getPlaylistInfo(self, id): +        ref = "playlist/" + id +        req = {"fields": "name,owner.screenname"} +        playlist = self.api_response(ref, req) + +        if "error" in playlist: +            return + +        name = playlist['name'] +        owner = playlist['owner.screenname'] +        return name, owner + + +    def _getPlaylists(self, user_id, page=1): +        ref = "user/%s/playlists" % user_id +        req = {"fields": "id", "page": page, "limit": 100} +        user = self.api_response(ref, req) + +        if "error" in user: +            return + +        for playlist in user['list']: +            yield playlist['id'] + +        if user['has_more']: +            for item in self._getPlaylists(user_id, page + 1): +                yield item + + +    def getPlaylists(self, user_id): +        return [(id,) + self.getPlaylistInfo(id) for id in self._getPlaylists(user_id)] + + +    def _getVideos(self, id, page=1): +        ref = "playlist/%s/videos" % id +        req = {"fields": "url", "page": page, "limit": 100} +        playlist = self.api_response(ref, req) + +        if "error" in playlist: +            return + +        for video in playlist['list']: +            yield video['url'] + +        if playlist['has_more']: +            for item in self._getVideos(id, page + 1): +                yield item + + +    def getVideos(self, playlist_id): +        return list(self._getVideos(playlist_id))[::-1] + + +    def decrypt(self, pyfile): +        m = re.match(self.__pattern__, pyfile.url) +        m_id = m.group('ID') +        m_type = m.group('TYPE') + +        if m_type == "playlist": +            self.logDebug("Url recognized as Playlist") +            p_info = self.getPlaylistInfo(m_id) +            playlists = [(m_id,) + p_info] if p_info else None +        else: +            self.logDebug("Url recognized as Channel") +            playlists = self.getPlaylists(m_id) +            self.logDebug("%s playlist\s found on channel \"%s\"" % (len(playlists), m_id)) + +        if not playlists: +            self.fail(_("No playlist available")) + +        for p_id, p_name, p_owner in playlists: +            p_videos = self.getVideos(p_id) +            p_folder = fs_join(self.config['general']['download_folder'], p_owner, p_name) +            self.logDebug("%s video\s found on playlist \"%s\"" % (len(p_videos), p_name)) +            self.packages.append((p_name, p_videos, p_folder))  #: folder is NOT recognized by pyload 0.4.9! diff --git a/pyload/plugin/crypter/DataHu.py b/pyload/plugin/crypter/DataHu.py new file mode 100644 index 000000000..a2e5d4721 --- /dev/null +++ b/pyload/plugin/crypter/DataHu.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class DataHu(SimpleCrypter): +    __name__    = "DataHu" +    __type__    = "crypter" +    __version__ = "0.06" + +    __pattern__ = r'http://(?:www\.)?data\.hu/dir/\w+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Data.hu folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("crash", ""), +                       ("stickell", "l.stickell@yahoo.it")] + + +    LINK_PATTERN = r'<a href=\'(http://data\.hu/get/.+)\' target=\'_blank\'>\1</a>' +    NAME_PATTERN = ur'<title>(?P<N>.+) Let\xf6lt\xe9se</title>' + + +    def prepare(self): +        super(DataHu, self).prepare() + +        if u'K\xe9rlek add meg a jelsz\xf3t' in self.html:  # Password protected +            password = self.getPassword() +            if not password: +                self.fail(_("Password required")) + +            self.logDebug("The folder is password protected', 'Using password: " + password) + +            self.html = self.load(self.pyfile.url, post={'mappa_pass': password}, decode=True) + +            if u'Hib\xe1s jelsz\xf3' in self.html:  # Wrong password +                self.fail(_("Wrong password")) diff --git a/pyload/plugin/crypter/DdlstorageCom.py b/pyload/plugin/crypter/DdlstorageCom.py new file mode 100644 index 000000000..f3744e15d --- /dev/null +++ b/pyload/plugin/crypter/DdlstorageCom.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class DdlstorageCom(DeadCrypter): +    __name__    = "DdlstorageCom" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'https?://(?:www\.)?ddlstorage\.com/folder/\w+' +    __config__  = [] + +    __description__ = """DDLStorage.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("godofdream", "soilfiction@gmail.com"), +                       ("stickell", "l.stickell@yahoo.it")] diff --git a/pyload/plugin/crypter/DepositfilesCom.py b/pyload/plugin/crypter/DepositfilesCom.py new file mode 100644 index 000000000..e47f9dd18 --- /dev/null +++ b/pyload/plugin/crypter/DepositfilesCom.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class DepositfilesCom(SimpleCrypter): +    __name__    = "DepositfilesCom" +    __type__    = "crypter" +    __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?depositfiles\.com/folders/\w+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Depositfiles.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    LINK_PATTERN = r'<div class="progressName".*?>\s*<a href="(.+?)" title=".+?" target="_blank">' + diff --git a/pyload/plugin/crypter/Dereferer.py b/pyload/plugin/crypter/Dereferer.py new file mode 100644 index 000000000..50e1257cc --- /dev/null +++ b/pyload/plugin/crypter/Dereferer.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Crypter import Crypter + + +class Dereferer(SimpleDereferer): +    __name__    = "Dereferer" +    __type__    = "crypter" +    __version__ = "0.11" + +    __pattern__ = r'https?://([^/]+)/.*?(?P<LINK>(ht|f)tps?(://|%3A%2F%2F).+)' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Crypter for dereferers""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/crypter/DevhostSt.py b/pyload/plugin/crypter/DevhostSt.py new file mode 100644 index 000000000..f06c8da30 --- /dev/null +++ b/pyload/plugin/crypter/DevhostSt.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://d-h.st/users/shine/?fld_id=37263#files + +import re + +from urlparse import urljoin + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class DevhostSt(SimpleCrypter): +    __name__    = "DevhostSt" +    __type__    = "crypter" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?d-h\.st/users/(?P<USER>\w+)(/\?fld_id=(?P<ID>\d+))?' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """d-h.st folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    LINK_PATTERN    = r'(?:/> |;">)<a href="(.+?)"(?!>Back to \w+<)' +    OFFLINE_PATTERN = r'"/cHP">test\.png<' + + +    def checkNameSize(self, getinfo=True): +        if not self.info or getinfo: +            self.logDebug("File info (BEFORE): %s" % self.info) +            self.info.update(self.getInfo(self.pyfile.url, self.html)) +            self.logDebug("File info (AFTER): %s"  % self.info) + +        try: +            if self.info['pattern']['ID'] == "0": +                raise + +            p = r'href="(.+?)">Back to \w+<' +            m = re.search(p, self.html) +            html = self.load(urljoin("http://d-h.st", m.group(1)), +                             cookies=False) + +            p = '\?fld_id=%s.*?">(.+?)<' % self.info['pattern']['ID'] +            m = re.search(p, html) +            self.pyfile.name = m.group(1) + +        except Exception, e: +            self.logDebug(e) +            self.pyfile.name = self.info['pattern']['USER'] + +        try: +            folder = self.info['folder'] = self.pyfile.name + +        except Exception: +            pass +        self.logDebug("File name: %s"   % self.pyfile.name, +                      "File folder: %s" % self.pyfile.name)
\ No newline at end of file diff --git a/pyload/plugin/crypter/DlProtectCom.py b/pyload/plugin/crypter/DlProtectCom.py new file mode 100644 index 000000000..081db7067 --- /dev/null +++ b/pyload/plugin/crypter/DlProtectCom.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from base64 import urlsafe_b64encode + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class DlProtectCom(SimpleCrypter): +    __name__    = "DlProtectCom" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'https?://(?:www\.)?dl-protect\.com/((en|fr)/)?\w+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Dl-protect.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    COOKIES = [("dl-protect.com", "l", "en")] + +    OFFLINE_PATTERN = r'Unfortunately, the link you are looking for is not found' + + +    def getLinks(self): +        # Direct link with redirect +        if not re.match(r"https?://(?:www\.)?dl-protect\.com/.+", self.req.http.lastEffectiveURL): +            return [self.req.http.lastEffectiveURL] + +        post_req = {'key'       : re.search(r'name="key" value="(.+?)"', self.html).group(1), +                    'submitform': ""} + +        if "Please click on continue to see the content" in self.html: +            post_req['submitform'] = "Continue" +            self.wait(2) + +        else: +            mstime  = int(round(time.time() * 1000)) +            b64time = "_" + urlsafe_b64encode(str(mstime)).replace("=", "%3D") + +            post_req.update({'i'         : b64time, +                             'submitform': "Decrypt+link"}) + +            if "Password :" in self.html: +                post_req['pwd'] = self.getPassword() + +            if "Security Code" in self.html: +                captcha_id   = re.search(r'/captcha\.php\?uid=(.+?)"', self.html).group(1) +                captcha_url  = "http://www.dl-protect.com/captcha.php?uid=" + captcha_id +                captcha_code = self.decryptCaptcha(captcha_url, imgtype="gif") + +                post_req['secure'] = captcha_code + +        self.html = self.load(self.pyfile.url, post=post_req) + +        for errmsg in ("The password is incorrect", "The security code is incorrect"): +            if errmsg in self.html: +                self.fail(_(errmsg[1:])) + +        return re.findall(r'<a href="([^/].+?)" target="_blank">', self.html) diff --git a/pyload/plugin/crypter/DontKnowMe.py b/pyload/plugin/crypter/DontKnowMe.py new file mode 100644 index 000000000..b0684ae8a --- /dev/null +++ b/pyload/plugin/crypter/DontKnowMe.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Crypter import Crypter + + +class DontKnowMe(SimpleDereferer): +    __name__    = "DontKnowMe" +    __type__    = "crypter" +    __version__ = "0.11" + +    __pattern__ = r'http://(?:www\.)?dontknow\.me/at/\?(?P<LINK>.+)' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """DontKnow.me decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("selaux", "")] diff --git a/pyload/plugin/crypter/DuckCryptInfo.py b/pyload/plugin/crypter/DuckCryptInfo.py new file mode 100644 index 000000000..04a7d3ea2 --- /dev/null +++ b/pyload/plugin/crypter/DuckCryptInfo.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +import re + +from BeautifulSoup import BeautifulSoup + +from pyload.plugin.Crypter import Crypter + + +class DuckCryptInfo(Crypter): +    __name__    = "DuckCryptInfo" +    __type__    = "crypter" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?duckcrypt\.info/(folder|wait|link)/(\w+)/?(\w*)' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """DuckCrypt.info decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("godofdream", "soilfiction@gmail.com")] + + +    TIMER_PATTERN = r'<span id="timer">(.*)</span>' + + +    def decrypt(self, pyfile): +        url = pyfile.url + +        m = re.match(self.__pattern__, url) +        if m is None: +            self.fail(_("Weird error in link")) +        if str(m.group(1)) == "link": +            self.handleLink(url) +        else: +            self.handleFolder(m) + + +    def handleFolder(self, m): +        html = self.load("http://duckcrypt.info/ajax/auth.php?hash=" + str(m.group(2))) +        m = re.match(self.__pattern__, html) +        self.logDebug("Redirectet to " + str(m.group(0))) +        html = self.load(str(m.group(0))) +        soup = BeautifulSoup(html) +        cryptlinks = soup.findAll("div", attrs={"class": "folderbox"}) +        self.logDebug("Redirectet to " + str(cryptlinks)) +        if not cryptlinks: +            self.error(_("No link found")) +        for clink in cryptlinks: +            if clink.find("a"): +                self.handleLink(clink.find("a")['href']) + + +    def handleLink(self, url): +        html = self.load(url) +        soup = BeautifulSoup(html) +        self.urls = [soup.find("iframe")['src']] +        if not self.urls: +            self.logInfo(_("No link found")) diff --git a/pyload/plugin/crypter/DuploadOrg.py b/pyload/plugin/crypter/DuploadOrg.py new file mode 100644 index 000000000..f92eb2ede --- /dev/null +++ b/pyload/plugin/crypter/DuploadOrg.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class DuploadOrg(DeadCrypter): +    __name__    = "DuploadOrg" +    __type__    = "crypter" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?dupload\.org/folder/\d+' +    __config__  = [] + +    __description__ = """Dupload.org folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] diff --git a/pyload/plugin/crypter/EasybytezCom.py b/pyload/plugin/crypter/EasybytezCom.py new file mode 100644 index 000000000..74fd5ad6a --- /dev/null +++ b/pyload/plugin/crypter/EasybytezCom.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSCrypter import XFSCrypter + + +class EasybytezCom(XFSCrypter): +    __name__    = "EasybytezCom" +    __type__    = "crypter" +    __version__ = "0.10" + +    __pattern__ = r'http://(?:www\.)?easybytez\.com/users/\d+/\d+' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Easybytez.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    LOGIN_ACCOUNT = True diff --git a/pyload/plugin/crypter/EmbeduploadCom.py b/pyload/plugin/crypter/EmbeduploadCom.py new file mode 100644 index 000000000..357126508 --- /dev/null +++ b/pyload/plugin/crypter/EmbeduploadCom.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +import re +from pyload.plugin.Crypter import Crypter +from pyload.network.HTTPRequest import BadHeader + + +class EmbeduploadCom(Crypter): +    __name__    = "EmbeduploadCom" +    __type__    = "crypter" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?embedupload\.com/\?d=.+' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"           , True         ), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package" , True         ), +                   ("preferedHoster"    , "str" , "Prefered hoster list (bar-separated)", "embedupload"), +                   ("ignoredHoster"     , "str" , "Ignored hoster list (bar-separated)" , ""           )] + +    __description__ = """EmbedUpload.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    LINK_PATTERN = r'<div id="(.+?)".*?>\s*<a href="(.+?)" target="_blank" (?:class="DownloadNow"|style="color:red")>' + + +    def decrypt(self, pyfile): +        self.html = self.load(pyfile.url, decode=True) +        tmp_links = [] + +        m = re.findall(self.LINK_PATTERN, self.html) +        if m: +            prefered_set = set(self.getConfig('preferedHoster').split('|')) +            prefered_set = map(lambda s: s.lower().split('.')[0], prefered_set) + +            self.logDebug("PF: %s" % prefered_set) + +            tmp_links.extend(x[1] for x in m if x[0] in prefered_set) +            self.urls = self.getLocation(tmp_links) + +            if not self.urls: +                ignored_set = set(self.getConfig('ignoredHoster').split('|')) +                ignored_set = map(lambda s: s.lower().split('.')[0], ignored_set) + +                self.logDebug("IG: %s" % ignored_set) + +                tmp_links.extend(x[1] for x in m if x[0] not in ignored_set) +                self.urls = self.getLocation(tmp_links) + + +    def getLocation(self, tmp_links): +        new_links = [] +        for link in tmp_links: +            try: +                header = self.load(link, just_header=True) +                if 'location' in header: +                    new_links.append(header['location']) +            except BadHeader: +                pass +        return new_links diff --git a/pyload/plugin/crypter/FilebeerInfo.py b/pyload/plugin/crypter/FilebeerInfo.py new file mode 100644 index 000000000..e2e0e1e3c --- /dev/null +++ b/pyload/plugin/crypter/FilebeerInfo.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class FilebeerInfo(DeadCrypter): +    __name__    = "FilebeerInfo" +    __type__    = "crypter" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?filebeer\.info/\d*~f\w+' +    __config__  = [] + +    __description__ = """Filebeer.info folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/crypter/FilecloudIo.py b/pyload/plugin/crypter/FilecloudIo.py new file mode 100644 index 000000000..1518fe813 --- /dev/null +++ b/pyload/plugin/crypter/FilecloudIo.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class FilecloudIo(SimpleCrypter): +    __name__    = "FilecloudIo" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'https?://(?:www\.)?(filecloud\.io|ifile\.it)/_\w+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Filecloud.io folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    LINK_PATTERN = r'href="(http://filecloud\.io/\w+)" title' +    NAME_PATTERN = r'>(?P<N>.+?) - filecloud\.io<' diff --git a/pyload/plugin/crypter/FilecryptCc.py b/pyload/plugin/crypter/FilecryptCc.py new file mode 100644 index 000000000..1d8388dcd --- /dev/null +++ b/pyload/plugin/crypter/FilecryptCc.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- +# +# Test links: +#   http://filecrypt.cc/Container/64E039F859.html + +import binascii +import re + +from Crypto.Cipher import AES +from urlparse import urljoin + +from pyload.plugin.Crypter import Crypter +from pyload.plugin.captcha.ReCaptcha import ReCaptcha + + +class FilecryptCc(Crypter): +    __name__    = "FilecryptCc" +    __type__    = "crypter" +    __version__ = "0.11" + +    __pattern__ = r'https?://(?:www\.)?filecrypt\.cc/Container/\w+' + +    __description__ = """Filecrypt.cc decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de")] + + +    # URL_REPLACEMENTS  = [(r'.html$', ""), (r'$', ".html")]  #@TODO: Extend SimpleCrypter + +    DLC_LINK_PATTERN = r'<button class="dlcdownload" type="button" title="Download \*.dlc" onclick="DownloadDLC\(\'(.+)\'\);"><i></i><span>dlc<' +    WEBLINK_PATTERN  = r"openLink.?'([\w_-]*)'," + +    CAPTCHA_PATTERN        = r'<img id="nc" src="(.+?)"' +    CIRCLE_CAPTCHA_PATTERN = r'<input type="image" src="(.+?)"' + +    MIRROR_PAGE_PATTERN = r'"[\w]*" href="(http://filecrypt.cc/Container/\w+\.html\?mirror=\d+)">' + + +    def setup(self): +        self.links = [] + + +    def decrypt(self, pyfile): +        self.html = self.load(pyfile.url) + +        if "content notfound" in self.html:  #@NOTE: "content notfound" is NOT a typo +            self.offline() + +        self.handlePasswordProtection() +        self.handleCaptcha() +        self.handleMirrorPages() + +        for handle in (self.handleCNL, self.handleWeblinks, self.handleDlcContainer): +            handle() +            if self.links: +                self.packages = [(pyfile.package().name, self.links, pyfile.package().name)] +                return + + +    def handleMirrorPages(self): +        if "mirror=" not in self.siteWithLinks: +            return + +        mirror = re.findall(self.MIRROR_PAGE_PATTERN, self.siteWithLinks) + +        self.logInfo(_("Found %d mirrors") % len(mirror)) + +        for i in mirror[1:]: +            self.siteWithLinks = self.siteWithLinks + self.load(i).decode("utf-8", "replace") + + +    def handlePasswordProtection(self): +        if '<input type="text" name="password"' not in self.html: +            return + +        self.logInfo(_("Folder is password protected")) + +        password = self.getPassword() + +        if not password: +            self.fail(_("Please enter the password in package section and try again")) + +        self.html = self.load(self.pyfile.url, post={"password": password}) + + +    def handleCaptcha(self): +        m  = re.search(self.CAPTCHA_PATTERN, self.html) +        m2 = re.search(self.CIRCLE_CAPTCHA_PATTERN, self.html) + +        if m:  #: normal captcha +            self.logDebug("Captcha-URL: %s" % m.group(1)) + +            captcha_code = self.decryptCaptcha(urljoin("http://filecrypt.cc", m.group(1)), +                                               forceUser=True, +                                               imgtype="gif") + +            self.siteWithLinks = self.load(self.pyfile.url, +                                           post={'recaptcha_response_field': captcha_code}, +                                           decode=True) +        elif m2:  #: circle captcha +            self.logDebug("Captcha-URL: %s" % m2.group(1)) + +            captcha_code = self.decryptCaptcha(urljoin("http://filecrypt.cc", m2.group(1)), +                                               forceUser=True, +                                               imgtype="gif", +                                               result_type='positional') + +            self.siteWithLinks = self.load(self.pyfile.url, +                                           post={'button.x': captcha_code[0], 'button.y': captcha_code[1]}, +                                           decode=True) + +        else: +            recaptcha   = ReCaptcha(self) +            captcha_key = recaptcha.detect_key() + +            if captcha_key: +                response, challenge = recaptcha.challenge(captcha_key) +                self.siteWithLinks  = self.load(self.pyfile.url, +                                                post={'g-recaptcha-response': response}, +                                                decode=True) +            else: +                self.logInfo(_("No captcha found")) +                self.siteWithLinks = self.html + +        if "recaptcha_image" in self.siteWithLinks or "data-sitekey" in self.siteWithLinks: +            self.invalidCaptcha() +            self.retry() + + +    def handleDlcContainer(self): +        dlc = re.findall(self.DLC_LINK_PATTERN, self.siteWithLinks) + +        if not dlc: +            return + +        for i in dlc: +            self.links.append("http://filecrypt.cc/DLC/%s.dlc" % i) + + +    def handleWeblinks(self): +        try: +            weblinks = re.findall(self.WEBLINK_PATTERN, self.siteWithLinks) + +            for link in weblinks: +                res   = self.load("http://filecrypt.cc/Link/%s.html" % link) +                link2 = re.search('<iframe noresize src="(.*)"></iframe>', res) +                res2  = self.load(link2.group(1), just_header=True) +                self.links.append(res2['location']) + +        except Exception, e: +            self.logDebug("Error decrypting weblinks: %s" % e) + + +    def handleCNL(self): +        try: +            vjk = re.findall('<input type="hidden" name="jk" value="function f\(\){ return \'(.*)\';}">', self.siteWithLinks) +            vcrypted = re.findall('<input type="hidden" name="crypted" value="(.*)">', self.siteWithLinks) + +            for i in xrange(len(vcrypted)): +                self.links.extend(self._getLinks(vcrypted[i], vjk[i])) + +        except Exception, e: +            self.logDebug("Error decrypting CNL: %s" % e) + + +    def _getLinks(self, crypted, jk): +        # Get key +        key = binascii.unhexlify(str(jk)) + +        # Decrypt +        Key  = key +        IV   = key +        obj  = AES.new(Key, AES.MODE_CBC, IV) +        text = obj.decrypt(crypted.decode('base64')) + +        # Extract links +        text  = text.replace("\x00", "").replace("\r", "") +        links = filter(bool, text.split('\n')) + +        return links diff --git a/pyload/plugin/crypter/FilefactoryCom.py b/pyload/plugin/crypter/FilefactoryCom.py new file mode 100644 index 000000000..05d631ffe --- /dev/null +++ b/pyload/plugin/crypter/FilefactoryCom.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class FilefactoryCom(SimpleCrypter): +    __name__    = "FilefactoryCom" +    __type__    = "crypter" +    __version__ = "0.32" + +    __pattern__ = r'https?://(?:www\.)?filefactory\.com/(?:f|folder)/\w+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Filefactory.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    COOKIES = [("filefactory.com", "locale", "en_US.utf8")] + +    LINK_PATTERN  = r'<td>\s*<a href="(.+?)"' +    NAME_PATTERN  = r'<h1>Files in <span>(?P<N>.+?)<' +    PAGES_PATTERN = r'data-paginator-totalPages="(\d+)' + + +    def loadPage(self, page_n): +        return self.load(self.pyfile.url, get={'page': page_n, 'show': 100}) diff --git a/pyload/plugin/crypter/FilerNet.py b/pyload/plugin/crypter/FilerNet.py new file mode 100644 index 000000000..ea6eb9e49 --- /dev/null +++ b/pyload/plugin/crypter/FilerNet.py @@ -0,0 +1,25 @@ +import re + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class FilerNet(SimpleCrypter): +    __name__    = "FilerNet" +    __type__    = "crypter" +    __version__ = "0.42" + +    __pattern__ = r'https?://filer\.net/folder/\w{16}' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Filer.net decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("nath_schwarz", "nathan.notwhite@gmail.com"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    LINK_PATTERN = r'href="(/get/\w{16})">(?!<)' + +    NAME_PATTERN    = r'<h3>(?P<N>.+?) - <small' +    OFFLINE_PATTERN = r'Nicht gefunden' diff --git a/pyload/plugin/crypter/FileserveCom.py b/pyload/plugin/crypter/FileserveCom.py new file mode 100644 index 000000000..4b742d5f3 --- /dev/null +++ b/pyload/plugin/crypter/FileserveCom.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Crypter import Crypter + + +class FileserveCom(Crypter): +    __name__    = "FileserveCom" +    __type__    = "crypter" +    __version__ = "0.11" + +    __pattern__ = r'http://(?:www\.)?fileserve\.com/list/\w+' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """FileServe.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("fionnc", "fionnc@gmail.com")] + + +    FOLDER_PATTERN = r'<table class="file_list">(.*?)</table>' +    LINK_PATTERN = r'<a href="(.+?)" class="sheet_icon wbold">' + + +    def decrypt(self, pyfile): +        html = self.load(pyfile.url) + +        new_links = [] + +        folder = re.search(self.FOLDER_PATTERN, html, re.S) +        if folder is None: +            self.error(_("FOLDER_PATTERN not found")) + +        new_links.extend(re.findall(self.LINK_PATTERN, folder.group(1))) + +        if new_links: +            self.urls = [map(lambda s: "http://fileserve.com%s" % s, new_links)] diff --git a/pyload/plugin/crypter/FilesonicCom.py b/pyload/plugin/crypter/FilesonicCom.py new file mode 100644 index 000000000..60a6bc8be --- /dev/null +++ b/pyload/plugin/crypter/FilesonicCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class FilesonicCom(DeadCrypter): +    __name__    = "FilesonicCom" +    __type__    = "crypter" +    __version__ = "0.12" + +    __pattern__ = r'http://(?:www\.)?filesonic\.com/folder/\w+' +    __config__  = [] + +    __description__ = """Filesonic.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/crypter/FilestubeCom.py b/pyload/plugin/crypter/FilestubeCom.py new file mode 100644 index 000000000..7774d248e --- /dev/null +++ b/pyload/plugin/crypter/FilestubeCom.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class FilestubeCom(SimpleCrypter): +    __name__    = "FilestubeCom" +    __type__    = "crypter" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?filestube\.(?:com|to)/\w+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Filestube.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    LINK_PATTERN = r'<a class=\"file-link-main(?: noref)?\" [^>]* href=\"(http://[^\"]+)' +    NAME_PATTERN = r'<h1\s*> (?P<N>.+)  download\s*</h1>' diff --git a/pyload/plugin/crypter/FiletramCom.py b/pyload/plugin/crypter/FiletramCom.py new file mode 100644 index 000000000..47c8a7f54 --- /dev/null +++ b/pyload/plugin/crypter/FiletramCom.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class FiletramCom(SimpleCrypter): +    __name__    = "FiletramCom" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?filetram\.com/[^/]+/.+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Filetram.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("igel", "igelkun@myopera.com"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    LINK_PATTERN = r'\s+(http://.+)' +    NAME_PATTERN = r'<title>(?P<N>.+?) - Free Download' diff --git a/pyload/plugin/crypter/FiredriveCom.py b/pyload/plugin/crypter/FiredriveCom.py new file mode 100644 index 000000000..e258ed42f --- /dev/null +++ b/pyload/plugin/crypter/FiredriveCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class FiredriveCom(DeadCrypter): +    __name__    = "FiredriveCom" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'https?://(?:www\.)?(firedrive|putlocker)\.com/share/.+' +    __config__  = [] + +    __description__ = """Firedrive.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] diff --git a/pyload/plugin/crypter/FourChanOrg.py b/pyload/plugin/crypter/FourChanOrg.py new file mode 100644 index 000000000..bf80e3ca1 --- /dev/null +++ b/pyload/plugin/crypter/FourChanOrg.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# +# Based on 4chandl by Roland Beermann (https://gist.github.com/enkore/3492599) + +import re + +from pyload.plugin.Crypter import Crypter + + +class FourChanOrg(Crypter): +    __name__    = "FourChanOrg" +    __type__    = "crypter" +    __version__ = "0.31" + +    __pattern__ = r'http://(?:www\.)?boards\.4chan\.org/\w+/res/(\d+)' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """4chan.org folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [] + + +    def decrypt(self, pyfile): +        pagehtml = self.load(pyfile.url) +        images = set(re.findall(r'(images\.4chan\.org/[^/]*/src/[^"<]+)', pagehtml)) +        self.urls = ["http://" + image for image in images] diff --git a/pyload/plugin/crypter/FreakhareCom.py b/pyload/plugin/crypter/FreakhareCom.py new file mode 100644 index 000000000..c061a44d4 --- /dev/null +++ b/pyload/plugin/crypter/FreakhareCom.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class FreakhareCom(SimpleCrypter): +    __name__    = "FreakhareCom" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?freakshare\.com/folder/.+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Freakhare.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    LINK_PATTERN = r'<a href="(http://freakshare\.com/files/.+?)" target="_blank">' +    NAME_PATTERN = r'Folder:</b> (?P<N>.+)' +    PAGES_PATTERN = r'Pages: +(\d+)' + + +    def loadPage(self, page_n): +        if not hasattr(self, 'f_id') and not hasattr(self, 'f_md5'): +            m = re.search(r'http://freakshare.com/\?x=folder&f_id=(\d+)&f_md5=(\w+)', self.html) +            if m: +                self.f_id = m.group(1) +                self.f_md5 = m.group(2) +        return self.load('http://freakshare.com/', get={'x': 'folder', +                                                        'f_id': self.f_id, +                                                        'f_md5': self.f_md5, +                                                        'entrys': '20', +                                                        'page': page_n - 1, +                                                        'order': ''}, decode=True) diff --git a/pyload/plugin/crypter/FreetexthostCom.py b/pyload/plugin/crypter/FreetexthostCom.py new file mode 100644 index 000000000..b6f8998b9 --- /dev/null +++ b/pyload/plugin/crypter/FreetexthostCom.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class FreetexthostCom(SimpleCrypter): +    __name__    = "FreetexthostCom" +    __type__    = "crypter" +    __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?freetexthost\.com/\w+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Freetexthost.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    def getLinks(self): +        m = re.search(r'<div id="contentsinner">\s*(.+)<div class="viewcount">', self.html, re.S) +        if m is None: +            self.error(_("Unable to extract links")) +        links = m.group(1) +        return links.strip().split("<br />\r\n") diff --git a/pyload/plugin/crypter/FshareVn.py b/pyload/plugin/crypter/FshareVn.py new file mode 100644 index 000000000..45a174f25 --- /dev/null +++ b/pyload/plugin/crypter/FshareVn.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class FshareVn(SimpleCrypter): +    __name__    = "FshareVn" +    __type__    = "crypter" +    __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?fshare\.vn/folder/.+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Fshare.vn folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    LINK_PATTERN = r'<li class="w_80pc"><a href="(.+?)" target="_blank">' + diff --git a/pyload/plugin/crypter/Go4UpCom.py b/pyload/plugin/crypter/Go4UpCom.py new file mode 100644 index 000000000..0aa252ae3 --- /dev/null +++ b/pyload/plugin/crypter/Go4UpCom.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class Go4UpCom(SimpleCrypter): +    __name__    = "Go4UpCom" +    __type__    = "crypter" +    __version__ = "0.11" + +    __pattern__ = r'http://go4up\.com/(dl/\w{12}|rd/\w{12}/\d+)' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Go4Up.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("rlindner81", "rlindner81@gmail.com"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    LINK_PATTERN = r'(http://go4up\.com/rd/.+?)<' + +    NAME_PATTERN = r'<title>Download (.+?)<' + +    OFFLINE_PATTERN = r'>\s*(404 Page Not Found|File not Found|Mirror does not exist)' + + +    def getLinks(self +        links = [] + +        m = re.search(r'(/download/gethosts/.+?)"') +        if m: +            self.html = self.load(urljoin("http://go4up.com/", m.group(1))) +            pages = [self.load(url) for url in re.findall(self.LINK_PATTERN, self.html)] +        else: +            pages = [self.html] + +        for html in pages: +            try: +                links.append(re.search(r'<b><a href="(.+?)"', html).group(1)) +            except Exception: +                continue + +        return links diff --git a/pyload/plugin/crypter/GooGl.py b/pyload/plugin/crypter/GooGl.py new file mode 100644 index 000000000..ed01f047f --- /dev/null +++ b/pyload/plugin/crypter/GooGl.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Crypter import Crypter +from pyload.utils import json_loads + + +class GooGl(Crypter): +    __name__    = "GooGl" +    __type__    = "crypter" +    __version__ = "0.01" + +    __pattern__ = r'https?://(?:www\.)?goo\.gl/\w+' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Goo.gl decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    API_URL = "https://www.googleapis.com/urlshortener/v1/url" + + +    def decrypt(self, pyfile): +        rep = self.load(self.API_URL, get={'shortUrl': pyfile.url}) +        self.logDebug("JSON data: " + rep) +        rep = json_loads(rep) + +        if 'longUrl' in rep: +            self.urls = [rep['longUrl']] +        else: +            self.fail(_("Unable to expand shortened link")) diff --git a/pyload/plugin/crypter/HoerbuchIn.py b/pyload/plugin/crypter/HoerbuchIn.py new file mode 100644 index 000000000..6f5aa842f --- /dev/null +++ b/pyload/plugin/crypter/HoerbuchIn.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- + +import re + +from BeautifulSoup import BeautifulSoup, BeautifulStoneSoup + +from pyload.plugin.Crypter import Crypter + + +class HoerbuchIn(Crypter): +    __name__    = "HoerbuchIn" +    __type__    = "crypter" +    __version__ = "0.60" + +    __pattern__ = r'http://(?:www\.)?hoerbuch\.in/(wp/horbucher/\d+/.+/|tp/out\.php\?.+|protection/folder_\d+\.html)' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Hoerbuch.in decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org"), +                       ("mkaay", "mkaay@mkaay.de")] + + +    article = re.compile("http://(?:www\.)?hoerbuch\.in/wp/horbucher/\d+/.+/") +    protection = re.compile("http://(?:www\.)?hoerbuch\.in/protection/folder_\d+.html") + + +    def decrypt(self, pyfile): +        self.pyfile = pyfile + +        if self.article.match(pyfile.url): +            html = self.load(pyfile.url) +            soup = BeautifulSoup(html, convertEntities=BeautifulStoneSoup.HTML_ENTITIES) + +            abookname = soup.find("a", attrs={"rel": "bookmark"}).text +            for a in soup.findAll("a", attrs={"href": self.protection}): +                package = "%s (%s)" % (abookname, a.previousSibling.previousSibling.text[:-1]) +                links = self.decryptFolder(a['href']) + +                self.packages.append((package, links, package)) +        else: +            self.urls = self.decryptFolder(pyfile.url) + + +    def decryptFolder(self, url): +        m = self.protection.search(url) +        if m is None: +            self.fail(_("Bad URL")) +        url = m.group(0) + +        self.pyfile.url = url +        html = self.load(url, post={"viewed": "adpg"}) + +        links = [] +        pattern = re.compile("http://www\.hoerbuch\.in/protection/(\w+)/(.*?)\"") +        for hoster, lid in pattern.findall(html): +            self.req.lastURL = url +            self.load("http://www.hoerbuch.in/protection/%s/%s" % (hoster, lid)) +            links.append(self.req.lastEffectiveURL) + +        return links diff --git a/pyload/plugin/crypter/HotfileCom.py b/pyload/plugin/crypter/HotfileCom.py new file mode 100644 index 000000000..8085aa797 --- /dev/null +++ b/pyload/plugin/crypter/HotfileCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class HotfileCom(DeadCrypter): +    __name__    = "HotfileCom" +    __type__    = "crypter" +    __version__ = "0.30" + +    __pattern__ = r'https?://(?:www\.)?hotfile\.com/list/\w+/\w+' +    __config__  = [] + +    __description__ = """Hotfile.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org")] diff --git a/pyload/plugin/crypter/ILoadTo.py b/pyload/plugin/crypter/ILoadTo.py new file mode 100644 index 000000000..f333e262f --- /dev/null +++ b/pyload/plugin/crypter/ILoadTo.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class ILoadTo(DeadCrypter): +    __name__    = "ILoadTo" +    __type__    = "crypter" +    __version__ = "0.11" + +    __pattern__ = r'http://(?:www\.)?iload\.to/go/\d+-[\w.-]+/' +    __config__  = [] + +    __description__ = """Iload.to decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("hzpz", "")] diff --git a/pyload/plugin/crypter/ImgurComAlbum.py b/pyload/plugin/crypter/ImgurComAlbum.py new file mode 100644 index 000000000..89606716b --- /dev/null +++ b/pyload/plugin/crypter/ImgurComAlbum.py @@ -0,0 +1,28 @@ +import re + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter +from pyload.utils import uniqify + + +class ImgurComAlbum(SimpleCrypter): +    __name__    = "ImgurComAlbum" +    __type__    = "crypter" +    __version__ = "0.51" + +    __pattern__ = r'https?://(?:www\.|m\.)?imgur\.com/(a|gallery|)/?\w{5,7}' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Imgur.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("nath_schwarz", "nathan.notwhite@gmail.com")] + + +    NAME_PATTERN = r'(?P<N>.+?) - Imgur' +    LINK_PATTERN = r'i\.imgur\.com/\w{7}s?\.(?:jpeg|jpg|png|gif|apng)' + + +    def getLinks(self): +        f = lambda url: "http://" + re.sub(r'(\w{7})s\.', r'\1.', url) +        return uniqify(map(f, re.findall(self.LINK_PATTERN, self.html))) diff --git a/pyload/plugin/crypter/LetitbitNet.py b/pyload/plugin/crypter/LetitbitNet.py new file mode 100644 index 000000000..587c75c80 --- /dev/null +++ b/pyload/plugin/crypter/LetitbitNet.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +import re +from pyload.plugin.Crypter import Crypter + + +class LetitbitNet(Crypter): +    __name__    = "LetitbitNet" +    __type__    = "crypter" +    __version__ = "0.10" + +    __pattern__ = r'http://(?:www\.)?letitbit\.net/folder/\w+' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Letitbit.net folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("DHMH", "webmaster@pcProfil.de"), +                       ("z00nx", "z00nx0@gmail.com")] + + +    FOLDER_PATTERN = r'<table>(.*)</table>' +    LINK_PATTERN = r'<a href="(.+?)" target="_blank">' + + +    def decrypt(self, pyfile): +        html = self.load(pyfile.url) + +        folder = re.search(self.FOLDER_PATTERN, html, re.S) +        if folder is None: +            self.error(_("FOLDER_PATTERN not found")) + +        self.urls.extend(re.findall(self.LINK_PATTERN, folder.group(0))) diff --git a/pyload/plugin/crypter/LinkCryptWs.py b/pyload/plugin/crypter/LinkCryptWs.py new file mode 100644 index 000000000..a900248ef --- /dev/null +++ b/pyload/plugin/crypter/LinkCryptWs.py @@ -0,0 +1,322 @@ +# -*- coding: utf-8 -*- + +import binascii +import re + +import pycurl + +from Crypto.Cipher import AES + +from pyload.plugin.Crypter import Crypter +from pyload.utils import html_unescape + + +class LinkCryptWs(Crypter): +    __name__    = "LinkCryptWs" +    __type__    = "crypter" +    __version__ = "0.08" + +    __pattern__ = r'http://(?:www\.)?linkcrypt\.ws/(dir|container)/(?P<ID>\w+)' + +    __description__ = """LinkCrypt.ws decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("kagenoshin", "kagenoshin[AT]gmx[DOT]ch"), +                       ("glukgluk", ""), +                       ("Gummibaer", "")] + + +    CRYPTED_KEY = "crypted" +    JK_KEY = "jk" + + +    def setup(self): +        self.captcha = False +        self.links   = [] +        self.sources = ['cnl', 'web', 'dlc', 'rsdf', 'ccf'] + + +    def prepare(self): +        # Init +        self.fileid = re.match(self.__pattern__, self.pyfile.url).group('ID') + +        self.req.cj.setCookie("linkcrypt.ws", "language", "en") + +        # Request package +        self.req.http.c.setopt(pycurl.USERAGENT, "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko")  #: better chance to not get those key-captchas +        self.html = self.load(self.pyfile.url) + + +    def decrypt(self, pyfile): +        if not self.js: +            self.fail(_("Missing JS Engine")) + +        self.prepare() + +        if not self.isOnline(): +            self.offline() + +        if self.isKeyCaptchaProtected(): +            self.retry(8, 15, _("Can't handle Key-Captcha")) + +        if self.isCaptchaProtected(): +            self.captcha = True +            self.unlockCaptchaProtection() +            self.handleCaptchaErrors() + +        # Check for protection +        if self.isPasswordProtected(): +            self.unlockPasswordProtection() +            self.handleErrors() + +        # get unrar password +        self.getunrarpw() + +        # Get package name and folder +        package_name, folder_name = self.getPackageInfo() + +        #get the container definitions from script section +        self.get_container_html() + +        # Extract package links +        for type in self.sources: +            links = self.handleLinkSource(type) + +            if links: +                self.links.extend(links) +                break + +        if self.links: +            self.packages = [(package_name, self.links, folder_name)] + + +    def isOnline(self): +        if "<title>Linkcrypt.ws // Error 404</title>" in self.html: +            self.logDebug("folder doesen't exist anymore") +            return False +        else: +            return True + + +    def isPasswordProtected(self): +        if "Authorizing" in self.html: +            self.logDebug("Links are password protected") +            return True +        else: +            return False + + +    def isCaptchaProtected(self): +        if 'id="captcha">' in self.html: +            self.logDebug("Links are captcha protected") +            return True +        else: +            return False + + +    def isKeyCaptchaProtected(self): +        if re.search(r'>If the folder does not open after klick on <', self.html, re.I): +            return True +        else: +            return False + + +    def unlockPasswordProtection(self): +        password = self.getPassword() + +        if password: +            self.logDebug("Submitting password [%s] for protected links" % password) +            self.html = self.load(self.pyfile.url, post={"password": password, 'x': "0", 'y': "0"}) +        else: +            self.fail(_("Folder is password protected")) + + +    def unlockCaptchaProtection(self): +        captcha_url  = re.search(r'<form.*?id\s*?=\s*?"captcha"[^>]*?>.*?<\s*?input.*?src="(.+?)"', self.html, re.I | re.S).group(1) +        captcha_code = self.decryptCaptcha(captcha_url, forceUser=True, imgtype="gif", result_type='positional') + +        self.html = self.load(self.pyfile.url, post={"x": captcha_code[0], "y": captcha_code[1]}) + + +    def getPackageInfo(self): +        name   = self.pyfile.package().name +        folder = self.pyfile.package().folder + +        self.logDebug("Defaulting to pyfile name [%s] and folder [%s] for package" % (name, folder)) + +        return name, folder + + +    def getunrarpw(self): +        sitein = self.html +        indexi = sitein.find("|source|") + 8 +        indexe = sitein.find("|",indexi) + +        unrarpw = sitein[indexi:indexe] + +        if not (unrarpw == "Password" or "Dateipasswort") : +            self.logDebug("File password set to: [%s]"% unrarpw) +            self.pyfile.package().password = unrarpw + + +    def handleErrors(self): +        if self.isPasswordProtected(): +            self.fail(_("Incorrect password")) + + +    def handleCaptchaErrors(self): +        if self.captcha: +            if "Your choice was wrong!" in self.html: +                self.invalidCaptcha() +                self.retry() +            else: +                self.correctCaptcha() + + +    def handleLinkSource(self, type): +        if type == 'cnl': +                return self.handleCNL2() + +        elif type == 'web': +                return self.handleWebLinks() + +        elif type in ('rsdf', 'ccf', 'dlc'): +                return self.handleContainer(type) + +        else: +            self.fail(_("Unknown source type: %s") % type)  #@TODO: Replace with self.error in 0.4.10 + + +    def handleWebLinks(self): +        self.logDebug("Search for Web links ") + +        package_links = [] +        pattern = r'<form action="http://linkcrypt.ws/out.html"[^>]*?>.*?<input[^>]*?value="(.+?)"[^>]*?name="file"' +        ids = re.findall(pattern, self.html, re.I | re.S) + +        self.logDebug("Decrypting %d Web links" % len(ids)) + +        for idx, weblink_id in enumerate(ids): +            try: +                self.logDebug("Decrypting Web link %d, %s" % (idx + 1, weblink_id)) + +                res = self.load("http://linkcrypt.ws/out.html", post = {'file':weblink_id}) + +                indexs = res.find("window.location =") + 19 +                indexe = res.find('"', indexs) + +                link2 = res[indexs:indexe] + +                self.logDebug(link2) + +                link2 = html_unescape(link2) +                package_links.append(link2) + +            except Exception, detail: +                self.logDebug("Error decrypting Web link %s, %s" % (weblink_id, detail)) + +        return package_links + + +    def get_container_html(self): +        self.container_html = [] + +        script = re.search(r'<div.*?id="ad_cont".*?<script.*?javascrip[^>]*?>(.*?)</script', self.html, re.I | re.S) + +        if script: +            container_html_text = script.group(1) +            container_html_text.strip() +            self.container_html = container_html_text.splitlines() + + +    def handle_javascript(self, line): +        return self.js.eval(line.replace('{}))',"{}).replace('document.open();document.write','').replace(';document.close();',''))")) + + +    def handleContainer(self, type): +        package_links = [] +        type = type.lower() + +        self.logDebug('Search for %s Container links' % type.upper()) + +        if not type.isalnum():  # check to prevent broken re-pattern (cnl2,rsdf,ccf,dlc,web are all alpha-numeric) +            self.fail(_("Unknown container type: %s") % type)  #@TODO: Replace with self.error in 0.4.10 + +        for line in self.container_html: +            if type in line: +                jseval = self.handle_javascript(line) +                clink = re.search(r'href=["\'](["\']+)', jseval, re.I) + +                if not clink: +                    continue + +                self.logDebug("clink avaible") + +                package_name, folder_name = self.getPackageInfo() +                self.logDebug("Added package with name %s.%s and container link %s" %( package_name, type, clink.group(1))) +                self.core.api.uploadContainer( "%s.%s" %(package_name, type), self.load(clink.group(1))) +                return "Found it" + +        return package_links + + +    def handleCNL2(self): +        self.logDebug("Search for CNL links") + +        package_links = [] +        cnl_line = None + +        for line in self.container_html: +            if "cnl" in line: +                cnl_line = line +                break + +        if cnl_line: +            self.logDebug("cnl_line gefunden") + +        try: +            cnl_section = self.handle_javascript(cnl_line) +            (vcrypted, vjk) = self._getCipherParams(cnl_section) +            for (crypted, jk) in zip(vcrypted, vjk): +                package_links.extend(self._getLinks(crypted, jk)) +        except Exception: +            self.logError(_("Unable to decrypt CNL links (JS Error) try to get over links")) +            return self.handleWebLinks() + +        return package_links + + +    def _getCipherParams(self, cnl_section): +        # Get jk +        jk_re = r'<INPUT.*?NAME="%s".*?VALUE="(.*?)"' % LinkCryptWs.JK_KEY +        vjk = re.findall(jk_re, cnl_section) + +        # Get crypted +        crypted_re = r'<INPUT.*?NAME="%s".*?VALUE="(.*?)"' % LinkCryptWs.CRYPTED_KEY +        vcrypted = re.findall(crypted_re, cnl_section) + +        # Log and return +        self.logDebug("Detected %d crypted blocks" % len(vcrypted)) +        return vcrypted, vjk + + +    def _getLinks(self, crypted, jk): +        # Get key +        jreturn = self.js.eval("%s f()" % jk) +        key     = binascii.unhexlify(jreturn) + +        self.logDebug("JsEngine returns value [%s]" % jreturn) + +        # Decrypt +        Key  = key +        IV   = key +        obj  = AES.new(Key, AES.MODE_CBC, IV) +        text = obj.decrypt(crypted.decode('base64')) + +        # Extract links +        text  = text.replace("\x00", "").replace("\r", "") +        links = filter(bool, text.split('\n')) + +        # Log and return +        self.logDebug("Package has %d links" % len(links)) + +        return links diff --git a/pyload/plugin/crypter/LinkSaveIn.py b/pyload/plugin/crypter/LinkSaveIn.py new file mode 100644 index 000000000..752eee390 --- /dev/null +++ b/pyload/plugin/crypter/LinkSaveIn.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleDereferer import SimpleDereferer + + +class LinkSaveIn(SimpleDereferer): +    __name__    = "LinkSaveIn" +    __type__    = "crypter" +    __version__ = "2.03" + +    __pattern__ = r'https?://(?:www\.)?linksave\.in/\w+' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """LinkSave.in decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    COOKIES = [("linksave.in", "Linksave_Language", "english")] + +    OFFLINE_PATTERN = r'>(Error )?404 -' diff --git a/pyload/plugin/crypter/LinkdecrypterCom.py b/pyload/plugin/crypter/LinkdecrypterCom.py new file mode 100644 index 000000000..1ba2dc377 --- /dev/null +++ b/pyload/plugin/crypter/LinkdecrypterCom.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Crypter import Crypter + + +class LinkdecrypterCom(Crypter): +    __name__    = "LinkdecrypterCom" +    __type__    = "crypter" +    __version__ = "0.29" + +    __pattern__ = r'^unmatchable$' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Linkdecrypter.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("flowlee", "")] + + +    TEXTAREA_PATTERN = r'<textarea name="links" wrap="off" readonly="1" class="caja_des">(.+)</textarea>' +    PASSWORD_PATTERN = r'<input type="text" name="password"' +    CAPTCHA_PATTERN  = r'<img class="captcha" src="(.+?)"(.*?)>' +    REDIR_PATTERN    = r'<i>(Click <a href="./">here</a> if your browser does not redirect you).</i>' + + +    def setup(self): +        self.password = self.getPassword() +        self.req.setOption("timeout", 300) + + +    def decrypt(self, pyfile): +        retries = 5 + +        post_dict = {"link_cache": "on", "pro_links": pyfile.url, "modo_links": "text"} +        self.html = self.load('http://linkdecrypter.com/', post=post_dict, decode=True) + +        while retries: +            m = re.search(self.TEXTAREA_PATTERN, self.html, re.S) +            if m: +                self.urls = [x for x in m.group(1).splitlines() if '[LINK-ERROR]' not in x] + +            m = re.search(self.CAPTCHA_PATTERN, self.html) +            if m: +                captcha_url = 'http://linkdecrypter.com/' + m.group(1) +                result_type = "positional" if "getPos" in m.group(2) else "textual" + +                m = re.search(r"<p><i><b>([^<]+)</b></i></p>", self.html) +                msg = m.group(1) if m else "" +                self.logInfo(_("Captcha protected link"), result_type, msg) + +                captcha = self.decryptCaptcha(captcha_url, result_type=result_type) +                if result_type == "positional": +                    captcha = "%d|%d" % captcha +                self.html = self.load('http://linkdecrypter.com/', post={"captcha": captcha}, decode=True) +                retries -= 1 + +            elif self.PASSWORD_PATTERN in self.html: +                if self.password: +                    self.logInfo(_("Password protected link")) +                    self.html = self.load('http://linkdecrypter.com/', post={'password': self.password}, decode=True) +                else: +                    self.fail(_("Missing password")) + +            else: +                retries -= 1 +                self.html = self.load('http://linkdecrypter.com/', decode=True) diff --git a/pyload/plugin/crypter/LixIn.py b/pyload/plugin/crypter/LixIn.py new file mode 100644 index 000000000..95d7c34c5 --- /dev/null +++ b/pyload/plugin/crypter/LixIn.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Crypter import Crypter + + +class LixIn(Crypter): +    __name__    = "LixIn" +    __type__    = "crypter" +    __version__ = "0.22" + +    __pattern__ = r'http://(?:www\.)?lix\.in/(?P<ID>.+)' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Lix.in decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org")] + + +    CAPTCHA_PATTERN = r'<img src="(captcha_img\.php\?.*?)"' +    SUBMIT_PATTERN = r'value=\'continue.*?\'' +    LINK_PATTERN = r'name="ifram" src="(.*?)"' + + +    def decrypt(self, pyfile): +        url = pyfile.url + +        m = re.match(self.__pattern__, url) +        if m is None: +            self.error(_("Unable to identify file ID")) + +        id = m.group('ID') +        self.logDebug("File id is %s" % id) + +        self.html = self.load(url, decode=True) + +        m = re.search(self.SUBMIT_PATTERN, self.html) +        if m is None: +            self.error(_("Link doesn't seem valid")) + +        m = re.search(self.CAPTCHA_PATTERN, self.html) +        if m: +            for _i in xrange(5): +                m = re.search(self.CAPTCHA_PATTERN, self.html) +                if m: +                    self.logDebug("Trying captcha") +                    captcharesult = self.decryptCaptcha("http://lix.in/" + m.group(1)) +                self.html = self.load(url, decode=True, +                                          post={"capt": captcharesult, "submit": "submit", "tiny": id}) +            else: +                self.logDebug("No captcha/captcha solved") +        else: +            self.html = self.load(url, decode=True, post={"submit": "submit", "tiny": id}) + +        m = re.search(self.LINK_PATTERN, self.html) +        if m is None: +            self.error(_("Unable to find destination url")) +        else: +            self.urls = [m.group(1)] +            self.logDebug("Found link %s, adding to package" % self.urls[0]) diff --git a/pyload/plugin/crypter/LofCc.py b/pyload/plugin/crypter/LofCc.py new file mode 100644 index 000000000..076ab59d7 --- /dev/null +++ b/pyload/plugin/crypter/LofCc.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class LofCc(DeadCrypter): +    __name__    = "LofCc" +    __type__    = "crypter" +    __version__ = "0.21" + +    __pattern__ = r'http://(?:www\.)?lof\.cc/(.+)' +    __config__  = [] + +    __description__ = """Lof.cc decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de")] diff --git a/pyload/plugin/crypter/MBLinkInfo.py b/pyload/plugin/crypter/MBLinkInfo.py new file mode 100644 index 000000000..7e7be2029 --- /dev/null +++ b/pyload/plugin/crypter/MBLinkInfo.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class MBLinkInfo(DeadCrypter): +    __name__    = "MBLinkInfo" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?mblink\.info/?\?id=(\d+)' +    __config__  = [] + +    __description__ = """MBLink.info decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Gummibaer", "Gummibaer@wiki-bierkiste.de"), +                       ("stickell", "l.stickell@yahoo.it")] diff --git a/pyload/plugin/crypter/MediafireCom.py b/pyload/plugin/crypter/MediafireCom.py new file mode 100644 index 000000000..f9f7a43bd --- /dev/null +++ b/pyload/plugin/crypter/MediafireCom.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- + +import re +from pyload.plugin.Crypter import Crypter +from pyload.plugin.hoster.MediafireCom import checkHTMLHeader +from pyload.utils import json_loads + + +class MediafireCom(Crypter): +    __name__    = "MediafireCom" +    __type__    = "crypter" +    __version__ = "0.14" + +    __pattern__ = r'http://(?:www\.)?mediafire\.com/(folder/|\?sharekey=|\?\w{13}($|[/#]))' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Mediafire.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    FOLDER_KEY_PATTERN = r'var afI= \'(\w+)' +    LINK_PATTERN = r'<meta property="og:url" content="http://www\.mediafire\.com/\?(\w+)"/>' + + +    def decrypt(self, pyfile): +        url, result = checkHTMLHeader(pyfile.url) +        self.logDebug("Location (%d): %s" % (result, url)) + +        if result == 0: +            # load and parse html +            html = self.load(pyfile.url) +            m = re.search(self.LINK_PATTERN, html) +            if m: +                # file page +                self.urls.append("http://www.mediafire.com/file/%s" % m.group(1)) +            else: +                # folder page +                m = re.search(self.FOLDER_KEY_PATTERN, html) +                if m: +                    folder_key = m.group(1) +                    self.logDebug("FOLDER KEY: %s" % folder_key) + +                    json_resp = json_loads(self.load("http://www.mediafire.com/api/folder/get_info.php", +                                                     get={'folder_key'     : folder_key, +                                                          'response_format': "json", +                                                          'version'        : 1})) +                    #self.logInfo(json_resp) +                    if json_resp['response']['result'] == "Success": +                        for link in json_resp['response']['folder_info']['files']: +                            self.urls.append("http://www.mediafire.com/file/%s" % link['quickkey']) +                    else: +                        self.fail(json_resp['response']['message']) +        elif result == 1: +            self.offline() +        else: +            self.urls.append(url) diff --git a/pyload/plugin/crypter/MegaCoNz.py b/pyload/plugin/crypter/MegaCoNz.py new file mode 100644 index 000000000..10c1031de --- /dev/null +++ b/pyload/plugin/crypter/MegaCoNz.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Crypter import Crypter + + +class MegaCoNz(Crypter): +    __name__    = "MegaCoNz" +    __type__    = "crypter" +    __version__ = "0.04" + +    __pattern__ = r'(?:https?://(?:www\.)?mega\.co\.nz/|mega:|chrome:.+?)#F!(?P<ID>[\w^_]+)!(?P<KEY>[\w,\\-]+)' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Mega.co.nz folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    def setup(self): +        self.req.setOption("timeout", 300) + + +    def decrypt(self, pyfile): +        url       = "https://mega.co.nz/#F!%s!%s" % re.match(self.__pattern__, pyfile.url).groups() +        self.html = self.load("http://rapidgen.org/linkfinder", post={'linklisturl': url}) +        self.urls = re.findall(r'(https://mega.co.nz/#N!.+?)<', self.html) + +        if not self.urls:  #@TODO: Remove in 0.4.10 +            self.fail(_("No link grabbed")) diff --git a/pyload/plugin/crypter/MegaRapidCz.py b/pyload/plugin/crypter/MegaRapidCz.py new file mode 100644 index 000000000..1ff859ce5 --- /dev/null +++ b/pyload/plugin/crypter/MegaRapidCz.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class MegaRapidCz(SimpleCrypter): +    __name__    = "MegaRapidCz" +    __type__    = "crypter" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?(share|mega)rapid\.cz/slozka/\d+/\w+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Share-Rapid.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    LINK_PATTERN = r'<td class="soubor".*?><a href="(.+?)">' + diff --git a/pyload/plugin/crypter/MegauploadCom.py b/pyload/plugin/crypter/MegauploadCom.py new file mode 100644 index 000000000..b9c675a7e --- /dev/null +++ b/pyload/plugin/crypter/MegauploadCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class MegauploadCom(DeadCrypter): +    __name__    = "MegauploadCom" +    __type__    = "crypter" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?megaupload\.com/(\?f|xml/folderfiles\.php\?.*&?folderid)=\w+' +    __config__  = [] + +    __description__ = """Megaupload.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/crypter/Movie2KTo.py b/pyload/plugin/crypter/Movie2KTo.py new file mode 100644 index 000000000..f8ff4e756 --- /dev/null +++ b/pyload/plugin/crypter/Movie2KTo.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class Movie2KTo(DeadCrypter): +    __name__    = "Movie2KTo" +    __type__    = "crypter" +    __version__ = "0.51" + +    __pattern__ = r'http://(?:www\.)?movie2k\.to/(.+)\.html' +    __config__  = [] + +    __description__ = """Movie2k.to decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("4Christopher", "4Christopher@gmx.de")] diff --git a/pyload/plugin/crypter/MultiUpOrg.py b/pyload/plugin/crypter/MultiUpOrg.py new file mode 100644 index 000000000..eabc5d219 --- /dev/null +++ b/pyload/plugin/crypter/MultiUpOrg.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +import re +from urlparse import urljoin + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class MultiUpOrg(SimpleCrypter): +    __name__    = "MultiUpOrg" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?multiup\.org/(en|fr)/(?P<TYPE>project|download|miror)/\w+(/\w+)?' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """MultiUp.org decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    NAME_PATTERN = r'<title>.*(?:Project|Projet|ownload|élécharger) (?P<N>.+?) (\(|- )' + + +    def getLinks(self): +        m_type = re.match(self.__pattern__, self.pyfile.url).group('TYPE') + +        if m_type == "project": +            pattern = r'\n(http://www\.multiup\.org/(?:en|fr)/download/.*)' +        else: +            pattern = r'style="width:97%;text-align:left".*\n.*href="(.*)"' +            if m_type == "download": +                dl_pattern = r'href="(.*)">.*\n.*<h5>DOWNLOAD</h5>' +                miror_page = urljoin("http://www.multiup.org", re.search(dl_pattern, self.html).group(1)) +                self.html = self.load(miror_page) + +        return re.findall(pattern, self.html) diff --git a/pyload/plugin/crypter/MultiloadCz.py b/pyload/plugin/crypter/MultiloadCz.py new file mode 100644 index 000000000..de29aea0f --- /dev/null +++ b/pyload/plugin/crypter/MultiloadCz.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +import re +from pyload.plugin.Crypter import Crypter + + +class MultiloadCz(Crypter): +    __name__    = "MultiloadCz" +    __type__    = "crypter" +    __version__ = "0.40" + +    __pattern__ = r'http://(?:[^/]*\.)?multiload\.cz/(stahnout|slozka)/.+' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"           , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package" , True), +                   ("usedHoster"        , "str" , "Prefered hoster list (bar-separated)", ""  ), +                   ("ignoredHoster"     , "str" , "Ignored hoster list (bar-separated)" , ""  )] + +    __description__ = """Multiload.cz decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    FOLDER_PATTERN = r'<form action="" method="get"><textarea.*?>([^>]*)</textarea></form>' +    LINK_PATTERN = r'<p class="manager-server"><strong>([^<]+)</strong></p><p class="manager-linky"><a href="(.+?)">' + + +    def decrypt(self, pyfile): +        self.html = self.load(pyfile.url, decode=True) + +        if re.match(self.__pattern__, pyfile.url).group(1) == "slozka": +            m = re.search(self.FOLDER_PATTERN, self.html) +            if m: +                self.urls.extend(m.group(1).split()) +        else: +            m = re.findall(self.LINK_PATTERN, self.html) +            if m: +                prefered_set = set(self.getConfig('usedHoster').split('|')) +                self.urls.extend(x[1] for x in m if x[0] in prefered_set) + +                if not self.urls: +                    ignored_set = set(self.getConfig('ignoredHoster').split('|')) +                    self.urls.extend(x[1] for x in m if x[0] not in ignored_set) diff --git a/pyload/plugin/crypter/MultiuploadCom.py b/pyload/plugin/crypter/MultiuploadCom.py new file mode 100644 index 000000000..9127140f1 --- /dev/null +++ b/pyload/plugin/crypter/MultiuploadCom.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class MultiuploadCom(DeadCrypter): +    __name__    = "MultiuploadCom" +    __type__    = "crypter" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?multiupload\.(com|nl)/\w+' + +    __description__ = """MultiUpload.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/crypter/NCryptIn.py b/pyload/plugin/crypter/NCryptIn.py new file mode 100644 index 000000000..64ea8727f --- /dev/null +++ b/pyload/plugin/crypter/NCryptIn.py @@ -0,0 +1,310 @@ +# -*- coding: utf-8 -*- + +import binascii +import re + +from Crypto.Cipher import AES + +from pyload.plugin.Crypter import Crypter +from pyload.plugin.captcha.ReCaptcha import ReCaptcha + + +class NCryptIn(Crypter): +    __name__    = "NCryptIn" +    __type__    = "crypter" +    __version__ = "1.34" + +    __pattern__ = r'http://(?:www\.)?ncrypt\.in/(?P<TYPE>folder|link|frame)-([^/\?]+)' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """NCrypt.in decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("fragonib", "fragonib[AT]yahoo[DOT]es"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    JK_KEY = "jk" +    CRYPTED_KEY = "crypted" + +    NAME_PATTERN = r'<meta name="description" content="(?P<N>.+?)"' + + +    def setup(self): +        self.package = None +        self.cleanedHtml = None +        self.links_source_order = ["cnl2", "rsdf", "ccf", "dlc", "web"] +        self.protection_type = None + + +    def decrypt(self, pyfile): +        # Init +        self.package = pyfile.package() +        package_links = [] +        package_name = self.package.name +        folder_name = self.package.folder + +        # Deal with single links +        if self.isSingleLink(): +            package_links.extend(self.handleSingleLink()) + +        # Deal with folders +        else: + +            # Request folder home +            self.html = self.requestFolderHome() +            self.cleanedHtml = self.removeHtmlCrap(self.html) +            if not self.isOnline(): +                self.offline() + +            # Check for folder protection +            if self.isProtected(): +                self.html = self.unlockProtection() +                self.cleanedHtml = self.removeHtmlCrap(self.html) +                self.handleErrors() + +            # Prepare package name and folder +            (package_name, folder_name) = self.getPackageInfo() + +            # Extract package links +            for link_source_type in self.links_source_order: +                package_links.extend(self.handleLinkSource(link_source_type)) +                if package_links:  # use only first source which provides links +                    break +            package_links = set(package_links) + +        # Pack and return links +        if package_links: +            self.packages = [(package_name, package_links, folder_name)] + + +    def isSingleLink(self): +        link_type = re.match(self.__pattern__, self.pyfile.url).group('TYPE') +        return link_type in ("link", "frame") + + +    def requestFolderHome(self): +        return self.load(self.pyfile.url, decode=True) + + +    def removeHtmlCrap(self, content): +        patterns = (r'(type="hidden".*?(name=".*?")?.*?value=".*?")', +                    r'display:none;">(.*?)</(div|span)>', +                    r'<div\s+class="jdownloader"(.*?)</div>', +                    r'<table class="global">(.*?)</table>', +                    r'<iframe\s+style="display:none(.*?)</iframe>') +        for pattern in patterns: +            rexpr = re.compile(pattern, re.S) +            content = re.sub(rexpr, "", content) +        return content + + +    def isOnline(self): +        if "Your folder does not exist" in self.cleanedHtml: +            self.logDebug("File not m") +            return False +        return True + + +    def isProtected(self): +        form = re.search(r'<form.*?name.*?protected.*?>(.*?)</form>', self.cleanedHtml, re.S) +        if form: +            content = form.group(1) +            for keyword in ("password", "captcha"): +                if keyword in content: +                    self.protection_type = keyword +                    self.logDebug("Links are %s protected" % self.protection_type) +                    return True +        return False + + +    def getPackageInfo(self): +        m = re.search(self.NAME_PATTERN, self.html) +        if m: +            name = folder = m.group('N').strip() +            self.logDebug("Found name [%s] and folder [%s] in package info" % (name, folder)) +        else: +            name = self.package.name +            folder = self.package.folder +            self.logDebug("Package info not m, defaulting to pyfile name [%s] and folder [%s]" % (name, folder)) +        return name, folder + + +    def unlockProtection(self): +        postData = {} + +        form = re.search(r'<form name="protected"(.*?)</form>', self.cleanedHtml, re.S).group(1) + +        # Submit package password +        if "password" in form: +            password = self.getPassword() +            self.logDebug("Submitting password [%s] for protected links" % password) +            postData['password'] = password + +        # Resolve anicaptcha +        if "anicaptcha" in form: +            self.logDebug("Captcha protected") +            captchaUri = re.search(r'src="(/temp/anicaptcha/.+?)"', form).group(1) +            captcha = self.decryptCaptcha("http://ncrypt.in" + captchaUri) +            self.logDebug("Captcha resolved [%s]" % captcha) +            postData['captcha'] = captcha + +        # Resolve recaptcha +        if "recaptcha" in form: +            self.logDebug("ReCaptcha protected") +            captcha_key = re.search(r'\?k=(.*?)"', form).group(1) +            self.logDebug("Resolving ReCaptcha with key [%s]" % captcha_key) +            recaptcha = ReCaptcha(self) +            response, challenge = recaptcha.challenge(captcha_key) +            postData['recaptcha_challenge_field'] = challenge +            postData['recaptcha_response_field']  = response + +        # Resolve circlecaptcha +        if "circlecaptcha" in form: +            self.logDebug("CircleCaptcha protected") +            captcha_img_url = "http://ncrypt.in/classes/captcha/circlecaptcha.php" +            coords = self.decryptCaptcha(captcha_img_url, forceUser=True, imgtype="png", result_type='positional') +            self.logDebug("Captcha resolved, coords [%s]" % str(coords)) +            postData['circle.x'] = coords[0] +            postData['circle.y'] = coords[1] + +        # Unlock protection +        postData['submit_protected'] = 'Continue to folder' +        return self.load(self.pyfile.url, post=postData, decode=True) + + +    def handleErrors(self): +        if self.protection_type == "password": +            if "This password is invalid!" in self.cleanedHtml: +                self.logDebug("Incorrect password, please set right password on 'Edit package' form and retry") +                self.fail(_("Incorrect password, please set right password on 'Edit package' form and retry")) + +        if self.protection_type == "captcha": +            if "The securitycheck was wrong!" in self.cleanedHtml: +                self.invalidCaptcha() +                self.retry() +            else: +                self.correctCaptcha() + + +    def handleLinkSource(self, link_source_type): +        # Check for JS engine +        require_js_engine = link_source_type in ("cnl2", "rsdf", "ccf", "dlc") +        if require_js_engine and not self.js: +            self.logDebug("No JS engine available, skip %s links" % link_source_type) +            return [] + +        # Select suitable handler +        if link_source_type == 'single': +            return self.handleSingleLink() +        if link_source_type == 'cnl2': +            return self.handleCNL2() +        elif link_source_type in ("rsdf", "ccf", "dlc"): +            return self.handleContainer(link_source_type) +        elif link_source_type == "web": +            return self.handleWebLinks() +        else: +            self.error(_('Unknown source type "%s"') % link_source_type) + + +    def handleSingleLink(self): +        self.logDebug("Handling Single link") +        package_links = [] + +        # Decrypt single link +        decrypted_link = self.decryptLink(self.pyfile.url) +        if decrypted_link: +            package_links.append(decrypted_link) + +        return package_links + + +    def handleCNL2(self): +        self.logDebug("Handling CNL2 links") +        package_links = [] + +        if 'cnl2_output' in self.cleanedHtml: +            try: +                (vcrypted, vjk) = self._getCipherParams() +                for (crypted, jk) in zip(vcrypted, vjk): +                    package_links.extend(self._getLinks(crypted, jk)) +            except Exception: +                self.fail(_("Unable to decrypt CNL2 links")) + +        return package_links + + +    def handleContainers(self): +        self.logDebug("Handling Container links") +        package_links = [] + +        pattern = r'/container/(rsdf|dlc|ccf)/(\w+)' +        containersLinks = re.findall(pattern, self.html) +        self.logDebug("Decrypting %d Container links" % len(containersLinks)) +        for containerLink in containersLinks: +            link = "http://ncrypt.in/container/%s/%s.%s" % (containerLink[0], containerLink[1], containerLink[0]) +            package_links.append(link) + +        return package_links + + +    def handleWebLinks(self): +        self.logDebug("Handling Web links") +        pattern = r'(http://ncrypt\.in/link-.*?=)' +        links = re.findall(pattern, self.html) + +        package_links = [] +        self.logDebug("Decrypting %d Web links" % len(links)) +        for i, link in enumerate(links): +            self.logDebug("Decrypting Web link %d, %s" % (i + 1, link)) +            decrypted_link = self.decrypt(link) +            if decrypted_link: +                package_links.append(decrypted_link) + +        return package_links + + +    def decryptLink(self, link): +        try: +            url = link.replace("link-", "frame-") +            link = self.load(url, just_header=True)['location'] +            return link +        except Exception, detail: +            self.logDebug("Error decrypting link %s, %s" % (link, detail)) + + +    def _getCipherParams(self): +        pattern = r'<input.*?name="%s".*?value="(.*?)"' + +        # Get jk +        jk_re = pattern % NCryptIn.JK_KEY +        vjk = re.findall(jk_re, self.html) + +        # Get crypted +        crypted_re = pattern % NCryptIn.CRYPTED_KEY +        vcrypted = re.findall(crypted_re, self.html) + +        # Log and return +        self.logDebug("Detected %d crypted blocks" % len(vcrypted)) +        return vcrypted, vjk + + +    def _getLinks(self, crypted, jk): +        # Get key +        jreturn = self.js.eval("%s f()" % jk) +        self.logDebug("JsEngine returns value [%s]" % jreturn) +        key = binascii.unhexlify(jreturn) + +        # Decrypt +        Key = key +        IV = key +        obj = AES.new(Key, AES.MODE_CBC, IV) +        text = obj.decrypt(crypted.decode('base64')) + +        # Extract links +        text = text.replace("\x00", "").replace("\r", "") +        links = filter(bool, text.split('\n')) + +        # Log and return +        self.logDebug("Block has %d links" % len(links)) +        return links diff --git a/pyload/plugin/crypter/NetfolderIn.py b/pyload/plugin/crypter/NetfolderIn.py new file mode 100644 index 000000000..9672ff581 --- /dev/null +++ b/pyload/plugin/crypter/NetfolderIn.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class NetfolderIn(SimpleCrypter): +    __name__    = "NetfolderIn" +    __type__    = "crypter" +    __version__ = "0.72" + +    __pattern__ = r'http://(?:www\.)?netfolder\.in/(folder\.php\?folder_id=)?(?P<ID>\w+)(?(1)|/\w+)' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """NetFolder.in decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org"), +                       ("fragonib", "fragonib[AT]yahoo[DOT]es")] + + +    NAME_PATTERN = r'<div class="Text">Inhalt des Ordners <span.*>(?P<N>.+)</span></div>' + + +    def prepare(self): +        super(NetfolderIn, self).prepare() + +        # Check for password protection +        if self.isPasswordProtected(): +            self.html = self.submitPassword() +            if not self.html: +                self.fail(_("Incorrect password, please set right password on Add package form and retry")) + + +    def isPasswordProtected(self): +        if '<input type="password" name="password"' in self.html: +            self.logDebug("Links are password protected") +            return True +        return False + + +    def submitPassword(self): +        # Gather data +        try: +            m  = re.match(self.__pattern__, self.pyfile.url) +            id = m.group('ID') +        except AttributeError: +            self.logDebug("Unable to get package id from url [%s]" % self.pyfile.url) +            return +        url = "http://netfolder.in/folder.php?folder_id=" + id +        password = self.getPassword() + +        # Submit package password +        post = {'password': password, 'save': 'Absenden'} +        self.logDebug("Submitting password [%s] for protected links with id [%s]" % (password, id)) +        html = self.load(url, {}, post) + +        # Check for invalid password +        if '<div class="InPage_Error">' in html: +            self.logDebug("Incorrect password, please set right password on Edit package form and retry") +            return None + +        return html + + +    def getLinks(self): +        links = re.search(r'name="list" value="(.*?)"', self.html).group(1).split(",") +        self.logDebug("Package has %d links" % len(links)) +        return links diff --git a/pyload/plugin/crypter/NosvideoCom.py b/pyload/plugin/crypter/NosvideoCom.py new file mode 100644 index 000000000..f2d7bfe04 --- /dev/null +++ b/pyload/plugin/crypter/NosvideoCom.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class NosvideoCom(SimpleCrypter): +    __name__    = "NosvideoCom" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?nosvideo\.com/\?v=\w+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Nosvideo.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("igel", "igelkun@myopera.com")] + + +    LINK_PATTERN = r'href="(http://(?:w{3}\.)?nosupload\.com/\?d=\w+)"' +    NAME_PATTERN = r'<[tT]itle>Watch (?P<N>.+?)<' diff --git a/pyload/plugin/crypter/OneKhDe.py b/pyload/plugin/crypter/OneKhDe.py new file mode 100644 index 000000000..6f5400f03 --- /dev/null +++ b/pyload/plugin/crypter/OneKhDe.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.utils import html_unescape + +from pyload.plugin.Crypter import Crypter + + +class OneKhDe(Crypter): +    __name__    = "OneKhDe" +    __type__    = "crypter" +    __version__ = "0.11" + +    __pattern__ = r'http://(?:www\.)?1kh\.de/f/' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """1kh.de decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org")] + + +    def __init__(self, parent): +        Crypter.__init__(self, parent) +        self.parent = parent + + +    def file_exists(self): +        """ returns True or False +        """ +        return True + + +    def proceed(self, url, location): +        url = self.parent.url +        self.html = self.load(url) +        link_ids = re.findall(r"<a id=\"DownloadLink_(\d*)\" href=\"http://1kh.de/", self.html) +        for id in link_ids: +            new_link = html_unescape(re.search("width=\"100%\" src=\"(.*)\"></iframe>", self.load("http://1kh.de/l/" + id)).group(1)) +            self.urls.append(new_link) diff --git a/pyload/plugin/crypter/OronCom.py b/pyload/plugin/crypter/OronCom.py new file mode 100644 index 000000000..257e0e046 --- /dev/null +++ b/pyload/plugin/crypter/OronCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class OronCom(DeadCrypter): +    __name__    = "OronCom" +    __type__    = "crypter" +    __version__ = "0.11" + +    __pattern__ = r'http://(?:www\.)?oron\.com/folder/\w+' +    __config__  = [] + +    __description__ = """Oron.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("DHMH", "webmaster@pcProfil.de")] diff --git a/pyload/plugin/crypter/PastebinCom.py b/pyload/plugin/crypter/PastebinCom.py new file mode 100644 index 000000000..d0c779aaa --- /dev/null +++ b/pyload/plugin/crypter/PastebinCom.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class PastebinCom(SimpleCrypter): +    __name__    = "PastebinCom" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?pastebin\.com/\w+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Pastebin.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    LINK_PATTERN = r'<div class="de\d+">(https?://[^ <]+)(?:[^<]*)</div>' +    NAME_PATTERN = r'<div class="paste_box_line1" title="(?P<N>.+?)">' + diff --git a/pyload/plugin/crypter/QuickshareCz.py b/pyload/plugin/crypter/QuickshareCz.py new file mode 100644 index 000000000..18350cba3 --- /dev/null +++ b/pyload/plugin/crypter/QuickshareCz.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +import re +from pyload.plugin.Crypter import Crypter + + +class QuickshareCz(Crypter): +    __name__    = "QuickshareCz" +    __type__    = "crypter" +    __version__ = "0.10" + +    __pattern__ = r'http://(?:www\.)?quickshare\.cz/slozka-\d+' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Quickshare.cz folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    FOLDER_PATTERN = r'<textarea.*?>(.*?)</textarea>' +    LINK_PATTERN = r'(http://www\.quickshare\.cz/\S+)' + + +    def decrypt(self, pyfile): +        html = self.load(pyfile.url) + +        m = re.search(self.FOLDER_PATTERN, html, re.S) +        if m is None: +            self.error(_("FOLDER_PATTERN not found")) +        self.urls.extend(re.findall(self.LINK_PATTERN, m.group(1))) diff --git a/pyload/plugin/crypter/RSLayerCom.py b/pyload/plugin/crypter/RSLayerCom.py new file mode 100644 index 000000000..8ea8a4369 --- /dev/null +++ b/pyload/plugin/crypter/RSLayerCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class RSLayerCom(DeadCrypter): +    __name__    = "RSLayerCom" +    __type__    = "crypter" +    __version__ = "0.21" + +    __pattern__ = r'http://(?:www\.)?rs-layer\.com/directory-' +    __config__  = [] + +    __description__ = """RS-Layer.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("hzpz", "")] diff --git a/pyload/plugin/crypter/RelinkUs.py b/pyload/plugin/crypter/RelinkUs.py new file mode 100644 index 000000000..d27750d1e --- /dev/null +++ b/pyload/plugin/crypter/RelinkUs.py @@ -0,0 +1,293 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import binascii +import re +import os + +from Crypto.Cipher import AES +from pyload.plugin.Crypter import Crypter +from pyload.utils import fs_join + + +class RelinkUs(Crypter): +    __name__    = "RelinkUs" +    __type__    = "crypter" +    __version__ = "3.12" + +    __pattern__ = r'http://(?:www\.)?relink\.us/(f/|((view|go)\.php\?id=))(?P<ID>.+)' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Relink.us decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("fragonib", "fragonib[AT]yahoo[DOT]es"), +                       ("AndroKev", "neureither.kevin@gmail.com")] + + +    PREFERRED_LINK_SOURCES = ["cnl2", "dlc", "web"] + +    OFFLINE_TOKEN = r'<title>Tattooside' + +    PASSWORD_TOKEN = r'container_password.php' +    PASSWORD_ERROR_ROKEN = r'You have entered an incorrect password' +    PASSWORD_SUBMIT_URL = r'http://www.relink.us/container_password.php' + +    CAPTCHA_TOKEN = r'container_captcha.php' +    CAPTCHA_ERROR_ROKEN = r'You have solved the captcha wrong' +    CAPTCHA_IMG_URL = r'http://www.relink.us/core/captcha/circlecaptcha.php' +    CAPTCHA_SUBMIT_URL = r'http://www.relink.us/container_captcha.php' + +    FILE_TITLE_REGEX = r'<th>Title</th><td>(.*)</td></tr>' +    FILE_NOTITLE = r'No title' + +    CNL2_FORM_REGEX = r'<form id="cnl_form-(.*?)</form>' +    CNL2_FORMINPUT_REGEX = r'<input.*?name="%s".*?value="(.*?)"' +    CNL2_JK_KEY = "jk" +    CNL2_CRYPTED_KEY = "crypted" + +    DLC_LINK_REGEX = r'<a href=".*?" class="dlc_button" target="_blank">' +    DLC_DOWNLOAD_URL = r'http://www.relink.us/download.php' + +    WEB_FORWARD_REGEX = r'getFile\(\'(.+)\'\)' +    WEB_FORWARD_URL = r'http://www.relink.us/frame.php' +    WEB_LINK_REGEX = r'<iframe name="Container" height="100%" frameborder="no" width="100%" src="(.+)"></iframe>' + + +    def setup(self): +        self.fileid  = None +        self.package = None +        self.captcha = False + + +    def decrypt(self, pyfile): +        # Init +        self.initPackage(pyfile) + +        # Request package +        self.requestPackage() + +        # Check for online +        if not self.isOnline(): +            self.offline() + +        # Check for protection +        if self.isPasswordProtected(): +            self.unlockPasswordProtection() +            self.handleErrors() + +        if self.isCaptchaProtected(): +            self.captcha = True +            self.unlockCaptchaProtection() +            self.handleErrors() + +        # Get package name and folder +        (package_name, folder_name) = self.getPackageInfo() + +        # Extract package links +        package_links = [] +        for sources in self.PREFERRED_LINK_SOURCES: +            package_links.extend(self.handleLinkSource(sources)) +            if package_links:  # use only first source which provides links +                break +        package_links = set(package_links) + +        # Pack +        if package_links: +            self.packages = [(package_name, package_links, folder_name)] + + +    def initPackage(self, pyfile): +        self.fileid = re.match(self.__pattern__, pyfile.url).group('ID') +        self.package = pyfile.package() + + +    def requestPackage(self): +        self.html = self.load(self.pyfile.url, decode=True) + + +    def isOnline(self): +        if self.OFFLINE_TOKEN in self.html: +            self.logDebug("File not found") +            return False +        return True + + +    def isPasswordProtected(self): +        if self.PASSWORD_TOKEN in self.html: +            self.logDebug("Links are password protected") +            return True + + +    def isCaptchaProtected(self): +        if self.CAPTCHA_TOKEN in self.html: +            self.logDebug("Links are captcha protected") +            return True +        return False + + +    def unlockPasswordProtection(self): +        password = self.getPassword() + +        self.logDebug("Submitting password [%s] for protected links" % password) + +        if password: +            passwd_url = self.PASSWORD_SUBMIT_URL + "?id=%s" % self.fileid +            passwd_data = {'id': self.fileid, 'password': password, 'pw': 'submit'} +            self.html = self.load(passwd_url, post=passwd_data, decode=True) + + +    def unlockCaptchaProtection(self): +        self.logDebug("Request user positional captcha resolving") +        captcha_img_url = self.CAPTCHA_IMG_URL + "?id=%s" % self.fileid +        coords = self.decryptCaptcha(captcha_img_url, forceUser=True, imgtype="png", result_type='positional') +        self.logDebug("Captcha resolved, coords [%s]" % str(coords)) +        captcha_post_url = self.CAPTCHA_SUBMIT_URL + "?id=%s" % self.fileid +        captcha_post_data = {'button.x': coords[0], 'button.y': coords[1], 'captcha': 'submit'} +        self.html = self.load(captcha_post_url, post=captcha_post_data, decode=True) + + +    def getPackageInfo(self): +        name = folder = None + +        # Try to get info from web +        m = re.search(self.FILE_TITLE_REGEX, self.html) +        if m: +            title = m.group(1).strip() +            if not self.FILE_NOTITLE in title: +                name = folder = title +                self.logDebug("Found name [%s] and folder [%s] in package info" % (name, folder)) + +        # Fallback to defaults +        if not name or not folder: +            name = self.package.name +            folder = self.package.folder +            self.logDebug("Package info not found, defaulting to pyfile name [%s] and folder [%s]" % (name, folder)) + +        # Return package info +        return name, folder + + +    def handleErrors(self): +        if self.PASSWORD_ERROR_ROKEN in self.html: +            msg = "Incorrect password, please set right password on 'Edit package' form and retry" +            self.logDebug(msg) +            self.fail(_(msg)) + +        if self.captcha: +            if self.CAPTCHA_ERROR_ROKEN in self.html: +                self.invalidCaptcha() +                self.retry() +            else: +                self.correctCaptcha() + + +    def handleLinkSource(self, source): +        if source == 'cnl2': +            return self.handleCNL2Links() +        elif source == 'dlc': +            return self.handleDLCLinks() +        elif source == 'web': +            return self.handleWEBLinks() +        else: +            self.error(_('Unknown source type "%s"') % source) + + +    def handleCNL2Links(self): +        self.logDebug("Search for CNL2 links") +        package_links = [] +        m = re.search(self.CNL2_FORM_REGEX, self.html, re.S) +        if m: +            cnl2_form = m.group(1) +            try: +                (vcrypted, vjk) = self._getCipherParams(cnl2_form) +                for (crypted, jk) in zip(vcrypted, vjk): +                    package_links.extend(self._getLinks(crypted, jk)) +            except Exception: +                self.logDebug("Unable to decrypt CNL2 links") +        return package_links + + +    def handleDLCLinks(self): +        self.logDebug("Search for DLC links") +        package_links = [] +        m = re.search(self.DLC_LINK_REGEX, self.html) +        if m: +            container_url = self.DLC_DOWNLOAD_URL + "?id=%s&dlc=1" % self.fileid +            self.logDebug("Downloading DLC container link [%s]" % container_url) +            try: +                dlc = self.load(container_url) +                dlc_filename = self.fileid + ".dlc" +                dlc_filepath = fs_join(self.config['general']['download_folder'], dlc_filename) +                with open(dlc_filepath, "wb") as f: +                    f.write(dlc) +                package_links.append(dlc_filepath) + +            except Exception: +                self.fail(_("Unable to download DLC container")) + +        return package_links + + +    def handleWEBLinks(self): +        self.logDebug("Search for WEB links") + +        package_links = [] +        params        = re.findall(self.WEB_FORWARD_REGEX, self.html) + +        self.logDebug("Decrypting %d Web links" % len(params)) + +        for index, param in enumerate(params): +            try: +                url = self.WEB_FORWARD_URL + "?%s" % param + +                self.logDebug("Decrypting Web link %d, %s" % (index + 1, url)) + +                res  = self.load(url, decode=True) +                link = re.search(self.WEB_LINK_REGEX, res).group(1) + +                package_links.append(link) + +            except Exception, detail: +                self.logDebug("Error decrypting Web link %s, %s" % (index, detail)) + +            self.setWait(4) +            self.wait() + +        return package_links + + +    def _getCipherParams(self, cnl2_form): +        # Get jk +        jk_re = self.CNL2_FORMINPUT_REGEX % self.CNL2_JK_KEY +        vjk = re.findall(jk_re, cnl2_form, re.I) + +        # Get crypted +        crypted_re = self.CNL2_FORMINPUT_REGEX % RelinkUs.CNL2_CRYPTED_KEY +        vcrypted = re.findall(crypted_re, cnl2_form, re.I) + +        # Log and return +        self.logDebug("Detected %d crypted blocks" % len(vcrypted)) +        return vcrypted, vjk + + +    def _getLinks(self, crypted, jk): +        # Get key +        jreturn = self.js.eval("%s f()" % jk) +        self.logDebug("JsEngine returns value [%s]" % jreturn) +        key = binascii.unhexlify(jreturn) + +        # Decrypt +        Key = key +        IV = key +        obj = AES.new(Key, AES.MODE_CBC, IV) +        text = obj.decrypt(crypted.decode('base64')) + +        # Extract links +        text = text.replace("\x00", "").replace("\r", "") +        links = filter(bool, text.split('\n')) + +        # Log and return +        self.logDebug("Package has %d links" % len(links)) +        return links diff --git a/pyload/plugin/crypter/SafelinkingNet.py b/pyload/plugin/crypter/SafelinkingNet.py new file mode 100644 index 000000000..5733f9694 --- /dev/null +++ b/pyload/plugin/crypter/SafelinkingNet.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- + +import re + +from BeautifulSoup import BeautifulSoup + +from pyload.utils import json_loads +from pyload.plugin.Crypter import Crypter +from pyload.plugin.captcha.SolveMedia import SolveMedia + + +class SafelinkingNet(Crypter): +    __name__    = "SafelinkingNet" +    __type__    = "crypter" +    __version__ = "0.14" + +    __pattern__ = r'https?://(?:www\.)?safelinking\.net/([pd])/\w+' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Safelinking.net decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("quareevo", "quareevo@arcor.de")] + + +    SOLVEMEDIA_PATTERN = "solvemediaApiKey = '([\w.-]+)';" + + +    def decrypt(self, pyfile): +        url = pyfile.url + +        if re.match(self.__pattern__, url).group(1) == "d": + +            header = self.load(url, just_header=True) +            if 'location' in header: +                self.urls = [header['location']] +            else: +                self.error(_("Couldn't find forwarded Link")) + +        else: +            postData = {"post-protect": "1"} + +            self.html = self.load(url) + +            if "link-password" in self.html: +                postData['link-password'] = self.getPassword() + +            if "altcaptcha" in self.html: +                for _i in xrange(5): +                    m = re.search(self.SOLVEMEDIA_PATTERN, self.html) +                    if m: +                        captchaKey = m.group(1) +                        captcha = SolveMedia(self) +                        captchaProvider = "Solvemedia" +                    else: +                        self.fail(_("Error parsing captcha")) + +                    response, challenge = captcha.challenge(captchaKey) +                    postData['adcopy_challenge'] = challenge +                    postData['adcopy_response']  = response + +                    self.html = self.load(url, post=postData) +                    if "The password you entered was incorrect" in self.html: +                        self.fail(_("Incorrect Password")) +                    if not "The CAPTCHA code you entered was wrong" in self.html: +                        break + +            pyfile.package().password = "" +            soup = BeautifulSoup(self.html) +            scripts = soup.findAll("script") +            for s in scripts: +                if "d_links" in s.text: +                    break +            m = re.search('d_links":(\[.*?\])', s.text) +            if m: +                linkDict = json_loads(m.group(1)) +                for link in linkDict: +                    if not "http://" in link['full']: +                        self.urls.append("https://safelinking.net/d/" + link['full']) +                    else: +                        self.urls.append(link['full']) diff --git a/pyload/plugin/crypter/SecuredIn.py b/pyload/plugin/crypter/SecuredIn.py new file mode 100644 index 000000000..b0b8eb6d3 --- /dev/null +++ b/pyload/plugin/crypter/SecuredIn.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class SecuredIn(DeadCrypter): +    __name__    = "SecuredIn" +    __type__    = "crypter" +    __version__ = "0.21" + +    __pattern__ = r'http://(?:www\.)?secured\.in/download-[\d]+-\w{8}\.html' +    __config__  = [] + +    __description__ = """Secured.in decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de")] diff --git a/pyload/plugin/crypter/SexuriaCom.py b/pyload/plugin/crypter/SexuriaCom.py new file mode 100644 index 000000000..39c3a8515 --- /dev/null +++ b/pyload/plugin/crypter/SexuriaCom.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Crypter import Crypter + + +class SexuriaCom(Crypter): +    __name__    = "SexuriaCom" +    __type__    = "crypter" +    __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?sexuria\.com/(v1/)?(Pornos_Kostenlos_.+?_(\d+)\.html|dl_links_\d+_\d+\.html|id=\d+\&part=\d+\&link=\d+)' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Sexuria.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("NETHead", "NETHead.AT.gmx.DOT.net")] + + +    PATTERN_SUPPORTED_MAIN     = re.compile(r'http://(www\.)?sexuria\.com/(v1/)?Pornos_Kostenlos_.+?_(\d+)\.html', re.I) +    PATTERN_SUPPORTED_CRYPT    = re.compile(r'http://(www\.)?sexuria\.com/(v1/)?dl_links_\d+_(?P<ID>\d+)\.html', re.I) +    PATTERN_SUPPORTED_REDIRECT = re.compile(r'http://(www\.)?sexuria\.com/out\.php\?id=(?P<ID>\d+)\&part=\d+\&link=\d+', re.I) +    PATTERN_TITLE              = re.compile(r'<title> - (?P<TITLE>.*) Sexuria - Kostenlose Pornos - Rapidshare XXX Porn</title>', re.I) +    PATTERN_PASSWORD           = re.compile(r'<strong>Passwort: </strong></div></td>.*?bgcolor="#EFEFEF">(?P<PWD>.*?)</td>', re.I | re.S) +    PATTERN_DL_LINK_PAGE       = re.compile(r'"(dl_links_\d+_\d+\.html)"', re.I) +    PATTERN_REDIRECT_LINKS     = re.compile(r'value="(http://sexuria\.com/out\.php\?id=\d+\&part=\d+\&link=\d+)" readonly', re.I) + + +    def decrypt(self, pyfile): +        # Init +        self.pyfile = pyfile +        self.package = pyfile.package() + +        # Get package links +        package_name, self.links, folder_name, package_pwd = self.decryptLinks(self.pyfile.url) +        self.packages = [(package_name, self.links, folder_name)] + + +    def decryptLinks(self, url): +        linklist = [] +        name = self.package.name +        folder = self.package.folder +        password = None + +        if re.match(self.PATTERN_SUPPORTED_MAIN, url): +            # Processing main page +            html = self.load(url) +            links = re.findall(self.PATTERN_DL_LINK_PAGE, html) +            for link in links: +                linklist.append("http://sexuria.com/v1/" + link) + +        elif re.match(self.PATTERN_SUPPORTED_REDIRECT, url): +            # Processing direct redirect link (out.php), redirecting to main page +            id = re.search(self.PATTERN_SUPPORTED_REDIRECT, url).group('ID') +            if id: +                linklist.append("http://sexuria.com/v1/Pornos_Kostenlos_liebe_%s.html" % id) + +        elif re.match(self.PATTERN_SUPPORTED_CRYPT, url): +            # Extract info from main file +            id = re.search(self.PATTERN_SUPPORTED_CRYPT, url).group('ID') +            html = self.load("http://sexuria.com/v1/Pornos_Kostenlos_info_%s.html" % id, decode=True) + +            title = re.search(self.PATTERN_TITLE, html).group('TITLE').strip() +            if title: +                name = folder = title +                self.logDebug("Package info found, name [%s] and folder [%s]" % (name, folder)) + +            pwd = re.search(self.PATTERN_PASSWORD, html).group('PWD') +            if pwd: +                password = pwd.strip() +                self.logDebug("Password info [%s] found" % password) + +            # Process link (dl_link) +            html = self.load(url) +            links = re.findall(self.PATTERN_REDIRECT_LINKS, html) +            if len(links) == 0: +                self.LogError("Broken for link %s" % link) +            else: +                for link in links: +                    link = link.replace("http://sexuria.com/", "http://www.sexuria.com/") +                    finallink = self.load(link, just_header=True)['location'] +                    if not finallink or "sexuria.com/" in finallink: +                        self.LogError("Broken for link %s" % link) +                    else: +                        linklist.append(finallink) + +        # Debug log +        self.logDebug("%d supported links" % len(linklist)) +        for i, link in enumerate(linklist): +            self.logDebug("Supported link %d, %s" % (i + 1, link)) + +        return name, linklist, folder, password diff --git a/pyload/plugin/crypter/ShareLinksBiz.py b/pyload/plugin/crypter/ShareLinksBiz.py new file mode 100644 index 000000000..9cae0017a --- /dev/null +++ b/pyload/plugin/crypter/ShareLinksBiz.py @@ -0,0 +1,279 @@ +# -*- coding: utf-8 -*- + +import binascii +import re + +from Crypto.Cipher import AES +from pyload.plugin.Crypter import Crypter + + +class ShareLinksBiz(Crypter): +    __name__    = "ShareLinksBiz" +    __type__    = "crypter" +    __version__ = "1.14" + +    __pattern__ = r'http://(?:www\.)?(share-links|s2l)\.biz/(?P<ID>_?\w+)' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Share-Links.biz decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("fragonib", "fragonib[AT]yahoo[DOT]es")] + + +    def setup(self): +        self.baseUrl = None +        self.fileId = None +        self.package = None +        self.captcha = False + + +    def decrypt(self, pyfile): +        # Init +        self.initFile(pyfile) + +        # Request package +        url = self.baseUrl + '/' + self.fileId +        self.html = self.load(url, decode=True) + +        # Unblock server (load all images) +        self.unblockServer() + +        # Check for protection +        if self.isPasswordProtected(): +            self.unlockPasswordProtection() +            self.handleErrors() + +        if self.isCaptchaProtected(): +            self.captcha = True +            self.unlockCaptchaProtection() +            self.handleErrors() + +        # Extract package links +        package_links = [] +        package_links.extend(self.handleWebLinks()) +        package_links.extend(self.handleContainers()) +        package_links.extend(self.handleCNL2()) +        package_links = set(package_links) + +        # Get package info +        package_name, package_folder = self.getPackageInfo() + +        # Pack +        self.packages = [(package_name, package_links, package_folder)] + + +    def initFile(self, pyfile): +        url = pyfile.url +        if 's2l.biz' in url: +            url = self.load(url, just_header=True)['location'] +        self.baseUrl = "http://www.%s.biz" % re.match(self.__pattern__, url).group(1) +        self.fileId = re.match(self.__pattern__, url).group('ID') +        self.package = pyfile.package() + + +    def isOnline(self): +        if "No usable content was found" in self.html: +            self.logDebug("File not found") +            return False +        return True + + +    def isPasswordProtected(self): +        if re.search(r'''<form.*?id="passwordForm".*?>''', self.html): +            self.logDebug("Links are protected") +            return True +        return False + + +    def isCaptchaProtected(self): +        if '<map id="captchamap"' in self.html: +            self.logDebug("Links are captcha protected") +            return True +        return False + + +    def unblockServer(self): +        imgs = re.findall(r"(/template/images/.*?\.gif)", self.html) +        for img in imgs: +            self.load(self.baseUrl + img) + + +    def unlockPasswordProtection(self): +        password = self.getPassword() +        self.logDebug("Submitting password [%s] for protected links" % password) +        post = {"password": password, 'login': 'Submit form'} +        url = self.baseUrl + '/' + self.fileId +        self.html = self.load(url, post=post, decode=True) + + +    def unlockCaptchaProtection(self): +        # Get captcha map +        captchaMap = self._getCaptchaMap() +        self.logDebug("Captcha map with [%d] positions" % len(captchaMap.keys())) + +        # Request user for captcha coords +        m = re.search(r'<img src="/captcha.gif\?d=(.*?)&PHPSESSID=(.*?)&legend=1"', self.html) +        captchaUrl = self.baseUrl + '/captcha.gif?d=%s&PHPSESSID=%s' % (m.group(1), m.group(2)) +        self.logDebug("Waiting user for correct position") +        coords = self.decryptCaptcha(captchaUrl, forceUser=True, imgtype="gif", result_type='positional') +        self.logDebug("Captcha resolved, coords [%s]" % str(coords)) + +        # Resolve captcha +        href = self._resolveCoords(coords, captchaMap) +        if href is None: +            self.invalidCaptcha() +            self.retry(wait_time=5) +        url = self.baseUrl + href +        self.html = self.load(url, decode=True) + + +    def _getCaptchaMap(self): +        mapp = {} +        for m in re.finditer(r'<area shape="rect" coords="(.*?)" href="(.*?)"', self.html): +            rect = eval('(' + m.group(1) + ')') +            href = m.group(2) +            mapp[rect] = href +        return mapp + + +    def _resolveCoords(self, coords, captchaMap): +        x, y = coords +        for rect, href in captchaMap.iteritems(): +            x1, y1, x2, y2 = rect +            if (x >= x1 and x <= x2) and (y >= y1 and y <= y2): +                return href + + +    def handleErrors(self): +        if "The inserted password was wrong" in self.html: +            self.logDebug("Incorrect password, please set right password on 'Edit package' form and retry") +            self.fail(_("Incorrect password, please set right password on 'Edit package' form and retry")) + +        if self.captcha: +            if "Your choice was wrong" in self.html: +                self.invalidCaptcha() +                self.retry(wait_time=5) +            else: +                self.correctCaptcha() + + +    def getPackageInfo(self): +        name = folder = None + +        # Extract from web package header +        title_re = r'<h2><img.*?/>(.*)</h2>' +        m = re.search(title_re, self.html, re.S) +        if m: +            title = m.group(1).strip() +            if 'unnamed' not in title: +                name = folder = title +                self.logDebug("Found name [%s] and folder [%s] in package info" % (name, folder)) + +        # Fallback to defaults +        if not name or not folder: +            name = self.package.name +            folder = self.package.folder +            self.logDebug("Package info not found, defaulting to pyfile name [%s] and folder [%s]" % (name, folder)) + +        # Return package info +        return name, folder + + +    def handleWebLinks(self): +        package_links = [] +        self.logDebug("Handling Web links") + +        #@TODO: Gather paginated web links +        pattern = r'javascript:_get\(\'(.*?)\', \d+, \'\'\)' +        ids = re.findall(pattern, self.html) +        self.logDebug("Decrypting %d Web links" % len(ids)) +        for i, ID in enumerate(ids): +            try: +                self.logDebug("Decrypting Web link %d, [%s]" % (i + 1, ID)) + +                dwLink = self.baseUrl + "/get/lnk/" + ID +                res = self.load(dwLink) + +                code = re.search(r'frm/(\d+)', res).group(1) +                fwLink = self.baseUrl + "/get/frm/" + code +                res = self.load(fwLink) + +                jscode = re.search(r'<script language="javascript">\s*eval\((.*)\)\s*</script>', res, re.S).group(1) +                jscode = self.js.eval("f = %s" % jscode) +                jslauncher = "window=''; parent={frames:{Main:{location:{href:''}}},location:''}; %s; parent.frames.Main.location.href" + +                dlLink = self.js.eval(jslauncher % jscode) + +                self.logDebug("JsEngine returns value [%s] for redirection link" % dlLink) + +                package_links.append(dlLink) +            except Exception, detail: +                self.logDebug("Error decrypting Web link [%s], %s" % (ID, detail)) +        return package_links + + +    def handleContainers(self): +        package_links = [] +        self.logDebug("Handling Container links") + +        pattern = r'javascript:_get\(\'(.*?)\', 0, \'(rsdf|ccf|dlc)\'\)' +        containersLinks = re.findall(pattern, self.html) +        self.logDebug("Decrypting %d Container links" % len(containersLinks)) +        for containerLink in containersLinks: +            link = "%s/get/%s/%s" % (self.baseUrl, containerLink[1], containerLink[0]) +            package_links.append(link) +        return package_links + + +    def handleCNL2(self): +        package_links = [] +        self.logDebug("Handling CNL2 links") + +        if '/lib/cnl2/ClicknLoad.swf' in self.html: +            try: +                (crypted, jk) = self._getCipherParams() +                package_links.extend(self._getLinks(crypted, jk)) +            except Exception: +                self.fail(_("Unable to decrypt CNL2 links")) +        return package_links + + +    def _getCipherParams(self): +        # Request CNL2 +        code   = re.search(r'ClicknLoad.swf\?code=(.*?)"', self.html).group(1) +        url    = "%s/get/cnl2/%s" % (self.baseUrl, code) +        res    = self.load(url) +        params = res.split(";;") + +        # Get jk +        strlist = list(params[1].decode('base64')) +        jk      = ''.join(strlist[::-1]) + +        # Get crypted +        strlist = list(params[2].decode('base64')) +        crypted = ''.join(strlist[::-1]) + +        # Log and return +        return crypted, jk + + +    def _getLinks(self, crypted, jk): +        # Get key +        jreturn = self.js.eval("%s f()" % jk) +        self.logDebug("JsEngine returns value [%s]" % jreturn) +        key = binascii.unhexlify(jreturn) + +        # Decrypt +        Key = key +        IV = key +        obj = AES.new(Key, AES.MODE_CBC, IV) +        text = obj.decrypt(crypted.decode('base64')) + +        # Extract links +        text = text.replace("\x00", "").replace("\r", "") +        links = filter(bool, text.split('\n')) + +        # Log and return +        self.logDebug("Block has %d links" % len(links)) +        return links diff --git a/pyload/plugin/crypter/SharingmatrixCom.py b/pyload/plugin/crypter/SharingmatrixCom.py new file mode 100644 index 000000000..bd0a7a85a --- /dev/null +++ b/pyload/plugin/crypter/SharingmatrixCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class SharingmatrixCom(DeadCrypter): +    __name__    = "SharingmatrixCom" +    __type__    = "crypter" +    __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?sharingmatrix\.com/folder/\w+' +    __config__  = [] + +    __description__ = """Sharingmatrix.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/crypter/SpeedLoadOrg.py b/pyload/plugin/crypter/SpeedLoadOrg.py new file mode 100644 index 000000000..95c8864dc --- /dev/null +++ b/pyload/plugin/crypter/SpeedLoadOrg.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class SpeedLoadOrg(DeadCrypter): +    __name__    = "SpeedLoadOrg" +    __type__    = "crypter" +    __version__ = "0.30" + +    __pattern__ = r'http://(?:www\.)?speedload\.org/(\d+~f$|folder/\d+/)' +    __config__  = [] + +    __description__ = """Speedload decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] diff --git a/pyload/plugin/crypter/StealthTo.py b/pyload/plugin/crypter/StealthTo.py new file mode 100644 index 000000000..23747be1b --- /dev/null +++ b/pyload/plugin/crypter/StealthTo.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class StealthTo(DeadCrypter): +    __name__    = "StealthTo" +    __type__    = "crypter" +    __version__ = "0.20" + +    __pattern__ = r'http://(?:www\.)?stealth\.to/folder/.+' +    __config__  = [] + +    __description__ = """Stealth.to decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org")] diff --git a/pyload/plugin/crypter/TnyCz.py b/pyload/plugin/crypter/TnyCz.py new file mode 100644 index 000000000..f04127479 --- /dev/null +++ b/pyload/plugin/crypter/TnyCz.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + +import re + + +class TnyCz(SimpleCrypter): +    __name__    = "TnyCz" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?tny\.cz/\w+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Tny.cz decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    NAME_PATTERN = r'<title>(?P<N>.+) - .+</title>' + + +    def getLinks(self): +        m = re.search(r'<a id=\'save_paste\' href="(.+save\.php\?hash=.+)">', self.html) +        return re.findall(".+", self.load(m.group(1), decode=True)) if m else None diff --git a/pyload/plugin/crypter/TrailerzoneInfo.py b/pyload/plugin/crypter/TrailerzoneInfo.py new file mode 100644 index 000000000..06894054b --- /dev/null +++ b/pyload/plugin/crypter/TrailerzoneInfo.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class TrailerzoneInfo(DeadCrypter): +    __name__    = "TrailerzoneInfo" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?trailerzone\.info/.+' +    __config__  = [] + +    __description__ = """TrailerZone.info decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("godofdream", "soilfiction@gmail.com")] diff --git a/pyload/plugin/crypter/TurbobitNet.py b/pyload/plugin/crypter/TurbobitNet.py new file mode 100644 index 000000000..8493af9da --- /dev/null +++ b/pyload/plugin/crypter/TurbobitNet.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter +from pyload.utils import json_loads + + +class TurbobitNet(SimpleCrypter): +    __name__    = "TurbobitNet" +    __type__    = "crypter" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?turbobit\.net/download/folder/(?P<ID>\w+)' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Turbobit.net folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    NAME_PATTERN = r'src=\'/js/lib/grid/icon/folder.png\'> <span>(?P<N>.+?)</span>' + + +    def _getLinks(self, id, page=1): +        gridFile = self.load("http://turbobit.net/downloadfolder/gridFile", +                             get={"rootId": id, "rows": 200, "page": page}, decode=True) +        grid = json_loads(gridFile) + +        if grid['rows']: +            for i in grid['rows']: +                yield i['id'] +            for id in self._getLinks(id, page + 1): +                yield id +        else: +            return + + +    def getLinks(self): +        id = re.match(self.__pattern__, self.pyfile.url).group('ID') +        fixurl = lambda id: "http://turbobit.net/%s.html" % id +        return map(fixurl, self._getLinks(id)) diff --git a/pyload/plugin/crypter/TusfilesNet.py b/pyload/plugin/crypter/TusfilesNet.py new file mode 100644 index 000000000..a04723c47 --- /dev/null +++ b/pyload/plugin/crypter/TusfilesNet.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +import math +import re +from urlparse import urljoin + +from pyload.plugin.internal.XFSCrypter import XFSCrypter + + +class TusfilesNet(XFSCrypter): +    __name__    = "TusfilesNet" +    __type__    = "crypter" +    __version__ = "0.08" + +    __pattern__ = r'https?://(?:www\.)?tusfiles\.net/go/(?P<ID>\w+)' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Tusfiles.net folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    PAGES_PATTERN = r'>\((\d+) \w+\)<' + +    URL_REPLACEMENTS = [(__pattern__ + ".*", r'https://www.tusfiles.net/go/\g<ID>/')] + + +    def loadPage(self, page_n): +        return self.load(urljoin(self.pyfile.url, str(page_n)), decode=True) + + +    def handlePages(self, pyfile): +        pages = re.search(self.PAGES_PATTERN, self.html) +        if pages: +            pages = int(math.ceil(int(pages.group('pages')) / 25.0)) +        else: +            return + +        for p in xrange(2, pages + 1): +            self.html = self.loadPage(p) +            self.links += self.getLinks() diff --git a/pyload/plugin/crypter/UlozTo.py b/pyload/plugin/crypter/UlozTo.py new file mode 100644 index 000000000..a33742c54 --- /dev/null +++ b/pyload/plugin/crypter/UlozTo.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- + +import re +from pyload.plugin.Crypter import Crypter + + +class UlozTo(Crypter): +    __name__    = "UlozTo" +    __type__    = "crypter" +    __version__ = "0.20" + +    __pattern__ = r'http://(?:www\.)?(uloz\.to|ulozto\.(cz|sk|net)|bagruj\.cz|zachowajto\.pl)/(m|soubory)/.+' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Uloz.to folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    FOLDER_PATTERN = r'<ul class="profile_files">(.*?)</ul>' +    LINK_PATTERN = r'<br /><a href="/(.+?)">.+?</a>' +    NEXT_PAGE_PATTERN = r'<a class="next " href="/(.+?)"> </a>' + + +    def decrypt(self, pyfile): +        html = self.load(pyfile.url) + +        new_links = [] +        for i in xrange(1, 100): +            self.logInfo(_("Fetching links from page %i") % i) +            m = re.search(self.FOLDER_PATTERN, html, re.S) +            if m is None: +                self.error(_("FOLDER_PATTERN not found")) + +            new_links.extend(re.findall(self.LINK_PATTERN, m.group(1))) +            m = re.search(self.NEXT_PAGE_PATTERN, html) +            if m: +                html = self.load("http://ulozto.net/" + m.group(1)) +            else: +                break +        else: +            self.logInfo(_("Limit of 99 pages reached, aborting")) + +        if new_links: +            self.urls = [map(lambda s: "http://ulozto.net/%s" % s, new_links)] diff --git a/pyload/plugin/crypter/UploadableCh.py b/pyload/plugin/crypter/UploadableCh.py new file mode 100644 index 000000000..1f1a5cf8b --- /dev/null +++ b/pyload/plugin/crypter/UploadableCh.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class UploadableCh(SimpleCrypter): +    __name__    = "UploadableCh" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?uploadable\.ch/list/\w+' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Uploadable.ch folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("guidobelix", "guidobelix@hotmail.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    LINK_PATTERN = r'"(.+?)" class="icon_zipfile">' +    NAME_PATTERN = r'<div class="folder"><span> </span>(?P<N>.+?)</div>' +    OFFLINE_PATTERN = r'We are sorry... The URL you entered cannot be found on the server.' +    TEMP_OFFLINE_PATTERN = r'<div class="icon_err">' diff --git a/pyload/plugin/crypter/UploadedTo.py b/pyload/plugin/crypter/UploadedTo.py new file mode 100644 index 000000000..9286fb7a5 --- /dev/null +++ b/pyload/plugin/crypter/UploadedTo.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class UploadedTo(SimpleCrypter): +    __name__    = "UploadedTo" +    __type__    = "crypter" +    __version__ = "0.42" + +    __pattern__ = r'http://(?:www\.)?(uploaded|ul)\.(to|net)/(f|folder|list)/(?P<ID>\w+)' +    __config__  = [("use_premium"       , "bool", "Use premium account if available"   , True), +                   ("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """UploadedTo decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    PLAIN_PATTERN = r'<small class="date"><a href="([\w/]+)" onclick=' +    NAME_PATTERN = r'<title>(?P<N>.+?)<' + + +    def getLinks(self): +        m = re.search(self.PLAIN_PATTERN, self.html) +        if m is None: +            self.error(_("PLAIN_PATTERN not found")) + +        plain_link = urljoin("http://uploaded.net/", m.group(1)) +        return self.load(plain_link).split('\n')[:-1] diff --git a/pyload/plugin/crypter/WiiReloadedOrg.py b/pyload/plugin/crypter/WiiReloadedOrg.py new file mode 100644 index 000000000..4190cb340 --- /dev/null +++ b/pyload/plugin/crypter/WiiReloadedOrg.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class WiiReloadedOrg(DeadCrypter): +    __name__    = "WiiReloadedOrg" +    __type__    = "crypter" +    __version__ = "0.11" + +    __pattern__ = r'http://(?:www\.)?wii-reloaded\.org/protect/get\.php\?i=.+' +    __config__  = [] + +    __description__ = """Wii-Reloaded.org decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("hzpz", "")] diff --git a/pyload/plugin/crypter/WuploadCom.py b/pyload/plugin/crypter/WuploadCom.py new file mode 100644 index 000000000..ed8d21565 --- /dev/null +++ b/pyload/plugin/crypter/WuploadCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadCrypter import DeadCrypter + + +class WuploadCom(DeadCrypter): +    __name__    = "WuploadCom" +    __type__    = "crypter" +    __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?wupload\.com/folder/\w+' +    __config__  = [] + +    __description__ = """Wupload.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/crypter/XFileSharingPro.py b/pyload/plugin/crypter/XFileSharingPro.py new file mode 100644 index 000000000..1ae40c6c5 --- /dev/null +++ b/pyload/plugin/crypter/XFileSharingPro.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.XFSCrypter import XFSCrypter + + +class XFileSharingPro(XFSCrypter): +    __name__    = "XFileSharingPro" +    __type__    = "crypter" +    __version__ = "0.05" + +    __pattern__ = r'^unmatchable$' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """XFileSharingPro dummy folder decrypter 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.crypterPlugins[self.__name__]['pattern'] + +        self.HOSTER_DOMAIN = re.match(self.__pattern__, self.pyfile.url).group("DOMAIN").lower() +        self.HOSTER_NAME   = "".join(part.capitalize() for part in re.split(r'(\.|\d+)', self.HOSTER_DOMAIN) if part != '.') + +        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) diff --git a/pyload/plugin/crypter/XupPl.py b/pyload/plugin/crypter/XupPl.py new file mode 100644 index 000000000..b62e37db6 --- /dev/null +++ b/pyload/plugin/crypter/XupPl.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Crypter import Crypter + + +class XupPl(Crypter): +    __name__    = "XupPl" +    __type__    = "crypter" +    __version__ = "0.10" + +    __pattern__ = r'https?://(?:[^/]*\.)?xup\.pl/.+' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Xup.pl decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("z00nx", "z00nx0@gmail.com")] + + +    def decrypt(self, pyfile): +        header = self.load(pyfile.url, just_header=True) +        if 'location' in header: +            self.urls = [header['location']] +        else: +            self.fail(_("Unable to find link")) diff --git a/pyload/plugin/crypter/YoutubeComFolder.py b/pyload/plugin/crypter/YoutubeComFolder.py new file mode 100644 index 000000000..e42615af0 --- /dev/null +++ b/pyload/plugin/crypter/YoutubeComFolder.py @@ -0,0 +1,148 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from pyload.utils import json_loads +from pyload.plugin.Crypter import Crypter +from pyload.utils import fs_join + + +class YoutubeComFolder(Crypter): +    __name__    = "YoutubeComFolder" +    __type__    = "crypter" +    __version__ = "1.01" + +    __pattern__ = r'https?://(?:www\.|m\.)?youtube\.com/(?P<TYPE>user|playlist|view_play_list)(/|.*?[?&](?:list|p)=)(?P<ID>[\w-]+)' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True ), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True ), +                   ("likes"             , "bool", "Grab user (channel) liked videos"   , False), +                   ("favorites"         , "bool", "Grab user (channel) favorite videos", False), +                   ("uploads"           , "bool", "Grab channel unplaylisted videos"   , True )] + +    __description__ = """Youtube.com channel & playlist decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    API_KEY = "AIzaSyCKnWLNlkX-L4oD1aEzqqhRw1zczeD6_k0" + + +    def api_response(self, ref, req): +        req.update({"key": self.API_KEY}) +        url  = urljoin("https://www.googleapis.com/youtube/v3/", ref) +        html = self.load(url, get=req) +        return json_loads(html) + + +    def getChannel(self, user): +        channels = self.api_response("channels", {"part": "id,snippet,contentDetails", "forUsername": user, "maxResults": "50"}) +        if channels['items']: +            channel = channels['items'][0] +            return {"id": channel['id'], +                    "title": channel['snippet']['title'], +                    "relatedPlaylists": channel['contentDetails']['relatedPlaylists'], +                    "user": user}  # One lone channel for user? + + +    def getPlaylist(self, p_id): +        playlists = self.api_response("playlists", {"part": "snippet", "id": p_id}) +        if playlists['items']: +            playlist = playlists['items'][0] +            return {"id": p_id, +                    "title": playlist['snippet']['title'], +                    "channelId": playlist['snippet']['channelId'], +                    "channelTitle": playlist['snippet']['channelTitle']} + + +    def _getPlaylists(self, id, token=None): +        req = {"part": "id", "maxResults": "50", "channelId": id} +        if token: +            req.update({"pageToken": token}) + +        playlists = self.api_response("playlists", req) + +        for playlist in playlists['items']: +            yield playlist['id'] + +        if "nextPageToken" in playlists: +            for item in self._getPlaylists(id, playlists['nextPageToken']): +                yield item + + +    def getPlaylists(self, ch_id): +        return map(self.getPlaylist, self._getPlaylists(ch_id)) + + +    def _getVideosId(self, id, token=None): +        req = {"part": "contentDetails", "maxResults": "50", "playlistId": id} +        if token: +            req.update({"pageToken": token}) + +        playlist = self.api_response("playlistItems", req) + +        for item in playlist['items']: +            yield item['contentDetails']['videoId'] + +        if "nextPageToken" in playlist: +            for item in self._getVideosId(id, playlist['nextPageToken']): +                yield item + + +    def getVideosId(self, p_id): +        return list(self._getVideosId(p_id)) + + +    def decrypt(self, pyfile): +        m = re.match(self.__pattern__, pyfile.url) +        m_id = m.group('ID') +        m_type = m.group('TYPE') + +        if m_type == "user": +            self.logDebug("Url recognized as Channel") +            user = m_id +            channel = self.getChannel(user) + +            if channel: +                playlists = self.getPlaylists(channel['id']) +                self.logDebug("%s playlist\s found on channel \"%s\"" % (len(playlists), channel['title'])) + +                relatedplaylist = {p_name: self.getPlaylist(p_id) for p_name, p_id in channel['relatedPlaylists'].iteritems()} +                self.logDebug("Channel's related playlists found = %s" % relatedplaylist.keys()) + +                relatedplaylist['uploads']['title'] = "Unplaylisted videos" +                relatedplaylist['uploads']['checkDups'] = True  #: checkDups flag + +                for p_name, p_data in relatedplaylist.iteritems(): +                    if self.getConfig(p_name): +                        p_data['title'] += " of " + user +                        playlists.append(p_data) +            else: +                playlists = [] +        else: +            self.logDebug("Url recognized as Playlist") +            playlists = [self.getPlaylist(m_id)] + +        if not playlists: +            self.fail(_("No playlist available")) + +        addedvideos = [] +        urlize = lambda x: "https://www.youtube.com/watch?v=" + x +        for p in playlists: +            p_name = p['title'] +            p_videos = self.getVideosId(p['id']) +            p_folder = fs_join(self.config['general']['download_folder'], p['channelTitle'], p_name) +            self.logDebug("%s video\s found on playlist \"%s\"" % (len(p_videos), p_name)) + +            if not p_videos: +                continue +            elif "checkDups" in p: +                p_urls = [urlize(v_id) for v_id in p_videos if v_id not in addedvideos] +                self.logDebug("%s video\s available on playlist \"%s\" after duplicates cleanup" % (len(p_urls), p_name)) +            else: +                p_urls = map(urlize, p_videos) + +            self.packages.append((p_name, p_urls, p_folder))  #: folder is NOT recognized by pyload 0.4.9! + +            addedvideos.extend(p_videos) diff --git a/pyload/plugin/crypter/__init__.py b/pyload/plugin/crypter/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/pyload/plugin/crypter/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/pyload/plugin/extractor/SevenZip.py b/pyload/plugin/extractor/SevenZip.py new file mode 100644 index 000000000..cf397114f --- /dev/null +++ b/pyload/plugin/extractor/SevenZip.py @@ -0,0 +1,154 @@ +# -*- coding: utf-8 -*- + +import os +import re +import subprocess + +from pyload.plugin.extractor.UnRar import ArchiveError, CRCError, PasswordError, UnRar, renice +from pyload.utils import fs_encode, fs_join + + +class SevenZip(UnRar): +    __name__    = "SevenZip" +    __type__    = "extractor" +    __version__ = "0.11" + +    __description__ = """7-Zip extractor plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Michael Nowak" , ""                 ), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    CMD     = "7z" +    VERSION = "" + +    EXTENSIONS = [".7z", ".xz", ".zip", ".gz", ".gzip", ".tgz", ".bz2", ".bzip2", +                  ".tbz2", ".tbz", ".tar", ".wim", ".swm", ".lzma", ".rar", ".cab", +                  ".arj", ".z", ".taz", ".cpio", ".rpm", ".deb", ".lzh", ".lha", +                  ".chm", ".chw", ".hxs", ".iso", ".msi", ".doc", ".xls", ".ppt", +                  ".dmg", ".xar", ".hfs", ".exe", ".ntfs", ".fat", ".vhd", ".mbr", +                  ".squashfs", ".cramfs", ".scap"] + + +    #@NOTE: there are some more uncovered 7z formats +    re_filelist = re.compile(r'([\d\:]+)\s+([\d\:]+)\s+([\w\.]+)\s+(\d+)\s+(\d+)\s+(.+)') +    re_wrongpwd = re.compile(r'(Can not open encrypted archive|Wrong password|Encrypted\s+\=\s+\+)', re.I) +    re_wrongcrc = re.compile(r'CRC Failed|Can not open file', re.I) +    re_version  = re.compile(r'7-Zip\s(?:\[64\]\s)?(\d+\.\d+)', re.I) + + +    @classmethod +    def isUsable(cls): +        if os.name == "nt": +            cls.CMD = os.path.join(pypath, "7z.exe") +            p = subprocess.Popen([cls.CMD], stdout=subprocess.PIPE, stderr=subprocess.PIPE) +            out, err = p.communicate() +        else: +            p = subprocess.Popen([cls.CMD], stdout=subprocess.PIPE, stderr=subprocess.PIPE) +            out, err = p.communicate() + +        m = cls.re_version.search(out) +        cls.VERSION = m.group(1) if m else '(version unknown)' + +        return True + + +    def verify(self, password): +        # 7z can't distinguish crc and pw error in test +        p = self.call_cmd("l", "-slt", fs_encode(self.filename)) +        out, err = p.communicate() + +        if self.re_wrongpwd.search(out): +            raise PasswordError + +        if self.re_wrongpwd.search(err): +            raise PasswordError + +        if self.re_wrongcrc.search(err): +            raise CRCError(err) + + + +    def check(self, password): +        p = self.call_cmd("l", "-slt", fs_encode(self.filename)) +        out, err = p.communicate() + +        # check if output or error macthes the 'wrong password'-Regexp +        if self.re_wrongpwd.search(out): +            raise PasswordError + +        if self.re_wrongcrc.search(out): +            raise CRCError(_("Header protected")) + + +    def repair(self): +        return False + + +    def extract(self, password=None): +        command = "x" if self.fullpath else "e" + +        p = self.call_cmd(command, '-o' + self.out, fs_encode(self.filename), password=password) + +        renice(p.pid, self.renice) + +        # communicate and retrieve stderr +        self._progress(p) +        err = p.stderr.read().strip() + +        if err: +            if self.re_wrongpwd.search(err): +                raise PasswordError + +            elif self.re_wrongcrc.search(err): +                raise CRCError(err) + +            else:  #: raise error if anything is on stderr +                raise ArchiveError(err) + +        if p.returncode > 1: +            raise ArchiveError(_("Process return code: %d") % p.returncode) + +        self.files = self.list(password) + + +    def list(self, password=None): +        command = "l" if self.fullpath else "l" + +        p = self.call_cmd(command, fs_encode(self.filename), password=password) +        out, err = p.communicate() + +        if "Can not open" in err: +            raise ArchiveError(_("Cannot open file")) + +        if p.returncode > 1: +            raise ArchiveError(_("Process return code: %d") % p.returncode) + +        result = set() +        for groups in self.re_filelist.findall(out): +            f = groups[-1].strip() +            result.add(fs_join(self.out, f)) + +        return list(result) + + +    def call_cmd(self, command, *xargs, **kwargs): +        args = [] + +        #overwrite flag +        if self.overwrite: +            args.append("-y") + +        #set a password +        if "password" in kwargs and kwargs["password"]: +            args.append("-p%s" % kwargs["password"]) +        else: +            args.append("-p-") + +        #@NOTE: return codes are not reliable, some kind of threading, cleanup whatever issue +        call = [self.CMD, command] + args + list(xargs) + +        self.manager.logDebug(" ".join(call)) + +        p = subprocess.Popen(call, stdout=subprocess.PIPE, stderr=subprocess.PIPE) +        return p diff --git a/pyload/plugin/extractor/UnRar.py b/pyload/plugin/extractor/UnRar.py new file mode 100644 index 000000000..3bc4f3464 --- /dev/null +++ b/pyload/plugin/extractor/UnRar.py @@ -0,0 +1,244 @@ +# -*- coding: utf-8 -*- + +import os +import re +import subprocess + +from glob import glob +from string import digits + +from pyload.plugin.Extractor import Extractor, ArchiveError, CRCError, PasswordError +from pyload.utils import fs_decode, fs_encode, fs_join + + +def renice(pid, value): +    if value and os.name != "nt": +        try: +            subprocess.Popen(["renice", str(value), str(pid)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=-1) + +        except Exception: +            pass + + +class UnRar(Extractor): +    __name__    = "UnRar" +    __type__    = "extractor" +    __version__ = "1.20" + +    __description__ = """Rar extractor plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN"         , "RaNaN@pyload.org" ), +                       ("Walter Purcaro", "vuolter@gmail.com"), +                       ("Immenz"        , "immenz@gmx.net"   )] + + +    CMD = "unrar" +    VERSION = "" +    EXTENSIONS = [".rar"] + + +    re_multipart = re.compile(r'\.(part|r)(\d+)(?:\.rar)?(\.rev|\.bad)?',re.I) + +    re_filefixed = re.compile(r'Building (.+)') +    re_filelist  = re.compile(r'^(.)(\s*[\w\.\-]+)\s+(\d+\s+)+(?:\d+\%\s+)?[\d\-]{8}\s+[\d\:]{5}', re.M|re.I) + +    re_wrongpwd  = re.compile(r'password', re.I) +    re_wrongcrc  = re.compile(r'encrypted|damaged|CRC failed|checksum error|corrupt', re.I) + +    re_version   = re.compile(r'(?:UN)?RAR\s(\d+\.\d+)', re.I) + + +    @classmethod +    def isUsable(cls): +        if os.name == "nt": +            try: +                cls.CMD = os.path.join(pypath, "RAR.exe") +                p = subprocess.Popen([cls.CMD], stdout=subprocess.PIPE, stderr=subprocess.PIPE) +                out, err = p.communicate() +                cls.__name__ = "RAR" +                cls.REPAIR = True + +            except OSError: +                cls.CMD = os.path.join(pypath, "UnRAR.exe") +                p = subprocess.Popen([cls.CMD], stdout=subprocess.PIPE, stderr=subprocess.PIPE) +                out, err = p.communicate() +        else: +            try: +                p = subprocess.Popen(["rar"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) +                out, err = p.communicate() +                cls.__name__ = "RAR" +                cls.REPAIR = True + +            except OSError:  #: fallback to unrar +                p = subprocess.Popen([cls.CMD], stdout=subprocess.PIPE, stderr=subprocess.PIPE) +                out, err = p.communicate() + +        m = cls.re_version.search(out) +        cls.VERSION = m.group(1) if m else '(version unknown)' + +        return True + + +    @classmethod +    def isMultipart(cls, filename): +        return True if cls.re_multipart.search(filename) else False + + +    def verify(self, password): +        p = self.call_cmd("t", "-v", fs_encode(self.filename), password=password) +        self._progress(p) +        err = p.stderr.read().strip() + +        if self.re_wrongpwd.search(err): +            raise PasswordError + +        if self.re_wrongcrc.search(err): +            raise CRCError(err) + + +    def check(self, password): +        p = self.call_cmd("l", "-v", fs_encode(self.filename), password=password) +        out, err = p.communicate() + +        if self.re_wrongpwd.search(err): +            raise PasswordError + +        if self.re_wrongcrc.search(err): +            raise CRCError(err) + +        # output only used to check if passworded files are present +        for attr in self.re_filelist.findall(out): +            if attr[0].startswith("*"): +                raise PasswordError + + +    def repair(self): +        p = self.call_cmd("rc", fs_encode(self.filename)) + +        # communicate and retrieve stderr +        self._progress(p) +        err = p.stderr.read().strip() +        if err or p.returncode: +            return False +        return True + + +    def _progress(self, process): +        s = "" +        while True: +            c = process.stdout.read(1) +            # quit loop on eof +            if not c: +                break +            # reading a percentage sign -> set progress and restart +            if c == '%': +                self.notifyProgress(int(s)) +                s = "" +            # not reading a digit -> therefore restart +            elif c not in digits: +                s = "" +            # add digit to progressstring +            else: +                s += c + + +    def extract(self, password=None): +        command = "x" if self.fullpath else "e" + +        p = self.call_cmd(command, fs_encode(self.filename), self.out, password=password) + +        renice(p.pid, self.renice) + +        # communicate and retrieve stderr +        self._progress(p) +        err = p.stderr.read().strip() + +        if err: +            if self.re_wrongpwd.search(err): +                raise PasswordError + +            elif self.re_wrongcrc.search(err): +                raise CRCError(err) + +            else:  #: raise error if anything is on stderr +                raise ArchiveError(err) + +        if p.returncode: +            raise ArchiveError(_("Process return code: %d") % p.returncode) + +        self.files = self.list(password) + + +    def getDeleteFiles(self): +        dir, name = os.path.split(self.filename) + +        # actually extracted file +        files = [self.filename] + +        # eventually Multipart Files +        files.extend(fs_join(dir, os.path.basename(file)) for file in filter(self.isMultipart, os.listdir(dir)) +                     if re.sub(self.re_multipart,".rar",name) == re.sub(self.re_multipart,".rar",file)) + +        return files + + +    def list(self, password=None): +        command = "vb" if self.fullpath else "lb" + +        p = self.call_cmd(command, "-v", fs_encode(self.filename), password=password) +        out, err = p.communicate() + +        if "Cannot open" in err: +            raise ArchiveError(_("Cannot open file")) + +        if err.strip():  #: only log error at this point +            self.manager.logError(err.strip()) + +        result = set() +        if not self.fullpath and self.VERSION.startswith('5'): +            # NOTE: Unrar 5 always list full path +            for f in fs_decode(out).splitlines(): +                f = fs_join(self.out, os.path.basename(f.strip())) +                if os.path.isfile(f): +                    result.add(fs_join(self.out, os.path.basename(f))) +        else: +            for f in fs_decode(out).splitlines(): +                f = f.strip() +                result.add(fs_join(self.out, f)) + +        return list(result) + + +    def call_cmd(self, command, *xargs, **kwargs): +        args = [] + +        # overwrite flag +        if self.overwrite: +            args.append("-o+") +        else: +            args.append("-o-") +            if self.delete != 'No': +                args.append("-or") + +        for word in self.excludefiles: +            args.append("-x'%s'" % word.strip()) + +        # assume yes on all queries +        args.append("-y") + +        # set a password +        if "password" in kwargs and kwargs['password']: +            args.append("-p%s" % kwargs['password']) +        else: +            args.append("-p-") + +        if self.keepbroken: +            args.append("-kb") + +        # NOTE: return codes are not reliable, some kind of threading, cleanup whatever issue +        call = [self.CMD, command] + args + list(xargs) + +        self.manager.logDebug(" ".join(call)) + +        p = subprocess.Popen(call, stdout=subprocess.PIPE, stderr=subprocess.PIPE) +        return p diff --git a/pyload/plugin/extractor/UnZip.py b/pyload/plugin/extractor/UnZip.py new file mode 100644 index 000000000..799af3926 --- /dev/null +++ b/pyload/plugin/extractor/UnZip.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import os +import sys +import zipfile + +from pyload.plugin.Extractor import Extractor, ArchiveError, CRCError, PasswordError +from pyload.utils import fs_encode + + +class UnZip(Extractor): +    __name__    = "UnZip" +    __type__    = "extractor" +    __version__ = "1.12" + +    __description__ = """Zip extractor plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    EXTENSIONS = [".zip", ".zip64"] +    VERSION ="(python %s.%s.%s)" % (sys.version_info[0], sys.version_info[1], sys.version_info[2]) + + +    @classmethod +    def isUsable(cls): +        return sys.version_info[:2] >= (2, 6) + + +    def list(self, password=None): +        with zipfile.ZipFile(fs_encode(self.filename), 'r', allowZip64=True) as z: +            z.setpassword(password) +            return z.namelist() + + +    def check(self, password): +        pass + + +    def verify(self): +        with zipfile.ZipFile(fs_encode(self.filename), 'r', allowZip64=True) as z: +            badfile = z.testzip() + +            if badfile: +                raise CRCError(badfile) +            else: +                raise PasswordError + + +    def extract(self, password=None): +        try: +            with zipfile.ZipFile(fs_encode(self.filename), 'r', allowZip64=True) as z: +                z.setpassword(password) + +                badfile = z.testzip() + +                if badfile: +                    raise CRCError(badfile) +                else: +                    z.extractall(self.out) + +        except (zipfile.BadZipfile, zipfile.LargeZipFile), e: +            raise ArchiveError(e) + +        except RuntimeError, e: +            if "encrypted" in e: +                raise PasswordError +            else: +                raise ArchiveError(e) +        else: +            self.files = z.namelist() diff --git a/pyload/plugin/extractor/__init__.py b/pyload/plugin/extractor/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/pyload/plugin/extractor/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/pyload/plugin/hook/BypassCaptcha.py b/pyload/plugin/hook/BypassCaptcha.py new file mode 100644 index 000000000..4579d17c5 --- /dev/null +++ b/pyload/plugin/hook/BypassCaptcha.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- + +from pycurl import FORM_FILE, LOW_SPEED_TIME + +from pyload.network.HTTPRequest import BadHeader +from pyload.network.RequestFactory import getURL, getRequest +from pyload.plugin.Hook import Hook, threaded + + +class BypassCaptchaException(Exception): + +    def __init__(self, err): +        self.err = err + + +    def getCode(self): +        return self.err + + +    def __str__(self): +        return "<BypassCaptchaException %s>" % self.err + + +    def __repr__(self): +        return "<BypassCaptchaException %s>" % self.err + + +class BypassCaptcha(Hook): +    __name__    = "BypassCaptcha" +    __type__    = "hook" +    __version__ = "0.06" + +    __config__ = [("force", "bool", "Force BC even if client is connected", False), +                ("passkey", "password", "Passkey", "")] + +    __description__ = """Send captchas to BypassCaptcha.com""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN"     , "RaNaN@pyload.org"     ), +                       ("Godofdream", "soilfcition@gmail.com"), +                       ("zoidberg"  , "zoidberg@mujmail.cz"  )] + + +    PYLOAD_KEY = "4f771155b640970d5607f919a615bdefc67e7d32" + +    SUBMIT_URL = "http://bypasscaptcha.com/upload.php" +    RESPOND_URL = "http://bypasscaptcha.com/check_value.php" +    GETCREDITS_URL = "http://bypasscaptcha.com/ex_left.php" + + +    def getCredits(self): +        res = getURL(self.GETCREDITS_URL, post={"key": self.getConfig('passkey')}) + +        data = dict(x.split(' ', 1) for x in res.splitlines()) +        return int(data['Left']) + + +    def submit(self, captcha, captchaType="file", match=None): +        req = getRequest() + +        #raise timeout threshold +        req.c.setopt(LOW_SPEED_TIME, 80) + +        try: +            res = req.load(self.SUBMIT_URL, +                           post={'vendor_key': self.PYLOAD_KEY, +                                 'key': self.getConfig('passkey'), +                                 'gen_task_id': "1", +                                 'file': (FORM_FILE, captcha)}, +                           multipart=True) +        finally: +            req.close() + +        data = dict(x.split(' ', 1) for x in res.splitlines()) +        if not data or "Value" not in data: +            raise BypassCaptchaException(res) + +        result = data['Value'] +        ticket = data['TaskId'] +        self.logDebug("Result %s : %s" % (ticket, result)) + +        return ticket, result + + +    def respond(self, ticket, success): +        try: +            res = getURL(self.RESPOND_URL, post={"task_id": ticket, "key": self.getConfig('passkey'), +                                                      "cv": 1 if success else 0}) +        except BadHeader, e: +            self.logError(_("Could not send response"), e) + + +    def captchaTask(self, task): +        if "service" in task.data: +            return False + +        if not task.isTextual(): +            return False + +        if not self.getConfig('passkey'): +            return False + +        if self.core.isClientConnected() and not self.getConfig('force'): +            return False + +        if self.getCredits() > 0: +            task.handler.append(self) +            task.data['service'] = self.__name__ +            task.setWaiting(100) +            self._processCaptcha(task) + +        else: +            self.logInfo(_("Your %s account has not enough credits") % self.__name__) + + +    def captchaCorrect(self, task): +        if task.data['service'] == self.__name__ and "ticket" in task.data: +            self.respond(task.data['ticket'], True) + + +    def captchaInvalid(self, task): +        if task.data['service'] == self.__name__ and "ticket" in task.data: +            self.respond(task.data['ticket'], False) + + +    @threaded +    def _processCaptcha(self, task): +        c = task.captchaFile +        try: +            ticket, result = self.submit(c) +        except BypassCaptchaException, e: +            task.error = e.getCode() +            return + +        task.data['ticket'] = ticket +        task.setResult(result) diff --git a/pyload/plugin/hook/Captcha9Kw.py b/pyload/plugin/hook/Captcha9Kw.py new file mode 100644 index 000000000..012266739 --- /dev/null +++ b/pyload/plugin/hook/Captcha9Kw.py @@ -0,0 +1,251 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import re +import time + +from base64 import b64encode + +from pyload.network.HTTPRequest import BadHeader +from pyload.network.RequestFactory import getURL + +from pyload.plugin.Hook import Hook, threaded + + +class Captcha9kw(Hook): +    __name__    = "Captcha9Kw" +    __type__    = "hook" +    __version__ = "0.28" + +    __config__ = [("ssl"           , "bool"    , "Use HTTPS"                                                                       , True                                                               ), +                ("force"         , "bool"    , "Force captcha resolving even if client is connected"                             , True                                                               ), +                ("confirm"       , "bool"    , "Confirm Captcha (cost +6 credits)"                                               , False                                                              ), +                ("captchaperhour", "int"     , "Captcha per hour"                                                                , "9999"                                                             ), +                ("captchapermin" , "int"     , "Captcha per minute"                                                              , "9999"                                                             ), +                ("prio"          , "int"     , "Priority (max 10)(cost +0 -> +10 credits)"                                       , "0"                                                                ), +                ("queue"         , "int"     , "Max. Queue (max 999)"                                                            , "50"                                                               ), +                ("hoster_options", "string"  , "Hoster options (format: pluginname:prio=1:selfsolfe=1:confirm=1:timeout=900|...)", "ShareonlineBiz:prio=0:timeout=999 | UploadedTo:prio=0:timeout=999"), +                ("selfsolve"     , "bool"    , "Selfsolve (manually solve your captcha in your 9kw client if active)"            , "0"                                                                ), +                ("passkey"       , "password", "API key"                                                                         , ""                                                                 ), +                ("timeout"       , "int"     , "Timeout in seconds (min 60, max 3999)"                                           , "900"                                                              )] + +    __description__ = """Send captchas to 9kw.eu""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN"         , "RaNaN@pyload.org" ), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    API_URL = "http://www.9kw.eu/index.cgi" + + +    def activate(self): +        if self.getConfig('ssl'): +            self.API_URL = self.API_URL.replace("http://", "https://") + + +    def getCredits(self): +        res = getURL(self.API_URL, +                     get={'apikey': self.getConfig('passkey'), +                          'pyload': "1", +                          'source': "pyload", +                          'action': "usercaptchaguthaben"}) + +        if res.isdigit(): +            self.logInfo(_("%s credits left") % res) +            credits = self.info['credits'] = int(res) +            return credits +        else: +            self.logError(res) +            return 0 + + +    @threaded +    def _processCaptcha(self, task): +        try: +            with open(task.captchaFile, 'rb') as f: +                data = f.read() + +        except IOError, e: +            self.logError(e) +            return + +        pluginname = re.search(r'_([^_]*)_\d+.\w+', task.captchaFile).group(1) +        option     = {'min'           : 2, +                      'max'           : 50, +                      'phrase'        : 0, +                      'numeric'       : 0, +                      'case_sensitive': 0, +                      'math'          : 0, +                      'prio'          : min(max(self.getConfig('prio'), 0), 10), +                      'confirm'       : self.getConfig('confirm'), +                      'timeout'       : min(max(self.getConfig('timeout'), 300), 3999), +                      'selfsolve'     : self.getConfig('selfsolve'), +                      'cph'           : self.getConfig('captchaperhour'), +                      'cpm'           : self.getConfig('captchapermin')} + +        for opt in str(self.getConfig('hoster_options').split('|')): + +            details = map(str.strip, opt.split(':')) + +            if not details or details[0].lower() != pluginname.lower(): +                continue + +            for d in details: +                hosteroption = d.split("=") + +                if len(hosteroption) < 2 or not hosteroption[1].isdigit(): +                    continue + +                o = hosteroption[0].lower() +                if o in option: +                    option[o] = hosteroption[1] + +            break + +        post_data = {'apikey'        : self.getConfig('passkey'), +                     'prio'          : option['prio'], +                     'confirm'       : option['confirm'], +                     'maxtimeout'    : option['timeout'], +                     'selfsolve'     : option['selfsolve'], +                     'captchaperhour': option['cph'], +                     'captchapermin' : option['cpm'], +                     'case-sensitive': option['case_sensitive'], +                     'min_len'       : option['min'], +                     'max_len'       : option['max'], +                     'phrase'        : option['phrase'], +                     'numeric'       : option['numeric'], +                     'math'          : option['math'], +                     'oldsource'     : pluginname, +                     'pyload'        : "1", +                     'source'        : "pyload", +                     'base64'        : "1", +                     'mouse'         : 1 if task.isPositional() else 0, +                     'file-upload-01': b64encode(data), +                     'action'        : "usercaptchaupload"} + +        for _i in xrange(5): +            try: +                res = getURL(self.API_URL, post=post_data) +            except BadHeader, e: +                time.sleep(3) +            else: +                if res and res.isdigit(): +                    break +        else: +            self.logError(_("Bad upload: %s") % res) +            return + +        self.logDebug(_("NewCaptchaID ticket: %s") % res, task.captchaFile) + +        task.data["ticket"] = res + +        for _i in xrange(int(self.getConfig('timeout') / 5)): +            result = getURL(self.API_URL, +                            get={'apikey': self.getConfig('passkey'), +                                 'id'    : res, +                                 'pyload': "1", +                                 'info'  : "1", +                                 'source': "pyload", +                                 'action': "usercaptchacorrectdata"}) + +            if not result or result == "NO DATA": +                time.sleep(5) +            else: +                break +        else: +            self.logDebug("Could not send request: %s" % res) +            result = None + +        self.logInfo(_("Captcha result for ticket %s: %s") % (res, result)) + +        task.setResult(result) + + +    def captchaTask(self, task): +        if not task.isTextual() and not task.isPositional(): +            return + +        if not self.getConfig('passkey'): +            return + +        if self.core.isClientConnected() and not self.getConfig('force'): +            return + +        credits = self.getCredits() + +        if not credits: +            self.logError(_("Your captcha 9kw.eu account has not enough credits")) +            return + +        queue = min(self.getConfig('queue'), 999) +        timeout = min(max(self.getConfig('timeout'), 300), 3999) +        pluginname = re.search(r'_([^_]*)_\d+.\w+', task.captchaFile).group(1) + +        for _i in xrange(5): +            servercheck = getURL("http://www.9kw.eu/grafik/servercheck.txt") +            if queue < re.search(r'queue=(\d+)', servercheck).group(1): +                break + +            time.sleep(10) +        else: +            self.fail(_("Too many captchas in queue")) + +        for opt in str(self.getConfig('hoster_options').split('|')): +            details = map(str.strip, opt.split(':')) + +            if not details or details[0].lower() != pluginname.lower(): +                continue + +            for d in details: +                hosteroption = d.split("=") + +                if len(hosteroption) > 1 \ +                   and hosteroption[0].lower() == 'timeout' \ +                   and hosteroption[1].isdigit(): +                    timeout = int(hosteroption[1]) + +            break + +        task.handler.append(self) + +        task.setWaiting(timeout) + +        self._processCaptcha(task) + + +    def _captchaResponse(self, task, correct): +        type = "correct" if correct else "refund" + +        if 'ticket' not in task.data: +            self.logDebug("No CaptchaID for %s request (task: %s)" % (type, task)) +            return + +        passkey = self.getConfig('passkey') + +        for _i in xrange(3): +            res = getURL(self.API_URL, +                         get={'action' : "usercaptchacorrectback", +                              'apikey' : passkey, +                              'api_key': passkey, +                              'correct': "1" if correct else "2", +                              'pyload' : "1", +                              'source' : "pyload", +                              'id'     : task.data["ticket"]}) + +            self.logDebug("Request %s: %s" % (type, res)) + +            if res == "OK": +                break + +            time.sleep(5) +        else: +            self.logDebug("Could not send %s request: %s" % (type, res)) + + +    def captchaCorrect(self, task): +        self._captchaResponse(task, True) + + +    def captchaInvalid(self, task): +        self._captchaResponse(task, False) diff --git a/pyload/plugin/hook/CaptchaBrotherhood.py b/pyload/plugin/hook/CaptchaBrotherhood.py new file mode 100644 index 000000000..3cbdb27d7 --- /dev/null +++ b/pyload/plugin/hook/CaptchaBrotherhood.py @@ -0,0 +1,172 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import StringIO +import pycurl +import time + +try: +    from PIL import Image +except ImportError: +    import Image + +from urllib import urlencode + +from pyload.network.RequestFactory import getURL, getRequest +from pyload.plugin.Hook import Hook, threaded + + +class CaptchaBrotherhoodException(Exception): + +    def __init__(self, err): +        self.err = err + + +    def getCode(self): +        return self.err + + +    def __str__(self): +        return "<CaptchaBrotherhoodException %s>" % self.err + + +    def __repr__(self): +        return "<CaptchaBrotherhoodException %s>" % self.err + + +class CaptchaBrotherhood(Hook): +    __name__    = "CaptchaBrotherhood" +    __type__    = "hook" +    __version__ = "0.08" + +    __config__ = [("username", "str", "Username", ""), +                ("force", "bool", "Force CT even if client is connected", False), +                ("passkey", "password", "Password", "")] + +    __description__ = """Send captchas to CaptchaBrotherhood.com""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN"   , "RaNaN@pyload.org"   ), +                       ("zoidberg", "zoidberg@mujmail.cz")] + + +    API_URL = "http://www.captchabrotherhood.com/" + + +    def activate(self): +        if self.getConfig('ssl'): +            self.API_URL = self.API_URL.replace("http://", "https://") + + +    def getCredits(self): +        res = getURL(self.API_URL + "askCredits.aspx", +                     get={"username": self.getConfig('username'), "password": self.getConfig('passkey')}) +        if not res.startswith("OK"): +            raise CaptchaBrotherhoodException(res) +        else: +            credits = int(res[3:]) +            self.logInfo(_("%d credits left") % credits) +            self.info['credits'] = credits +            return credits + + +    def submit(self, captcha, captchaType="file", match=None): +        try: +            img = Image.open(captcha) +            output = StringIO.StringIO() +            self.logDebug("CAPTCHA IMAGE", img, img.format, img.mode) +            if img.format in ("GIF", "JPEG"): +                img.save(output, img.format) +            else: +                if img.mode != "RGB": +                    img = img.convert("RGB") +                img.save(output, "JPEG") +            data = output.getvalue() +            output.close() +        except Exception, e: +            raise CaptchaBrotherhoodException("Reading or converting captcha image failed: %s" % e) + +        req = getRequest() + +        url = "%ssendNewCaptcha.aspx?%s" % (self.API_URL, +                                            urlencode({'username'     : self.getConfig('username'), +                                                       'password'     : self.getConfig('passkey'), +                                                       'captchaSource': "pyLoad", +                                                       'timeout'      : "80"})) + +        req.c.setopt(pycurl.URL, url) +        req.c.setopt(pycurl.POST, 1) +        req.c.setopt(pycurl.POSTFIELDS, data) +        req.c.setopt(pycurl.HTTPHEADER, ["Content-Type: text/html"]) + +        try: +            req.c.perform() +            res = req.getResponse() +        except Exception, e: +            raise CaptchaBrotherhoodException("Submit captcha image failed") + +        req.close() + +        if not res.startswith("OK"): +            raise CaptchaBrotherhoodException(res[1]) + +        ticket = res[3:] + +        for _i in xrange(15): +            time.sleep(5) +            res = self.api_response("askCaptchaResult", ticket) +            if res.startswith("OK-answered"): +                return ticket, res[12:] + +        raise CaptchaBrotherhoodException("No solution received in time") + + +    def api_response(self, api, ticket): +        res = getURL("%s%s.aspx" % (self.API_URL, api), +                          get={"username": self.getConfig('username'), +                               "password": self.getConfig('passkey'), +                               "captchaID": ticket}) +        if not res.startswith("OK"): +            raise CaptchaBrotherhoodException("Unknown response: %s" % res) + +        return res + + +    def captchaTask(self, task): +        if "service" in task.data: +            return False + +        if not task.isTextual(): +            return False + +        if not self.getConfig('username') or not self.getConfig('passkey'): +            return False + +        if self.core.isClientConnected() and not self.getConfig('force'): +            return False + +        if self.getCredits() > 10: +            task.handler.append(self) +            task.data['service'] = self.__name__ +            task.setWaiting(100) +            self._processCaptcha(task) +        else: +            self.logInfo(_("Your CaptchaBrotherhood Account has not enough credits")) + + +    def captchaInvalid(self, task): +        if task.data['service'] == self.__name__ and "ticket" in task.data: +            res = self.api_response("complainCaptcha", task.data['ticket']) + + +    @threaded +    def _processCaptcha(self, task): +        c = task.captchaFile +        try: +            ticket, result = self.submit(c) +        except CaptchaBrotherhoodException, e: +            task.error = e.getCode() +            return + +        task.data['ticket'] = ticket +        task.setResult(result) diff --git a/pyload/plugin/hook/DeathByCaptcha.py b/pyload/plugin/hook/DeathByCaptcha.py new file mode 100644 index 000000000..0f0e66ea2 --- /dev/null +++ b/pyload/plugin/hook/DeathByCaptcha.py @@ -0,0 +1,219 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import re +import time + +from base64 import b64encode +from pycurl import FORM_FILE, HTTPHEADER + +from pyload.utils import json_loads +from pyload.network.HTTPRequest import BadHeader +from pyload.network.RequestFactory import getRequest +from pyload.plugin.Hook import Hook, threaded + + +class DeathByCaptchaException(Exception): +    DBC_ERRORS = {'not-logged-in': 'Access denied, check your credentials', +                  'invalid-credentials': 'Access denied, check your credentials', +                  'banned': 'Access denied, account is suspended', +                  'insufficient-funds': 'Insufficient account balance to decrypt CAPTCHA', +                  'invalid-captcha': 'CAPTCHA is not a valid image', +                  'service-overload': 'CAPTCHA was rejected due to service overload, try again later', +                  'invalid-request': 'Invalid request', +                  'timed-out': 'No CAPTCHA solution received in time'} + + +    def __init__(self, err): +        self.err = err + + +    def getCode(self): +        return self.err + + +    def getDesc(self): +        if self.err in self.DBC_ERRORS.keys(): +            return self.DBC_ERRORS[self.err] +        else: +            return self.err + + +    def __str__(self): +        return "<DeathByCaptchaException %s>" % self.err + + +    def __repr__(self): +        return "<DeathByCaptchaException %s>" % self.err + + +class DeathByCaptcha(Hook): +    __name__    = "DeathByCaptcha" +    __type__    = "hook" +    __version__ = "0.06" + +    __config__ = [("username", "str", "Username", ""), +                ("passkey", "password", "Password", ""), +                ("force", "bool", "Force DBC even if client is connected", False)] + +    __description__ = """Send captchas to DeathByCaptcha.com""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN"   , "RaNaN@pyload.org"   ), +                       ("zoidberg", "zoidberg@mujmail.cz")] + + +    API_URL = "http://api.dbcapi.me/api/" + + +    def activate(self): +        if self.getConfig('ssl'): +            self.API_URL = self.API_URL.replace("http://", "https://") + + +    def api_response(self, api="captcha", post=False, multipart=False): +        req = getRequest() +        req.c.setopt(HTTPHEADER, ["Accept: application/json", "User-Agent: pyLoad %s" % self.core.version]) + +        if post: +            if not isinstance(post, dict): +                post = {} +            post.update({"username": self.getConfig('username'), +                         "password": self.getConfig('passkey')}) + +        res = None +        try: +            json = req.load("%s%s" % (self.API_URL, api), +                            post=post, +                            multipart=multipart) +            self.logDebug(json) +            res = json_loads(json) + +            if "error" in res: +                raise DeathByCaptchaException(res['error']) +            elif "status" not in res: +                raise DeathByCaptchaException(str(res)) + +        except BadHeader, e: +            if 403 == e.code: +                raise DeathByCaptchaException('not-logged-in') +            elif 413 == e.code: +                raise DeathByCaptchaException('invalid-captcha') +            elif 503 == e.code: +                raise DeathByCaptchaException('service-overload') +            elif e.code in (400, 405): +                raise DeathByCaptchaException('invalid-request') +            else: +                raise + +        finally: +            req.close() + +        return res + + +    def getCredits(self): +        res = self.api_response("user", True) + +        if 'is_banned' in res and res['is_banned']: +            raise DeathByCaptchaException('banned') +        elif 'balance' in res and 'rate' in res: +            self.info.update(res) +        else: +            raise DeathByCaptchaException(res) + + +    def getStatus(self): +        res = self.api_response("status", False) + +        if 'is_service_overloaded' in res and res['is_service_overloaded']: +            raise DeathByCaptchaException('service-overload') + + +    def submit(self, captcha, captchaType="file", match=None): +        #@NOTE: Workaround multipart-post bug in HTTPRequest.py +        if re.match("^\w*$", self.getConfig('passkey')): +            multipart = True +            data = (FORM_FILE, captcha) +        else: +            multipart = False +            with open(captcha, 'rb') as f: +                data = f.read() +            data = "base64:" + b64encode(data) + +        res = self.api_response("captcha", {"captchafile": data}, multipart) + +        if "captcha" not in res: +            raise DeathByCaptchaException(res) +        ticket = res['captcha'] + +        for _i in xrange(24): +            time.sleep(5) +            res = self.api_response("captcha/%d" % ticket, False) +            if res['text'] and res['is_correct']: +                break +        else: +            raise DeathByCaptchaException('timed-out') + +        result = res['text'] +        self.logDebug("Result %s : %s" % (ticket, result)) + +        return ticket, result + + +    def captchaTask(self, task): +        if "service" in task.data: +            return False + +        if not task.isTextual(): +            return False + +        if not self.getConfig('username') or not self.getConfig('passkey'): +            return False + +        if self.core.isClientConnected() and not self.getConfig('force'): +            return False + +        try: +            self.getStatus() +            self.getCredits() +        except DeathByCaptchaException, e: +            self.logError(e.getDesc()) +            return False + +        balance, rate = self.info['balance'], self.info['rate'] +        self.logInfo(_("Account balance"), +                     _("US$%.3f (%d captchas left at %.2f cents each)") % (balance / 100, +                                                                           balance // rate, rate)) + +        if balance > rate: +            task.handler.append(self) +            task.data['service'] = self.__name__ +            task.setWaiting(180) +            self._processCaptcha(task) + + +    def captchaInvalid(self, task): +        if task.data['service'] == self.__name__ and "ticket" in task.data: +            try: +                res = self.api_response("captcha/%d/report" % task.data['ticket'], True) + +            except DeathByCaptchaException, e: +                self.logError(e.getDesc()) + +            except Exception, e: +                self.logError(e) + + +    @threaded +    def _processCaptcha(self, task): +        c = task.captchaFile +        try: +            ticket, result = self.submit(c) +        except DeathByCaptchaException, e: +            task.error = e.getCode() +            self.logError(e.getDesc()) +            return + +        task.data['ticket'] = ticket +        task.setResult(result) diff --git a/pyload/plugin/hook/ExpertDecoders.py b/pyload/plugin/hook/ExpertDecoders.py new file mode 100644 index 000000000..0f86baa17 --- /dev/null +++ b/pyload/plugin/hook/ExpertDecoders.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +from base64 import b64encode +from pycurl import LOW_SPEED_TIME +from uuid import uuid4 + +from pyload.network.HTTPRequest import BadHeader +from pyload.network.RequestFactory import getURL, getRequest +from pyload.plugin.Hook import Hook, threaded + + +class ExpertDecoders(Hook): +    __name__    = "ExpertDecoders" +    __type__    = "hook" +    __version__ = "0.04" + +    __config__ = [("force", "bool", "Force CT even if client is connected", False), +                ("passkey", "password", "Access key", "")] + +    __description__ = """Send captchas to expertdecoders.com""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN"   , "RaNaN@pyload.org"   ), +                       ("zoidberg", "zoidberg@mujmail.cz")] + + +    API_URL = "http://www.fasttypers.org/imagepost.ashx" + + +    def activate(self): +        if self.getConfig('ssl'): +            self.API_URL = self.API_URL.replace("http://", "https://") + + +    def getCredits(self): +        res = getURL(self.API_URL, post={"key": self.getConfig('passkey'), "action": "balance"}) + +        if res.isdigit(): +            self.logInfo(_("%s credits left") % res) +            self.info['credits'] = credits = int(res) +            return credits +        else: +            self.logError(res) +            return 0 + + +    @threaded +    def _processCaptcha(self, task): +        task.data['ticket'] = ticket = uuid4() +        result = None + +        with open(task.captchaFile, 'rb') as f: +            data = f.read() + +        req = getRequest() +        #raise timeout threshold +        req.c.setopt(LOW_SPEED_TIME, 80) + +        try: +            result = req.load(self.API_URL, +                              post={'action'     : "upload", +                                    'key'        : self.getConfig('passkey'), +                                    'file'       : b64encode(data), +                                    'gen_task_id': ticket}) +        finally: +            req.close() + +        self.logDebug("Result %s : %s" % (ticket, result)) +        task.setResult(result) + + +    def captchaTask(self, task): +        if not task.isTextual(): +            return False + +        if not self.getConfig('passkey'): +            return False + +        if self.core.isClientConnected() and not self.getConfig('force'): +            return False + +        if self.getCredits() > 0: +            task.handler.append(self) +            task.setWaiting(100) +            self._processCaptcha(task) + +        else: +            self.logInfo(_("Your ExpertDecoders Account has not enough credits")) + + +    def captchaInvalid(self, task): +        if "ticket" in task.data: + +            try: +                res = getURL(self.API_URL, +                             post={'action': "refund", 'key': self.getConfig('passkey'), 'gen_task_id': task.data['ticket']}) +                self.logInfo(_("Request refund"), res) + +            except BadHeader, e: +                self.logError(_("Could not send refund request"), e) diff --git a/pyload/plugin/hook/ImageTyperz.py b/pyload/plugin/hook/ImageTyperz.py new file mode 100644 index 000000000..a7c3389c1 --- /dev/null +++ b/pyload/plugin/hook/ImageTyperz.py @@ -0,0 +1,153 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import re + +from base64 import b64encode +from pycurl import FORM_FILE, LOW_SPEED_TIME + +from pyload.network.RequestFactory import getURL, getRequest +from pyload.plugin.Hook import Hook, threaded + + +class ImageTyperzException(Exception): + +    def __init__(self, err): +        self.err = err + + +    def getCode(self): +        return self.err + + +    def __str__(self): +        return "<ImageTyperzException %s>" % self.err + + +    def __repr__(self): +        return "<ImageTyperzException %s>" % self.err + + +class ImageTyperz(Hook): +    __name__    = "ImageTyperz" +    __type__    = "hook" +    __version__ = "0.06" + +    __config__ = [("username", "str", "Username", ""), +                ("passkey", "password", "Password", ""), +                ("force", "bool", "Force IT even if client is connected", False)] + +    __description__ = """Send captchas to ImageTyperz.com""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN"   , "RaNaN@pyload.org"   ), +                       ("zoidberg", "zoidberg@mujmail.cz")] + + +    SUBMIT_URL = "http://captchatypers.com/Forms/UploadFileAndGetTextNEW.ashx" +    RESPOND_URL = "http://captchatypers.com/Forms/SetBadImage.ashx" +    GETCREDITS_URL = "http://captchatypers.com/Forms/RequestBalance.ashx" + + +    def getCredits(self): +        res = getURL(self.GETCREDITS_URL, +                     post={'action': "REQUESTBALANCE", +                           'username': self.getConfig('username'), +                           'password': self.getConfig('passkey')}) + +        if res.startswith('ERROR'): +            raise ImageTyperzException(res) + +        try: +            balance = float(res) +        except Exception: +            raise ImageTyperzException("Invalid response") + +        self.logInfo(_("Account balance: $%s left") % res) +        return balance + + +    def submit(self, captcha, captchaType="file", match=None): +        req = getRequest() +        #raise timeout threshold +        req.c.setopt(LOW_SPEED_TIME, 80) + +        try: +            #@NOTE: Workaround multipart-post bug in HTTPRequest.py +            if re.match("^\w*$", self.getConfig('passkey')): +                multipart = True +                data = (FORM_FILE, captcha) +            else: +                multipart = False +                with open(captcha, 'rb') as f: +                    data = f.read() +                data = b64encode(data) + +            res = req.load(self.SUBMIT_URL, +                           post={'action': "UPLOADCAPTCHA", +                                 'username': self.getConfig('username'), +                                 'password': self.getConfig('passkey'), "file": data}, +                           multipart=multipart) +        finally: +            req.close() + +        if res.startswith("ERROR"): +            raise ImageTyperzException(res) +        else: +            data = res.split('|') +            if len(data) == 2: +                ticket, result = data +            else: +                raise ImageTyperzException("Unknown response: %s" % res) + +        return ticket, result + + +    def captchaTask(self, task): +        if "service" in task.data: +            return False + +        if not task.isTextual(): +            return False + +        if not self.getConfig('username') or not self.getConfig('passkey'): +            return False + +        if self.core.isClientConnected() and not self.getConfig('force'): +            return False + +        if self.getCredits() > 0: +            task.handler.append(self) +            task.data['service'] = self.__name__ +            task.setWaiting(100) +            self._processCaptcha(task) + +        else: +            self.logInfo(_("Your %s account has not enough credits") % self.__name__) + + +    def captchaInvalid(self, task): +        if task.data['service'] == self.__name__ and "ticket" in task.data: +            res = getURL(self.RESPOND_URL, +                         post={'action': "SETBADIMAGE", +                               'username': self.getConfig('username'), +                               'password': self.getConfig('passkey'), +                               'imageid': task.data['ticket']}) + +            if res == "SUCCESS": +                self.logInfo(_("Bad captcha solution received, requested refund")) +            else: +                self.logError(_("Bad captcha solution received, refund request failed"), res) + + +    @threaded +    def _processCaptcha(self, task): +        c = task.captchaFile +        try: +            ticket, result = self.submit(c) +        except ImageTyperzException, e: +            task.error = e.getCode() +            return + +        task.data['ticket'] = ticket +        task.setResult(result) diff --git a/pyload/plugin/hook/XFileSharingPro.py b/pyload/plugin/hook/XFileSharingPro.py new file mode 100644 index 000000000..0a4949498 --- /dev/null +++ b/pyload/plugin/hook/XFileSharingPro.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Hook import Hook + + +class XFileSharingPro(Hook): +    __name__    = "XFileSharingPro" +    __type__    = "hook" +    __version__ = "0.36" + +    __config__ = [("activated"       , "bool", "Activated"                     , True ), +                  ("use_hoster_list" , "bool", "Load listed hosters only"      , False), +                  ("use_crypter_list", "bool", "Load listed crypters only"     , False), +                  ("use_builtin_list", "bool", "Load built-in plugin list"     , True ), +                  ("hoster_list"     , "str" , "Hoster list (comma separated)" , ""   ), +                  ("crypter_list"    , "str" , "Crypter list (comma separated)", ""   )] + +    __description__ = """Load XFileSharingPro based hosters and crypter which don't need a own plugin to run""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    # event_list = ["pluginConfigChanged"] +    regexp     = {'hoster' : (r'https?://(?:www\.)?(?P<DOMAIN>[\w\-.^_]{3,63}(?:\.[a-zA-Z]{2,})(?:\:\d+)?)/(?:embed-)?\w{12}(?:\W|$)', +                              r'https?://(?:[^/]+\.)?(?P<DOMAIN>%s)/(?:embed-)?\w+'), +                  'crypter': (r'https?://(?:www\.)?(?P<DOMAIN>[\w\-.^_]{3,63}(?:\.[a-zA-Z]{2,})(?:\:\d+)?)/(?:user|folder)s?/\w+', +                              r'https?://(?:[^/]+\.)?(?P<DOMAIN>%s)/(?:user|folder)s?/\w+')} + +    HOSTER_BUILTIN  = [#WORKING HOSTERS: +                       "backin.net", "eyesfile.ca", "file4safe.com", "fileband.com", "filedwon.com", "fileparadox.in", +                       "filevice.com", "hostingbulk.com", "junkyvideo.com", "linestorage.com", "ravishare.com", "ryushare.com", +                       "salefiles.com", "sendmyway.com", "sharesix.com", "thefile.me", "verzend.be", "xvidstage.com", +                       #NOT TESTED: +                       "101shared.com", "4upfiles.com", "filemaze.ws", "filenuke.com", "linkzhost.com", "mightyupload.com", +                       "rockdizfile.com", "sharebeast.com", "sharerepo.com", "shareswift.com", "uploadbaz.com", "uploadc.com", +                       "vidbull.com", "zalaa.com", "zomgupload.com", +                       #NOT WORKING: +                       "amonshare.com", "banicrazy.info", "boosterking.com", "host4desi.com", "laoupload.com", "rd-fs.com"] +    CRYPTER_BUILTIN = ["junocloud.me", "rapidfileshare.net"] + + +    # def pluginConfigChanged(self, plugin, name, value): +        # self.loadPattern() + + +    def activate(self): +        self.loadPattern() + + +    def loadPattern(self): +        use_builtin_list = self.getConfig("use_builtin_list") + +        for type in ("hoster", "crypter"): +            every_plugin = not self.getConfig('use_%s_list' % type) + +            if every_plugin: +                self.logInfo(_("Handling any %s I can!") % type) +                pattern = self.regexp[type][0] +            else: +                plugins    = self.getConfig('%s_list' % type) +                plugin_set = set(plugins.replace(' ', '').replace('\\', '').replace('|', ',').replace(';', ',').lower().split(',')) + +                if use_builtin_list: +                    plugin_set |= set(x.lower() for x in getattr(self, "%s_BUILTIN" % type.upper())) + +                plugin_set -= set(('', u'')) + +                if not plugin_set: +                    self.logInfo(_("No %s to handle") % type) +                    self._unload(type) +                    return + +                match_list = '|'.join(sorted(plugin_set)) + +                len_match_list = len(plugin_set) +                self.logInfo(_("Handling %d %s%s: %s") % (len_match_list, +                                                          type, +                                                          "" if len_match_list == 1 else "s", +                                                          match_list.replace('|', ', '))) + +                pattern = self.regexp[type][1] % match_list.replace('.', '\.') + +            dict = self.core.pluginManager.plugins[type]["XFileSharingPro"] +            dict['pattern'] = pattern +            dict['re'] = re.compile(pattern) + +            self.logDebug("Loaded %s pattern: %s" % (type, pattern)) + + +    def _unload(self, type): +        dict = self.core.pluginManager.plugins[type]["XFileSharingPro"] +        dict['pattern'] = r'^unmatchable$' +        dict['re'] = re.compile(dict['pattern']) + + +    def deactivate(self): +        # self.unloadHoster("BasePlugin") +        for type in ("hoster", "crypter"): +            self._unload(type, "XFileSharingPro") + + +    # def downloadFailed(self, pyfile): +        # if pyfile.pluginname == "BasePlugin" \ +           # and pyfile.hasStatus("failed") \ +           # and not self.getConfig('use_hoster_list') \ +           # and self.unloadHoster("BasePlugin"): +            # self.logDebug("Unloaded XFileSharingPro from BasePlugin") +            # pyfile.setStatus("queued") diff --git a/pyload/plugin/hook/__init__.py b/pyload/plugin/hook/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/pyload/plugin/hook/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/pyload/plugin/hoster/AlldebridCom.py b/pyload/plugin/hoster/AlldebridCom.py new file mode 100644 index 000000000..d739600d9 --- /dev/null +++ b/pyload/plugin/hoster/AlldebridCom.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +import re + +from random import randrange +from urllib import unquote + +from pyload.utils import json_loads +from pyload.plugin.internal.MultiHoster import MultiHoster +from pyload.utils import parseFileSize + + +class AlldebridCom(MultiHoster): +    __name__    = "AlldebridCom" +    __type__    = "hoster" +    __version__ = "0.46" + +    __pattern__ = r'https?://(?:www\.|s\d+\.)?alldebrid\.com/dl/[\w^_]+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Alldebrid.com multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Andy Voigt", "spamsales@online.de")] + + +    def setup(self): +        self.chunkLimit = 16 + + +    def handlePremium(self, pyfile): +        password = self.getPassword() + +        data = json_loads(self.load("http://www.alldebrid.com/service.php", +                                     get={'link': pyfile.url, 'json': "true", 'pw': password})) + +        self.logDebug("Json data", data) + +        if data['error']: +            if data['error'] == "This link isn't available on the hoster website.": +                self.offline() +            else: +                self.logWarning(data['error']) +                self.tempOffline() +        else: +            if pyfile.name and not pyfile.name.endswith('.tmp'): +                pyfile.name = data['filename'] +            pyfile.size = parseFileSize(data['filesize']) +            self.link = data['link'] + +        if self.getConfig('ssl'): +            self.link = self.link.replace("http://", "https://") +        else: +            self.link = self.link.replace("https://", "http://") + + diff --git a/pyload/plugin/hoster/AndroidfilehostCom.py b/pyload/plugin/hoster/AndroidfilehostCom.py new file mode 100644 index 000000000..aa1387c24 --- /dev/null +++ b/pyload/plugin/hoster/AndroidfilehostCom.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -* +# +# Test links: +#   https://www.androidfilehost.com/?fid=95916177934518197 + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class AndroidfilehostCom(SimpleHoster): +    __name__    = "AndroidfilehostCom" +    __type__    = "hoster" +    __version__ = "0.01" + +    __pattern__ = r'https?://(?:www\.)?androidfilehost\.com/\?fid=\d+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Androidfilehost.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de")] + + +    NAME_PATTERN    = r'<br />(?P<N>.*?)</h1>' +    SIZE_PATTERN    = r'<h4>size</h4>\s*<p>(?P<S>[\d.,]+)(?P<U>[\w^_]+)</p>' +    HASHSUM_PATTERN = r'<h4>(?P<T>.*?)</h4>\s*<p><code>(?P<H>.*?)</code></p>' + +    OFFLINE_PATTERN = r'404 not found' + +    WAIT_PATTERN    = r'users must wait <strong>(\d+) secs' + + +    def setup(self): +        self.multiDL        = True +        self.resumeDownload = True +        self.chunkLimit     = 1 + + +    def handleFree(self, pyfile): +        wait = re.search(self.WAIT_PATTERN, self.html) +        self.logDebug("Waiting time: %s seconds" % wait.group(1)) + +        fid = re.search(r'id="fid" value="(\d+)" />', self.html).group(1) +        self.logDebug("fid: %s" % fid) + +        html = self.load("https://www.androidfilehost.com/libs/otf/mirrors.otf.php", +                         post={'submit': 'submit', +                               'action': 'getdownloadmirrors', +                               'fid'   : fid}, +                         decode=True) + +        self.link   = re.findall('"url":"(.*?)"', html)[0].replace("\\", "") +        mirror_host = self.link.split("/")[2] + +        self.logDebug("Mirror Host: %s" % mirror_host) + +        html = self.load("https://www.androidfilehost.com/libs/otf/stats.otf.php", +                         get={'fid'   : fid, +                              'w'     : 'download', +                              'mirror': mirror_host}, +                         decode=True) diff --git a/pyload/plugin/hoster/BasketbuildCom.py b/pyload/plugin/hoster/BasketbuildCom.py new file mode 100644 index 000000000..ea9b9bc29 --- /dev/null +++ b/pyload/plugin/hoster/BasketbuildCom.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -* +# +# Test links: +#   https://s.basketbuild.com/filedl/devs?dev=pacman&dl=pacman/falcon/RC-3/pac_falcon-RC-3-20141103.zip +#   https://s.basketbuild.com/filedl/gapps?dl=gapps-gb-20110828-signed.zip + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class BasketbuildCom(SimpleHoster): +    __name__    = "BasketbuildCom" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'https?://(?:www\.)?(?:\w\.)?basketbuild\.com/filedl/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """basketbuild.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de")] + + +    NAME_PATTERN    = r'File Name:</strong> (?P<N>.+?)<br/>' +    SIZE_PATTERN    = r'File Size:</strong> (?P<S>[\d.,]+) (?P<U>[\w^_]+)' +    OFFLINE_PATTERN = r'404 - Page Not Found' + + +    def setup(self): +        self.multiDL        = True +        self.resumeDownload = True +        self.chunkLimit     = 1 + + +    def handleFree(self, pyfile): +        try: +            link1 = re.search(r'href="(.+dlgate/.+)"', self.html).group(1) +            self.html = self.load(link1) + +        except AttributeError: +            self.error(_("Hop #1 not found")) + +        else: +            self.logDebug("Next hop: %s" % link1) + +        try: +            wait = re.search(r'var sec = (\d+)', self.html).group(1) +            self.logDebug("Wait %s seconds" % wait) +            self.wait(wait) + +        except AttributeError: +            self.logDebug("No wait time found") + +        try: +            self.link = re.search(r'id="dlLink">\s*<a href="(.+?)"', self.html).group(1) + +        except AttributeError: +            self.error(_("DL-Link not found")) diff --git a/pyload/plugin/hoster/BayfilesCom.py b/pyload/plugin/hoster/BayfilesCom.py new file mode 100644 index 000000000..ab94b4015 --- /dev/null +++ b/pyload/plugin/hoster/BayfilesCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class BayfilesCom(DeadHoster): +    __name__    = "BayfilesCom" +    __type__    = "hoster" +    __version__ = "0.09" + +    __pattern__ = r'https?://(?:www\.)?bayfiles\.(com|net)/file/(?P<ID>\w+/\w+/[^/]+)' +    __config__  = [] + +    __description__ = """Bayfiles.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] diff --git a/pyload/plugin/hoster/BezvadataCz.py b/pyload/plugin/hoster/BezvadataCz.py new file mode 100644 index 000000000..5d1d05172 --- /dev/null +++ b/pyload/plugin/hoster/BezvadataCz.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class BezvadataCz(SimpleHoster): +    __name__    = "BezvadataCz" +    __type__    = "hoster" +    __version__ = "0.26" + +    __pattern__ = r'http://(?:www\.)?bezvadata\.cz/stahnout/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """BezvaData.cz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN = r'<p><b>Soubor: (?P<N>[^<]+)</b></p>' +    SIZE_PATTERN = r'<li><strong>Velikost:</strong> (?P<S>[^<]+)</li>' +    OFFLINE_PATTERN = r'<title>BezvaData \| Soubor nenalezen</title>' + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True + + +    def handleFree(self, pyfile): +        #download button +        m = re.search(r'<a class="stahnoutSoubor".*?href="(.*?)"', self.html) +        if m is None: +            self.error(_("Page 1 URL not found")) +        url = "http://bezvadata.cz%s" % m.group(1) + +        #captcha form +        self.html = self.load(url) +        self.checkErrors() +        for _i in xrange(5): +            action, inputs = self.parseHtmlForm('frm-stahnoutFreeForm') +            if not inputs: +                self.error(_("FreeForm")) + +            m = re.search(r'<img src="data:image/png;base64,(.*?)"', self.html) +            if m is None: +                self.error(_("Wrong captcha image")) + +            #captcha image is contained in html page as base64encoded data but decryptCaptcha() expects image url +            self.load, proper_load = self.loadcaptcha, self.load +            try: +                inputs['captcha'] = self.decryptCaptcha(m.group(1), imgtype='png') +            finally: +                self.load = proper_load + +            if '<img src="data:image/png;base64' in self.html: +                self.invalidCaptcha() +            else: +                self.correctCaptcha() +                break +        else: +            self.fail(_("No valid captcha code entered")) + +        #download url +        self.html = self.load("http://bezvadata.cz%s" % action, post=inputs) +        self.checkErrors() +        m = re.search(r'<a class="stahnoutSoubor2" href="(.*?)">', self.html) +        if m is None: +            self.error(_("Page 2 URL not found")) +        url = "http://bezvadata.cz%s" % m.group(1) +        self.logDebug("DL URL %s" % url) + +        #countdown +        m = re.search(r'id="countdown">(\d\d):(\d\d)<', self.html) +        wait_time = (int(m.group(1)) * 60 + int(m.group(2))) if m else 120 +        self.wait(wait_time, False) + +        self.link = url + + +    def checkErrors(self): +        if 'images/button-download-disable.png' in self.html: +            self.longWait(5 * 60, 24)  #: parallel dl limit +        elif '<div class="infobox' in self.html: +            self.tempOffline() + +        self.info.pop('error', None) + + +    def loadcaptcha(self, data, *args, **kwargs): +        return data.decode('base64') diff --git a/pyload/plugin/hoster/BillionuploadsCom.py b/pyload/plugin/hoster/BillionuploadsCom.py new file mode 100644 index 000000000..2dec3e8b8 --- /dev/null +++ b/pyload/plugin/hoster/BillionuploadsCom.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class BillionuploadsCom(XFSHoster): +    __name__    = "BillionuploadsCom" +    __type__    = "hoster" +    __version__ = "0.04" + +    __pattern__ = r'http://(?:www\.)?billionuploads\.com/\w{12}' + +    __description__ = """Billionuploads.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN = r'<td class="dofir" title="(?P<N>.+?)"' +    SIZE_PATTERN = r'<td class="dofir">(?P<S>[\d.,]+) (?P<U>[\w^_]+)' diff --git a/pyload/plugin/hoster/BitshareCom.py b/pyload/plugin/hoster/BitshareCom.py new file mode 100644 index 000000000..56beb7353 --- /dev/null +++ b/pyload/plugin/hoster/BitshareCom.py @@ -0,0 +1,156 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import re + +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class BitshareCom(SimpleHoster): +    __name__    = "BitshareCom" +    __type__    = "hoster" +    __version__ = "0.53" + +    __pattern__ = r'http://(?:www\.)?bitshare\.com/(files/)?(?(1)|\?f=)(?P<ID>\w+)(?(1)/(?P<NAME>.+?)\.html)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Bitshare.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Paul King", ""), +                       ("fragonib", "fragonib[AT]yahoo[DOT]es")] + + +    COOKIES = [("bitshare.com", "language_selection", "EN")] + +    INFO_PATTERN    = r'Downloading (?P<N>.+) - (?P<S>[\d.,]+) (?P<U>[\w^_]+)</h1>' +    OFFLINE_PATTERN = r'[Ff]ile (not available|was deleted|was not found)' + +    AJAXID_PATTERN  = r'var ajaxdl = "(.*?)";' +    TRAFFIC_USED_UP = r'Your Traffic is used up for today' + + +    def setup(self): +        self.multiDL    = self.premium +        self.chunkLimit = 1 + + +    def process(self, pyfile): +        if self.premium: +            self.account.relogin(self.user) + +        # File id +        m = re.match(self.__pattern__, pyfile.url) +        self.file_id = max(m.group('ID1'), m.group('ID2')) +        self.logDebug("File id is [%s]" % self.file_id) + +        # Load main page +        self.html = self.load(pyfile.url, ref=False, decode=True) + +        # Check offline +        if re.search(self.OFFLINE_PATTERN, self.html): +            self.offline() + +        # Check Traffic used up +        if re.search(self.TRAFFIC_USED_UP, self.html): +            self.logInfo(_("Your Traffic is used up for today")) +            self.wait(30 * 60, True) +            self.retry() + +        # File name +        m     = re.match(self.__pattern__, pyfile.url) +        name1 = m.group('NAME') if m else None + +        m     = re.search(self.INFO_PATTERN, self.html) +        name2 = m.group('N') if m else None + +        pyfile.name = max(name1, name2) + +        # Ajax file id +        self.ajaxid = re.search(self.AJAXID_PATTERN, self.html).group(1) +        self.logDebug("File ajax id is [%s]" % self.ajaxid) + +        # This may either download our file or forward us to an error page +        self.link = self.getDownloadUrl() + +        if self.checkDownload({"error": ">Error occured<"}): +            self.retry(5, 5 * 60, "Bitshare host : Error occured") + + +    def getDownloadUrl(self): +        # Return location if direct download is active +        if self.premium: +            header = self.load(self.pyfile.url, just_header=True) +            if 'location' in header: +                return header['location'] + +        # Get download info +        self.logDebug("Getting download info") +        res = self.load("http://bitshare.com/files-ajax/" + self.file_id + "/request.html", +                        post={"request": "generateID", "ajaxid": self.ajaxid}) + +        self.handleErrors(res, ':') + +        parts    = res.split(":") +        filetype = parts[0] +        wait     = int(parts[1]) +        captcha  = int(parts[2]) + +        self.logDebug("Download info [type: '%s', waiting: %d, captcha: %d]" % (filetype, wait, captcha)) + +        # Waiting +        if wait > 0: +            self.logDebug("Waiting %d seconds." % wait) +            if wait < 120: +                self.wait(wait, False) +            else: +                self.wait(wait - 55, True) +                self.retry() + +        # Resolve captcha +        if captcha == 1: +            self.logDebug("File is captcha protected") +            recaptcha = ReCaptcha(self) + +            # Try up to 3 times +            for i in xrange(3): +                response, challenge = recaptcha.challenge() +                res = self.load("http://bitshare.com/files-ajax/" + self.file_id + "/request.html", +                                     post={"request"                  : "validateCaptcha", +                                           "ajaxid"                   : self.ajaxid, +                                           "recaptcha_challenge_field": challenge, +                                           "recaptcha_response_field" : response}) +                if self.handleCaptchaErrors(res): +                    break + +        # Get download URL +        self.logDebug("Getting download url") +        res = self.load("http://bitshare.com/files-ajax/" + self.file_id + "/request.html", +                        post={"request": "getDownloadURL", "ajaxid": self.ajaxid}) + +        self.handleErrors(res, '#') + +        url = res.split("#")[-1] + +        return url + + +    def handleErrors(self, res, separator): +        self.logDebug("Checking response [%s]" % res) +        if "ERROR:Session timed out" in res: +            self.retry() +        elif "ERROR" in res: +            msg = res.split(separator)[-1] +            self.fail(msg) + + +    def handleCaptchaErrors(self, res): +        self.logDebug("Result of captcha resolving [%s]" % res) +        if "SUCCESS" in res: +            self.correctCaptcha() +            return True +        elif "ERROR:SESSION ERROR" in res: +            self.retry() + +        self.invalidCaptcha() diff --git a/pyload/plugin/hoster/BoltsharingCom.py b/pyload/plugin/hoster/BoltsharingCom.py new file mode 100644 index 000000000..58d4a23a9 --- /dev/null +++ b/pyload/plugin/hoster/BoltsharingCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class BoltsharingCom(DeadHoster): +    __name__    = "BoltsharingCom" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?boltsharing\.com/\w{12}' +    __config__  = [] + +    __description__ = """Boltsharing.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/hoster/CatShareNet.py b/pyload/plugin/hoster/CatShareNet.py new file mode 100644 index 000000000..b9d5ad650 --- /dev/null +++ b/pyload/plugin/hoster/CatShareNet.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class CatShareNet(SimpleHoster): +    __name__    = "CatShareNet" +    __type__    = "hoster" +    __version__ = "0.13" + +    __pattern__ = r'http://(?:www\.)?catshare\.net/\w{16}' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """CatShare.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("z00nx", "z00nx0@gmail.com"), +                       ("prOq", ""), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    TEXT_ENCODING = True + +    INFO_PATTERN = r'<title>(?P<N>.+) \((?P<S>[\d.,]+) (?P<U>[\w^_]+)\)<' +    OFFLINE_PATTERN = r'<div class="alert alert-error"' + +    IP_BLOCKED_PATTERN = ur'>Nasz serwis wykryÅ ÅŒe Twój adres IP nie pochodzi z Polski.<' +    WAIT_PATTERN       = r'var\scount\s=\s(\d+);' + +    LINK_FREE_PATTERN    = r'<form action="(.+?)" method="GET">' +    LINK_PREMIUM_PATTERN = r'<form action="(.+?)" method="GET">' + + +    def setup(self): +        self.multiDL        = self.premium +        self.resumeDownload = True + + +    def checkErrors(self): +        m = re.search(self.IP_BLOCKED_PATTERN, self.html) +        if m: +            self.fail(_("Only connections from Polish IP address are allowed")) + +        return super(CatShareNet, self).checkErrors() + + +    def handleFree(self, pyfile): +        recaptcha = ReCaptcha(self) + +        response, challenge = recaptcha.challenge() +        self.html = self.load(pyfile.url, +                              post={'recaptcha_challenge_field': challenge, +                                    'recaptcha_response_field' : response}) + +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m: +            self.link = m.group(1) + diff --git a/pyload/plugin/hoster/CloudzerNet.py b/pyload/plugin/hoster/CloudzerNet.py new file mode 100644 index 000000000..775b4656a --- /dev/null +++ b/pyload/plugin/hoster/CloudzerNet.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class CloudzerNet(DeadHoster): +    __name__    = "CloudzerNet" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'https?://(?:www\.)?(cloudzer\.net/file/|clz\.to/(file/)?)\w+' +    __config__  = [] + +    __description__ = """Cloudzer.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("gs", "I-_-I-_-I@web.de"), +                       ("z00nx", "z00nx0@gmail.com"), +                       ("stickell", "l.stickell@yahoo.it")] diff --git a/pyload/plugin/hoster/CloudzillaTo.py b/pyload/plugin/hoster/CloudzillaTo.py new file mode 100644 index 000000000..337aa9d3c --- /dev/null +++ b/pyload/plugin/hoster/CloudzillaTo.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class CloudzillaTo(SimpleHoster): +    __name__    = "CloudzillaTo" +    __type__    = "hoster" +    __version__ = "0.06" + +    __pattern__ = r'http://(?:www\.)?cloudzilla\.to/share/file/(?P<ID>[\w^_]+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Cloudzilla.to hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    INFO_PATTERN    = r'title="(?P<N>.+?)">\1</span> <span class="size">\((?P<S>[\d.]+) (?P<U>[\w^_]+)' +    OFFLINE_PATTERN = r'>File not found...<' + +    PASSWORD_PATTERN = r'<div id="pwd_protected">' + + +    def checkErrors(self): +        m = re.search(self.PASSWORD_PATTERN, self.html) +        if m: +            self.html = self.load(self.pyfile.url, get={'key': self.getPassword()}) + +        if re.search(self.PASSWORD_PATTERN, self.html): +            self.retry(reason="Wrong password") + + +    def handleFree(self, pyfile): +        self.html = self.load("http://www.cloudzilla.to/generateticket/", +                              post={'file_id': self.info['pattern']['ID'], 'key': self.getPassword()}) + +        ticket = dict(re.findall(r'<(.+?)>([^<>]+?)</', self.html)) + +        self.logDebug(ticket) + +        if 'error' in ticket: +            if "File is password protected" in ticket['error']: +                self.retry(reason="Wrong password") +            else: +                self.fail(ticket['error']) + +        if 'wait' in ticket: +            self.wait(ticket['wait'], int(ticket['wait']) > 5) + +        self.link = "http://%(server)s/download/%(file_id)s/%(ticket_id)s" % {'server'   : ticket['server'], +                                                                              'file_id'  : self.info['pattern']['ID'], +                                                                              'ticket_id': ticket['ticket_id']} + + +    def handlePremium(self, pyfile): +        return self.handleFree(pyfile) diff --git a/pyload/plugin/hoster/CramitIn.py b/pyload/plugin/hoster/CramitIn.py new file mode 100644 index 000000000..3ccb3cfc6 --- /dev/null +++ b/pyload/plugin/hoster/CramitIn.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class CramitIn(XFSHoster): +    __name__    = "CramitIn" +    __type__    = "hoster" +    __version__ = "0.07" + +    __pattern__ = r'http://(?:www\.)?cramit\.in/\w{12}' + +    __description__ = """Cramit.in hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    INFO_PATTERN = r'<span class=t2>\s*(?P<N>.*?)</span>.*?<small>\s*\((?P<S>.*?)\)' + +    LINK_PATTERN = r'href="(http://cramit\.in/file_download/.*?)"' diff --git a/pyload/plugin/hoster/CrockoCom.py b/pyload/plugin/hoster/CrockoCom.py new file mode 100644 index 000000000..2ac9062fb --- /dev/null +++ b/pyload/plugin/hoster/CrockoCom.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class CrockoCom(SimpleHoster): +    __name__    = "CrockoCom" +    __type__    = "hoster" +    __version__ = "0.19" + +    __pattern__ = r'http://(?:www\.)?(crocko|easy-share)\.com/\w+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Crocko hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN    = r'<span class="fz24">Download:\s*<strong>(?P<N>.*)' +    SIZE_PATTERN    = r'<span class="tip1"><span class="inner">(?P<S>[^<]+)</span></span>' +    OFFLINE_PATTERN = r'<h1>Sorry,<br />the page you\'re looking for <br />isn\'t here.</h1>|File not found' + +    CAPTCHA_PATTERN = r"u='(/file_contents/captcha/\w+)';\s*w='(\d+)';" + +    FORM_PATTERN = r'<form  method="post" action="(.+?)">(.*?)</form>' +    FORM_INPUT_PATTERN = r'<input[^>]* name="?([^" ]+)"? value="?([^" ]+)"?.*?>' + +    NAME_REPLACEMENTS = [(r'<.*?>', '')] + + +    def handleFree(self, pyfile): +        if "You need Premium membership to download this file." in self.html: +            self.fail(_("You need Premium membership to download this file")) + +        for _i in xrange(5): +            m = re.search(self.CAPTCHA_PATTERN, self.html) +            if m: +                url, wait_time = 'http://crocko.com' + m.group(1), int(m.group(2)) +                self.wait(wait_time) +                self.html = self.load(url) +            else: +                break + +        m = re.search(self.FORM_PATTERN, self.html, re.S) +        if m is None: +            self.error(_("FORM_PATTERN not found")) + +        action, form = m.groups() +        inputs = dict(re.findall(self.FORM_INPUT_PATTERN, form)) +        recaptcha = ReCaptcha(self) + +        for _i in xrange(5): +            inputs['recaptcha_response_field'], inputs['recaptcha_challenge_field'] = recaptcha.challenge() +            self.download(action, post=inputs) + +            if self.checkDownload({"captcha": recaptcha.KEY_AJAX_PATTERN}): +                self.invalidCaptcha() +            else: +                break +        else: +            self.fail(_("No valid captcha solution received")) diff --git a/pyload/plugin/hoster/CyberlockerCh.py b/pyload/plugin/hoster/CyberlockerCh.py new file mode 100644 index 000000000..ec06844c3 --- /dev/null +++ b/pyload/plugin/hoster/CyberlockerCh.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class CyberlockerCh(DeadHoster): +    __name__    = "CyberlockerCh" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?cyberlocker\.ch/\w+' +    __config__  = [] + +    __description__ = """Cyberlocker.ch hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] diff --git a/pyload/plugin/hoster/CzshareCom.py b/pyload/plugin/hoster/CzshareCom.py new file mode 100644 index 000000000..03b46f444 --- /dev/null +++ b/pyload/plugin/hoster/CzshareCom.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://czshare.com/5278880/random.bin + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster +from pyload.utils import parseFileSize + + +class CzshareCom(SimpleHoster): +    __name__    = "CzshareCom" +    __type__    = "hoster" +    __version__ = "0.99" + +    __pattern__ = r'http://(?:www\.)?(czshare|sdilej)\.(com|cz)/(\d+/|download\.php\?).+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """CZshare.com hoster plugin, now Sdilej.cz""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN    = r'<div class="tab" id="parameters">\s*<p>\s*Cel. n.zev: <a href=.*?>(?P<N>[^<]+)</a>' +    SIZE_PATTERN    = r'<div class="tab" id="category">(?:\s*<p>[^\n]*</p>)*\s*Velikost:\s*(?P<S>[\d .,]+)(?P<U>[\w^_]+)\s*</div>' +    OFFLINE_PATTERN = r'<div class="header clearfix">\s*<h2 class="red">' + +    SIZE_REPLACEMENTS = [(' ', '')] +    URL_REPLACEMENTS  = [(r'http://[^/]*/download.php\?.*?id=(\w+).*', r'http://sdilej.cz/\1/x/')] + +    CHECK_TRAFFIC = True + +    FREE_URL_PATTERN     = r'<a href="(.+?)" class="page-download">[^>]*alt="(.+?)" /></a>' +    FREE_FORM_PATTERN    = r'<form action="download\.php" method="post">\s*<img src="captcha\.php" id="captcha" />(.*?)</form>' +    PREMIUM_FORM_PATTERN = r'<form action="/profi_down\.php" method="post">(.*?)</form>' +    FORM_INPUT_PATTERN   = r'<input[^>]* name="(.+?)" value="(.+?)"[^>]*/>' +    MULTIDL_PATTERN      = r'<p><font color=\'red\'>Z[^<]*PROFI.</font></p>' +    USER_CREDIT_PATTERN  = r'<div class="credit">\s*kredit: <strong>([\d .,]+)(\w+)</strong>\s*</div><!-- .credit -->' + + +    def checkTrafficLeft(self): +        # check if user logged in +        m = re.search(self.USER_CREDIT_PATTERN, self.html) +        if m is None: +            self.account.relogin(self.user) +            self.html = self.load(self.pyfile.url, decode=True) +            m = re.search(self.USER_CREDIT_PATTERN, self.html) +            if m is None: +                return False + +        # check user credit +        try: +            credit = parseFileSize(m.group(1).replace(' ', ''), m.group(2)) +            self.logInfo(_("Premium download for %i KiB of Credit") % (self.pyfile.size / 1024)) +            self.logInfo(_("User %s has %i KiB left") % (self.user, credit / 1024)) +            if credit < self.pyfile.size: +                self.logInfo(_("Not enough credit to download file: %s") % self.pyfile.name) +                return False +        except Exception, e: +            # let's continue and see what happens... +            self.logError(e) + +        return True + + +    def handlePremium(self, pyfile): +    # parse download link +        try: +            form = re.search(self.PREMIUM_FORM_PATTERN, self.html, re.S).group(1) +            inputs = dict(re.findall(self.FORM_INPUT_PATTERN, form)) +        except Exception, e: +            self.logError(e) +            self.resetAccount() + +        # download the file, destination is determined by pyLoad +        self.download("http://sdilej.cz/profi_down.php", post=inputs, disposition=True) + + +    def handleFree(self, pyfile): +        # get free url +        m = re.search(self.FREE_URL_PATTERN, self.html) +        if m is None: +            self.error(_("FREE_URL_PATTERN not found")) + +        parsed_url = "http://sdilej.cz" + m.group(1) + +        self.logDebug("PARSED_URL:" + parsed_url) + +        # get download ticket and parse html +        self.html = self.load(parsed_url, decode=True) +        if re.search(self.MULTIDL_PATTERN, self.html): +            self.longWait(5 * 60, 12) + +        try: +            form = re.search(self.FREE_FORM_PATTERN, self.html, re.S).group(1) +            inputs = dict(re.findall(self.FORM_INPUT_PATTERN, form)) +            pyfile.size = int(inputs['size']) + +        except Exception, e: +            self.logError(e) +            self.error(_("Form")) + +        # get and decrypt captcha +        captcha_url = 'http://sdilej.cz/captcha.php' +        for _i in xrange(5): +            inputs['captchastring2'] = self.decryptCaptcha(captcha_url) +            self.html = self.load(parsed_url, post=inputs, decode=True) + +            if u"<li>ZadanÜ ovÄÅovacà kód nesouhlasÃ!</li>" in self.html: +                self.invalidCaptcha() + +            elif re.search(self.MULTIDL_PATTERN, self.html): +                self.longWait(5 * 60, 12) + +            else: +                self.correctCaptcha() +                break +        else: +            self.fail(_("No valid captcha code entered")) + +        m = re.search("countdown_number = (\d+);", self.html) +        self.setWait(int(m.group(1)) if m else 50) + +        # download the file, destination is determined by pyLoad +        self.logDebug("WAIT URL", self.req.lastEffectiveURL) + +        m = re.search("free_wait.php\?server=(.*?)&(.*)", self.req.lastEffectiveURL) +        if m is None: +            self.error(_("Download URL not found")) + +        self.link = "http://%s/download.php?%s" % (m.group(1), m.group(2)) + +        self.wait() + + +    def checkFile(self, rules={}): +        # check download +        check = self.checkDownload({ +            "temp offline" : re.compile(r"^Soubor je do.*asn.* nedostupn.*$"), +            "credit"       : re.compile(r"^Nem.*te dostate.*n.* kredit.$"), +            "multi-dl"     : re.compile(self.MULTIDL_PATTERN), +            "captcha"      : "<li>ZadanÜ ovÄÅovacà kód nesouhlasÃ!</li>" +        }) + +        if check == "temp offline": +            self.fail(_("File not available - try later")) + +        elif check == "credit": +            self.resetAccount() + +        elif check == "multi-dl": +            self.longWait(5 * 60, 12) + +        elif check == "captcha": +            self.invalidCaptcha() +            self.retry() + +        return super(CzshareCom, self).checkFile(rules) diff --git a/pyload/plugin/hoster/DailymotionCom.py b/pyload/plugin/hoster/DailymotionCom.py new file mode 100644 index 000000000..f90067446 --- /dev/null +++ b/pyload/plugin/hoster/DailymotionCom.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.datatype.File import statusMap +from pyload.utils import json_loads +from pyload.network.RequestFactory import getURL +from pyload.plugin.Hoster import Hoster + + +def getInfo(urls): +    result  = [] +    regex   = re.compile(DailymotionCom.__pattern__) +    apiurl  = "https://api.dailymotion.com/video/%s" +    request = {"fields": "access_error,status,title"} + +    for url in urls: +        id   = regex.match(url).group('ID') +        html = getURL(apiurl % id, get=request) +        info = json_loads(html) + +        name = info['title'] + ".mp4" if "title" in info else url + +        if "error" in info or info['access_error']: +            status = "offline" +        else: +            status = info['status'] +            if status in ("ready", "published"): +                status = "online" +            elif status in ("waiting", "processing"): +                status = "temp. offline" +            else: +                status = "offline" + +        result.append((name, 0, statusMap[status], url)) + +    return result + + +class DailymotionCom(Hoster): +    __name__    = "DailymotionCom" +    __type__    = "hoster" +    __version__ = "0.20" + +    __pattern__ = r'https?://(?:www\.)?dailymotion\.com/.*video/(?P<ID>[\w^_]+)' +    __config__  = [("quality", "Lowest;LD 144p;LD 240p;SD 384p;HQ 480p;HD 720p;HD 1080p;Highest", "Quality", "Highest")] + +    __description__ = """Dailymotion.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True + + +    def getStreams(self): +        streams = [] + +        for result in re.finditer(r"\"(?P<URL>http:\\/\\/www.dailymotion.com\\/cdn\\/H264-(?P<QF>.*?)\\.*?)\"", +                                  self.html): +            url = result.group('URL') +            qf  = result.group('QF') + +            link    = url.replace("\\", "") +            quality = tuple(int(x) for x in qf.split("x")) + +            streams.append((quality, link)) + +        return sorted(streams, key=lambda x: x[0][::-1]) + + +    def getQuality(self): +        q = self.getConfig('quality') + +        if q == "Lowest": +            quality = 0 +        elif q == "Highest": +            quality = -1 +        else: +            quality = int(q.rsplit(" ")[1][:-1]) + +        return quality + + +    def getLink(self, streams, quality): +        if quality > 0: +            for x, s in [item for item in enumerate(streams)][::-1]: +                qf = s[0][1] +                if qf <= quality: +                    idx = x +                    break +            else: +                idx = 0 +        else: +            idx = quality + +        s = streams[idx] + +        self.logInfo(_("Download video quality %sx%s") % s[0]) + +        return s[1] + + +    def checkInfo(self, pyfile): +        pyfile.name, pyfile.size, pyfile.status, pyfile.url = getInfo([pyfile.url])[0] + +        if pyfile.status == 1: +            self.offline() + +        elif pyfile.status == 6: +            self.tempOffline() + + +    def process(self, pyfile): +        self.checkInfo(pyfile) + +        id = re.match(self.__pattern__, pyfile.url).group('ID') +        self.html = self.load("http://www.dailymotion.com/embed/video/" + id, decode=True) + +        streams = self.getStreams() +        quality = self.getQuality() + +        self.download(self.getLink(streams, quality)) diff --git a/pyload/plugin/hoster/DataHu.py b/pyload/plugin/hoster/DataHu.py new file mode 100644 index 000000000..3736282d2 --- /dev/null +++ b/pyload/plugin/hoster/DataHu.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://data.hu/get/6381232/random.bin + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class DataHu(SimpleHoster): +    __name__    = "DataHu" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?data\.hu/get/\w+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Data.hu hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("crash", ""), +                       ("stickell", "l.stickell@yahoo.it")] + + +    INFO_PATTERN = ur'<title>(?P<N>.*) \((?P<S>[^)]+)\) let\xf6lt\xe9se</title>' +    OFFLINE_PATTERN = ur'Az adott f\xe1jl nem l\xe9tezik' +    LINK_FREE_PATTERN = r'<div class="download_box_button"><a href="(.+?)">' + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = self.premium diff --git a/pyload/plugin/hoster/DataportCz.py b/pyload/plugin/hoster/DataportCz.py new file mode 100644 index 000000000..ecc9f8d5d --- /dev/null +++ b/pyload/plugin/hoster/DataportCz.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class DataportCz(SimpleHoster): +    __name__    = "DataportCz" +    __type__    = "hoster" +    __version__ = "0.41" + +    __pattern__ = r'http://(?:www\.)?dataport\.cz/file/(.+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Dataport.cz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN = r'<span itemprop="name">(?P<N>[^<]+)</span>' +    SIZE_PATTERN = r'<td class="fil">Velikost</td>\s*<td>(?P<S>[^<]+)</td>' +    OFFLINE_PATTERN = r'<h2>Soubor nebyl nalezen</h2>' + +    CAPTCHA_PATTERN = r'<section id="captcha_bg">\s*<img src="(.*?)"' +    FREE_SLOTS_PATTERN = ur'PoÄet volnÜch slotů: <span class="darkblue">(\d+)</span><br />' + + +    def handleFree(self, pyfile): +        captchas = {"1": "jkeG", "2": "hMJQ", "3": "vmEK", "4": "ePQM", "5": "blBd"} + +        for _i in xrange(60): +            action, inputs = self.parseHtmlForm('free_download_form') +            self.logDebug(action, inputs) +            if not action or not inputs: +                self.error(_("free_download_form")) + +            if "captchaId" in inputs and inputs['captchaId'] in captchas: +                inputs['captchaCode'] = captchas[inputs['captchaId']] +            else: +                self.error(_("captcha")) + +            self.download("http://www.dataport.cz%s" % action, post=inputs) + +            check = self.checkDownload({"captcha": 'alert("\u0160patn\u011b opsan\u00fd k\u00f3d z obr\u00e1zu");', +                                        "slot"   : 'alert("Je n\u00e1m l\u00edto, ale moment\u00e1ln\u011b nejsou'}) +            if check == "captcha": +                self.error(_("invalid captcha")) + +            elif check == "slot": +                self.logDebug("No free slots - wait 60s and retry") +                self.wait(60, False) +                self.html = self.load(pyfile.url, decode=True) +                continue + +            else: +                break diff --git a/pyload/plugin/hoster/DateiTo.py b/pyload/plugin/hoster/DateiTo.py new file mode 100644 index 000000000..9b8eeb3c5 --- /dev/null +++ b/pyload/plugin/hoster/DateiTo.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class DateiTo(SimpleHoster): +    __name__    = "DateiTo" +    __type__    = "hoster" +    __version__ = "0.07" + +    __pattern__ = r'http://(?:www\.)?datei\.to/datei/(?P<ID>\w+)\.html' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Datei.to hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN    = r'Dateiname:</td>\s*<td colspan="2"><strong>(?P<N>.*?)</' +    SIZE_PATTERN    = r'Dateigröße:</td>\s*<td colspan="2">(?P<S>.*?)</' +    OFFLINE_PATTERN = r'>Datei wurde nicht gefunden<|>Bitte wÀhle deine Datei aus... <' + +    WAIT_PATTERN    = r'countdown\({seconds: (\d+)' +    MULTIDL_PATTERN = r'>Du lÀdst bereits eine Datei herunter<' + +    DATA_PATTERN = r'url: "(.*?)", data: "(.*?)",' + + +    def handleFree(self, pyfile): +        url = 'http://datei.to/ajax/download.php' +        data = {'P': 'I', 'ID': self.info['pattern']['ID']} +        recaptcha = ReCaptcha(self) + +        for _i in xrange(10): +            self.logDebug("URL", url, "POST", data) +            self.html = self.load(url, post=data) +            self.checkErrors() + +            if url.endswith('download.php') and 'P' in data: +                if data['P'] == 'I': +                    self.doWait() + +                elif data['P'] == 'IV': +                    break + +            m = re.search(self.DATA_PATTERN, self.html) +            if m is None: +                self.error(_("data")) +            url = 'http://datei.to/' + m.group(1) +            data = dict(x.split('=') for x in m.group(2).split('&')) + +            if url.endswith('recaptcha.php'): +                data['recaptcha_response_field'], data['recaptcha_challenge_field'] = recaptcha.challenge() +        else: +            self.fail(_("Too bad...")) + +        self.link = self.html + + +    def checkErrors(self): +        m = re.search(self.MULTIDL_PATTERN, self.html) +        if m: +            m = re.search(self.WAIT_PATTERN, self.html) +            wait_time = int(m.group(1)) if m else 30 + +            errmsg = self.info['error'] = _("Parallel downloads") +            self.retry(wait_time=wait_time, reason=errmsg) + +        self.info.pop('error', None) + + +    def doWait(self): +        m = re.search(self.WAIT_PATTERN, self.html) +        wait_time = int(m.group(1)) if m else 30 + +        self.load('http://datei.to/ajax/download.php', post={'P': 'Ads'}) +        self.wait(wait_time, False) diff --git a/pyload/plugin/hoster/DdlstorageCom.py b/pyload/plugin/hoster/DdlstorageCom.py new file mode 100644 index 000000000..c2077eb16 --- /dev/null +++ b/pyload/plugin/hoster/DdlstorageCom.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class DdlstorageCom(DeadHoster): +    __name__    = "DdlstorageCom" +    __type__    = "hoster" +    __version__ = "1.02" + +    __pattern__ = r'https?://(?:www\.)?ddlstorage\.com/\w+' +    __config__  = [] + +    __description__ = """DDLStorage.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] diff --git a/pyload/plugin/hoster/DebridItaliaCom.py b/pyload/plugin/hoster/DebridItaliaCom.py new file mode 100644 index 000000000..af9500707 --- /dev/null +++ b/pyload/plugin/hoster/DebridItaliaCom.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.MultiHoster import MultiHoster + + +class DebridItaliaCom(MultiHoster): +    __name__    = "DebridItaliaCom" +    __type__    = "hoster" +    __version__ = "0.17" + +    __pattern__ = r'https?://(?:www\.|s\d+\.)?debriditalia\.com/dl/\d+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Debriditalia.com multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    URL_REPLACEMENTS = [("https://", "http://")] + + +    def handlePremium(self, pyfile): +        self.html = self.load("http://www.debriditalia.com/api.php", +                              get={'generate': "on", 'link': pyfile.url, 'p': self.getPassword()}) + +        if "ERROR:" not in self.html: +            self.link = self.html.strip() +        else: +            self.info['error'] = re.search(r'ERROR:(.*)', self.html).group(1).strip() + +            self.html = self.load("http://debriditalia.com/linkgen2.php", +                                  post={'xjxfun'   : "convertiLink", +                                        'xjxargs[]': "S<![CDATA[%s]]>" % pyfile.url, +                                        'xjxargs[]': "S%s" % self.getPassword()}) +            try: +                self.link = re.search(r'<a href="(.+?)"', self.html).group(1) +            except AttributeError: +                pass diff --git a/pyload/plugin/hoster/DepositfilesCom.py b/pyload/plugin/hoster/DepositfilesCom.py new file mode 100644 index 000000000..d718b58dd --- /dev/null +++ b/pyload/plugin/hoster/DepositfilesCom.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- + +import re + +from urllib import unquote + +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class DepositfilesCom(SimpleHoster): +    __name__    = "DepositfilesCom" +    __type__    = "hoster" +    __version__ = "0.55" + +    __pattern__ = r'https?://(?:www\.)?(depositfiles\.com|dfiles\.(eu|ru))(/\w{1,3})?/files/(?P<ID>\w+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Depositfiles.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org"), +                       ("zoidberg", "zoidberg@mujmail.cz"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    NAME_PATTERN    = r'<script type="text/javascript">eval\( unescape\(\'(?P<N>.*?)\'' +    SIZE_PATTERN    = r': <b>(?P<S>[\d.,]+) (?P<U>[\w^_]+)</b>' +    OFFLINE_PATTERN = r'<span class="html_download_api-not_exists"></span>' + +    NAME_REPLACEMENTS = [(r'\%u([0-9A-Fa-f]{4})', lambda m: unichr(int(m.group(1), 16))), +                         (r'.*<b title="(?P<N>.+?)".*', "\g<N>")] +    URL_REPLACEMENTS  = [(__pattern__ + ".*", "https://dfiles.eu/files/\g<ID>")] + +    COOKIES = [("dfiles.eu", "lang_current", "en")] + +    WAIT_PATTERN = r'(?:download_waiter_remain">|html_download_api-limit_interval">|>Please wait|>Try in).+' +    ERROR_PATTER = r'File is checked, please try again in a minute' + +    LINK_FREE_PATTERN    = r'<form id="downloader_file_form" action="(http://.+?\.(dfiles\.eu|depositfiles\.com)/.+?)" method="post"' +    LINK_PREMIUM_PATTERN = r'class="repeat"><a href="(.+?)"' +    LINK_MIRROR_PATTERN  = r'class="repeat_mirror"><a href="(.+?)"' + + +    def handleFree(self, pyfile): +        self.html = self.load(pyfile.url, post={'gateway_result': "1"}) + +        self.checkErrors() + +        m = re.search(r"var fid = '(\w+)';", self.html) +        if m is None: +            self.retry(wait_time=5) +        params = {'fid': m.group(1)} +        self.logDebug("FID: %s" % params['fid']) + +        self.checkErrors() + +        recaptcha = ReCaptcha(self) +        captcha_key = recaptcha.detect_key() +        if captcha_key is None: +            return + +        self.html = self.load("https://dfiles.eu/get_file.php", get=params) + +        if '<input type=button value="Continue" onclick="check_recaptcha' in self.html: +            params['response'], params['challenge'] = recaptcha.challenge(captcha_key) +            self.html = self.load("https://dfiles.eu/get_file.php", get=params) + +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m: +            self.link = unquote(m.group(1)) + + +    def handlePremium(self, pyfile): +        if '<span class="html_download_api-gold_traffic_limit">' in self.html: +            self.logWarning(_("Download limit reached")) +            self.retry(25, 60 * 60, "Download limit reached") + +        elif 'onClick="show_gold_offer' in self.html: +            self.account.relogin(self.user) +            self.retry() + +        else: +            link   = re.search(self.LINK_PREMIUM_PATTERN, self.html) +            mirror = re.search(self.LINK_MIRROR_PATTERN, self.html) + +            if link: +                self.link = link.group(1) + +            elif mirror: +                self.link = mirror.group(1) diff --git a/pyload/plugin/hoster/DevhostSt.py b/pyload/plugin/hoster/DevhostSt.py new file mode 100644 index 000000000..5816f1ba0 --- /dev/null +++ b/pyload/plugin/hoster/DevhostSt.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# +# Test links: +#   http://d-h.st/mM8 + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class DevhostSt(SimpleHoster): +    __name__    = "DevhostSt" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?d-h\.st/(?!users/)\w{3}' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """d-h.st hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de")] + + +    NAME_PATTERN      = r'<span title="(?P<N>.*?)"' +    SIZE_PATTERN      = r'</span> \((?P<S>[\d.,]+) (?P<U>[\w^_]+)\)<br' +    HASHSUM_PATTERN   = r'>(?P<T>.*?) Sum</span>:  (?P<H>.*?)<br' + +    OFFLINE_PATTERN   = r'>File Not Found' +    LINK_FREE_PATTERN = r'var product_download_url= \'(.+?)\'' + + +    def setup(self): +        self.multiDL    = True +        self.chunkLimit = 1 diff --git a/pyload/plugin/hoster/DlFreeFr.py b/pyload/plugin/hoster/DlFreeFr.py new file mode 100644 index 000000000..22a32bcf4 --- /dev/null +++ b/pyload/plugin/hoster/DlFreeFr.py @@ -0,0 +1,138 @@ +# -*- coding: utf-8 -*- + +import pycurl +import re + +from pyload.network.Browser import Browser +from pyload.network.CookieJar import CookieJar +from pyload.plugin.captcha.AdYouLike import AdYouLike +from pyload.plugin.internal.SimpleHoster import SimpleHoster, replace_patterns +from pyload.utils import json_loads + + +class CustomBrowser(Browser): + +    def __init__(self, bucket=None, options={}): +        Browser.__init__(self, bucket, options) + + +    def load(self, *args, **kwargs): +        post = kwargs.get("post") + +        if post is None and len(args) > 2: +            post = args[2] + +        if post: +            self.http.c.setopt(pycurl.FOLLOWLOCATION, 0) +            self.http.c.setopt(pycurl.POST, 1) +            self.http.c.setopt(pycurl.CUSTOMREQUEST, "POST") +        else: +            self.http.c.setopt(pycurl.FOLLOWLOCATION, 1) +            self.http.c.setopt(pycurl.POST, 0) +            self.http.c.setopt(pycurl.CUSTOMREQUEST, "GET") + +        return Browser.load(self, *args, **kwargs) + + +class DlFreeFr(SimpleHoster): +    __name__    = "DlFreeFr" +    __type__    = "hoster" +    __version__ = "0.28" + +    __pattern__ = r'http://(?:www\.)?dl\.free\.fr/(\w+|getfile\.pl\?file=/\w+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Dl.free.fr hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("the-razer", "daniel_ AT gmx DOT net"), +                       ("zoidberg", "zoidberg@mujmail.cz"), +                       ("Toilal", "toilal.dev@gmail.com")] + + +    NAME_PATTERN = r'Fichier:</td>\s*<td.*?>(?P<N>[^>]*)</td>' +    SIZE_PATTERN = r'Taille:</td>\s*<td.*?>(?P<S>[\d.,]+\w)o' +    OFFLINE_PATTERN = r'Erreur 404 - Document non trouv|Fichier inexistant|Le fichier demandé n\'a pas été trouvé' + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True +        self.limitDL        = 5 +        self.chunkLimit     = 1 + + +    def init(self): +        factory = self.core.requestFactory +        self.req = CustomBrowser(factory.bucket, factory.getOptions()) + + +    def process(self, pyfile): +        pyfile.url = replace_patterns(pyfile.url, self.URL_REPLACEMENTS) +        valid_url = pyfile.url +        headers = self.load(valid_url, just_header=True) + +        if headers.get('code') == 302: +            valid_url = headers.get('location') +            headers = self.load(valid_url, just_header=True) + +        if headers.get('code') == 200: +            content_type = headers.get('content-type') +            if content_type and content_type.startswith("text/html"): +                # Undirect acces to requested file, with a web page providing it (captcha) +                self.html = self.load(valid_url) +                self.handleFree(pyfile) +            else: +                # Direct access to requested file for users using free.fr as Internet Service Provider. +                self.link = valid_url + +        elif headers.get('code') == 404: +            self.offline() + +        else: +            self.fail(_("Invalid return code: ") + str(headers.get('code'))) + + +    def handleFree(self, pyfile): +        action, inputs = self.parseHtmlForm('action="getfile.pl"') + +        adyoulike = AdYouLike(self) +        response, challenge = adyoulike.challenge() +        inputs.update(response) + +        self.load("http://dl.free.fr/getfile.pl", post=inputs) +        headers = self.getLastHeaders() +        if headers.get("code") == 302 and "set-cookie" in headers and "location" in headers: +            m = re.search("(.*?)=(.*?); path=(.*?); domain=(.*?)", headers.get("set-cookie")) +            cj = CookieJar(__name__) +            if m: +                cj.setCookie(m.group(4), m.group(1), m.group(2), m.group(3)) +            else: +                self.fail(_("Cookie error")) + +            self.link = headers.get("location") + +            self.req.setCookieJar(cj) +        else: +            self.fail(_("Invalid response")) + + +    def getLastHeaders(self): +        #parse header +        header = {"code": self.req.code} +        for line in self.req.http.header.splitlines(): +            line = line.strip() +            if not line or ":" not in line: +                continue + +            key, none, value = line.partition(":") +            key = key.lower().strip() +            value = value.strip() + +            if key in header: +                if type(header[key]) == list: +                    header[key].append(value) +                else: +                    header[key] = [header[key], value] +            else: +                header[key] = value +        return header diff --git a/pyload/plugin/hoster/DodanePl.py b/pyload/plugin/hoster/DodanePl.py new file mode 100644 index 000000000..9bd5e45a9 --- /dev/null +++ b/pyload/plugin/hoster/DodanePl.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class DodanePl(DeadHoster): +    __name__    = "DodanePl" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?dodane\.pl/file/\d+' +    __config__  = [] + +    __description__ = """Dodane.pl hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("z00nx", "z00nx0@gmail.com")] diff --git a/pyload/plugin/hoster/DuploadOrg.py b/pyload/plugin/hoster/DuploadOrg.py new file mode 100644 index 000000000..6b7574eee --- /dev/null +++ b/pyload/plugin/hoster/DuploadOrg.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class DuploadOrg(DeadHoster): +    __name__    = "DuploadOrg" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?dupload\.org/\w{12}' +    __config__  = [] + +    __description__ = """Dupload.grg hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] diff --git a/pyload/plugin/hoster/EasybytezCom.py b/pyload/plugin/hoster/EasybytezCom.py new file mode 100644 index 000000000..07e75714d --- /dev/null +++ b/pyload/plugin/hoster/EasybytezCom.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class EasybytezCom(XFSHoster): +    __name__    = "EasybytezCom" +    __type__    = "hoster" +    __version__ = "0.23" + +    __pattern__ = r'http://(?:www\.)?easybytez\.com/\w{12}' + +    __description__ = """Easybytez.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    OFFLINE_PATTERN = r'>File not available' + +    LINK_PATTERN = r'(http://(\w+\.(easybytez|easyload|ezbytez|zingload)\.(com|to)|\d+\.\d+\.\d+\.\d+)/files/\d+/\w+/.+?)["\'<]' diff --git a/pyload/plugin/hoster/EdiskCz.py b/pyload/plugin/hoster/EdiskCz.py new file mode 100644 index 000000000..2a8fe867e --- /dev/null +++ b/pyload/plugin/hoster/EdiskCz.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class EdiskCz(SimpleHoster): +    __name__    = "EdiskCz" +    __type__    = "hoster" +    __version__ = "0.23" + +    __pattern__ = r'http://(?:www\.)?edisk\.(cz|sk|eu)/(stahni|sk/stahni|en/download)/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Edisk.cz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    INFO_PATTERN = r'<span class="fl" title="(?P<N>.+?)">\s*.*?\((?P<S>[\d.,]+) (?P<U>[\w^_]+)\)</h1></span>' +    OFFLINE_PATTERN = r'<h3>This file does not exist due to one of the following:</h3><ul><li>' + +    ACTION_PATTERN = r'/en/download/(\d+/.*\.html)' +    LINK_FREE_PATTERN = r'http://.*edisk\.cz.*\.html' + + +    def setup(self): +        self.multiDL = False + + +    def process(self, pyfile): +        url = re.sub("/(stahni|sk/stahni)/", "/en/download/", pyfile.url) + +        self.logDebug("URL:" + url) + +        m = re.search(self.ACTION_PATTERN, url) +        if m is None: +            self.error(_("ACTION_PATTERN not found")) +        action = m.group(1) + +        self.html = self.load(url, decode=True) +        self.getFileInfo() + +        self.html = self.load(re.sub("/en/download/", "/en/download-slow/", url)) + +        url = self.load(re.sub("/en/download/", "/x-download/", url), post={ +            "action": action +        }) + +        if not re.match(self.LINK_FREE_PATTERN, url): +            self.fail(_("Unexpected server response")) + +        self.link = url + diff --git a/pyload/plugin/hoster/EgoFilesCom.py b/pyload/plugin/hoster/EgoFilesCom.py new file mode 100644 index 000000000..150b8bc0c --- /dev/null +++ b/pyload/plugin/hoster/EgoFilesCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class EgoFilesCom(DeadHoster): +    __name__    = "EgoFilesCom" +    __type__    = "hoster" +    __version__ = "0.16" + +    __pattern__ = r'https?://(?:www\.)?egofiles\.com/\w+' +    __config__  = [] + +    __description__ = """Egofiles.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] diff --git a/pyload/plugin/hoster/EnteruploadCom.py b/pyload/plugin/hoster/EnteruploadCom.py new file mode 100644 index 000000000..b3a736697 --- /dev/null +++ b/pyload/plugin/hoster/EnteruploadCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class EnteruploadCom(DeadHoster): +    __name__    = "EnteruploadCom" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?enterupload\.com/\w+' +    __config__  = [] + +    __description__ = """EnterUpload.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/hoster/EpicShareNet.py b/pyload/plugin/hoster/EpicShareNet.py new file mode 100644 index 000000000..0bab20afa --- /dev/null +++ b/pyload/plugin/hoster/EpicShareNet.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class EpicShareNet(DeadHoster): +    __name__    = "EpicShareNet" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'https?://(?:www\.)?epicshare\.net/\w{12}' +    __config__  = [] + +    __description__ = """EpicShare.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")] diff --git a/pyload/plugin/hoster/EuroshareEu.py b/pyload/plugin/hoster/EuroshareEu.py new file mode 100644 index 000000000..1cb805a90 --- /dev/null +++ b/pyload/plugin/hoster/EuroshareEu.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class EuroshareEu(SimpleHoster): +    __name__    = "EuroshareEu" +    __type__    = "hoster" +    __version__ = "0.28" + +    __pattern__ = r'http://(?:www\.)?euroshare\.(eu|sk|cz|hu|pl)/file/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Euroshare.eu hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    INFO_PATTERN    = r'<span style="float: left;"><strong>(?P<N>.+?)</strong> \((?P<S>.+?)\)</span>' +    OFFLINE_PATTERN = ur'<h2>S.bor sa nena.iel</h2>|PoÅŸadovaná stránka neexistuje!' + +    LINK_FREE_PATTERN = r'<a href="(/file/\d+/[^/]*/download/)"><div class="downloadButton"' + +    ERR_PARDL_PATTERN         = r'<h2>Prebieha s.ahovanie</h2>|<p>Naraz je z jednej IP adresy mo.n. s.ahova. iba jeden s.bor' +    ERR_NOT_LOGGED_IN_PATTERN = r'href="/customer-zone/login/"' + +    URL_REPLACEMENTS = [(r"(http://[^/]*\.)(sk|cz|hu|pl)/", r"\1eu/")] + + +    def handlePremium(self, pyfile): +        if self.ERR_NOT_LOGGED_IN_PATTERN in self.html: +            self.account.relogin(self.user) +            self.retry(reason=_("User not logged in")) + +        self.link = pyfile.url.rstrip('/') + "/download/" + +        check = self.checkDownload({"login": re.compile(self.ERR_NOT_LOGGED_IN_PATTERN), +                                    "json" : re.compile(r'\{"status":"error".*?"message":"(.*?)"')}) + +        if check == "login" or (check == "json" and self.lastCheck.group(1) == "Access token expired"): +            self.account.relogin(self.user) +            self.retry(reason=_("Access token expired")) + +        elif check == "json": +            self.fail(self.lastCheck.group(1)) + + +    def handleFree(self, pyfile): +        if re.search(self.ERR_PARDL_PATTERN, self.html): +            self.longWait(5 * 60, 12) + +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.error(_("LINK_FREE_PATTERN not found")) + +        self.link = "http://euroshare.eu%s" % m.group(1) + + +    def checkFile(self, rules={}): +        if self.checkDownload({"multi-dl": re.compile(self.ERR_PARDL_PATTERN)}) +            self.longWait(5 * 60, 12) + +        return super(EuroshareEu, self).checkFile(rules) diff --git a/pyload/plugin/hoster/ExashareCom.py b/pyload/plugin/hoster/ExashareCom.py new file mode 100644 index 000000000..504eef334 --- /dev/null +++ b/pyload/plugin/hoster/ExashareCom.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class ExashareCom(XFSHoster): +    __name__    = "ExashareCom" +    __type__    = "hoster" +    __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?exashare\.com/\w{12}' + +    __description__ = """Exashare.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    INFO_PATTERN      = r'>(?P<NAME>.+?)<small>\( (?P<S>[\d.,]+) (?P<U>[\w^_]+)' +    LINK_FREE_PATTERN = r'file: "(.+?)"' + + +    def setup(self): +        self.multiDL        = True +        self.chunkLimit     = 1 +        self.resumeDownload = self.premium + + +    def handleFree(self, pyfile): +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.error(_("Free download link not found")) +        else: +            self.link = m.group(1) diff --git a/pyload/plugin/hoster/ExtabitCom.py b/pyload/plugin/hoster/ExtabitCom.py new file mode 100644 index 000000000..8614f439d --- /dev/null +++ b/pyload/plugin/hoster/ExtabitCom.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.utils import json_loads + +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster, secondsToMidnight + + +class ExtabitCom(SimpleHoster): +    __name__    = "ExtabitCom" +    __type__    = "hoster" +    __version__ = "0.65" + +    __pattern__ = r'http://(?:www\.)?extabit\.com/(file|go|fid)/(?P<ID>\w+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Extabit.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN = r'<th>File:</th>\s*<td class="col-fileinfo">\s*<div title="(?P<N>.+?)">' +    SIZE_PATTERN = r'<th>Size:</th>\s*<td class="col-fileinfo">(?P<S>[^<]+)</td>' +    OFFLINE_PATTERN = r'>File not found<' +    TEMP_OFFLINE_PATTERN = r'>(File is temporary unavailable|No download mirror)<' + +    LINK_FREE_PATTERN = r'[\'"](http://guest\d+\.extabit\.com/\w+/.*?)[\'"]' + + +    def handleFree(self, pyfile): +        if r">Only premium users can download this file" in self.html: +            self.fail(_("Only premium users can download this file")) + +        m = re.search(r"Next free download from your ip will be available in <b>(\d+)\s*minutes", self.html) +        if m: +            self.wait(int(m.group(1)) * 60, True) +        elif "The daily downloads limit from your IP is exceeded" in self.html: +            self.logWarning(_("You have reached your daily downloads limit for today")) +            self.wait(secondsToMidnight(gmt=2), True) + +        self.logDebug("URL: " + self.req.http.lastEffectiveURL) +        m = re.match(self.__pattern__, self.req.http.lastEffectiveURL) +        fileID = m.group('ID') if m else self.info['pattern']['ID'] + +        m = re.search(r'recaptcha/api/challenge\?k=(\w+)', self.html) +        if m: +            recaptcha = ReCaptcha(self) +            captcha_key = m.group(1) + +            for _i in xrange(5): +                get_data = {"type": "recaptcha"} +                get_data['capture'], get_data['challenge'] = recaptcha.challenge(captcha_key) +                res = json_loads(self.load("http://extabit.com/file/%s/" % fileID, get=get_data)) +                if "ok" in res: +                    self.correctCaptcha() +                    break +                else: +                    self.invalidCaptcha() +            else: +                self.fail(_("Invalid captcha")) +        else: +            self.error(_("Captcha")) + +        if not "href" in res: +            self.error(_("Bad JSON response")) + +        self.html = self.load("http://extabit.com/file/%s%s" % (fileID, res['href'])) + +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.error(_("LINK_FREE_PATTERN not found")) + +        self.link = m.group(1) + diff --git a/pyload/plugin/hoster/FastixRu.py b/pyload/plugin/hoster/FastixRu.py new file mode 100644 index 000000000..679194969 --- /dev/null +++ b/pyload/plugin/hoster/FastixRu.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- + +import re + +from random import randrange +from urllib import unquote + +from pyload.utils import json_loads +from pyload.plugin.internal.MultiHoster import MultiHoster + + +class FastixRu(MultiHoster): +    __name__    = "FastixRu" +    __type__    = "hoster" +    __version__ = "0.11" + +    __pattern__ = r'http://(?:www\.)?fastix\.(ru|it)/file/\w{24}' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Fastix multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Massimo Rosamilia", "max@spiritix.eu")] + + +    def setup(self): +        self.chunkLimit = 3 + + +    def handlePremium(self, pyfile): +        api_key = self.account.getAccountData(self.user) +        api_key = api_key['api'] + +        self.html = self.load("http://fastix.ru/api_v2/", +                         get={'apikey': api_key, 'sub': "getdirectlink", 'link': pyfile.url}) + +        data = json_loads(self.html) + +        self.logDebug("Json data", data) + +        if "error\":true" in self.html: +            self.offline() +        else: +            self.link = data['downloadlink'] + + diff --git a/pyload/plugin/hoster/FastshareCz.py b/pyload/plugin/hoster/FastshareCz.py new file mode 100644 index 000000000..fba0e0479 --- /dev/null +++ b/pyload/plugin/hoster/FastshareCz.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class FastshareCz(SimpleHoster): +    __name__    = "FastshareCz" +    __type__    = "hoster" +    __version__ = "0.29" + +    __pattern__ = r'http://(?:www\.)?fastshare\.cz/\d+/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """FastShare.cz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + +    URL_REPLACEMENTS = [("#.*", "")] + +    COOKIES = [("fastshare.cz", "lang", "en")] + +    NAME_PATTERN    = r'<h3 class="section_title">(?P<N>.+?)<' +    SIZE_PATTERN    = r'>Size\s*:</strong> (?P<S>[\d.,]+) (?P<U>[\w^_]+)' +    OFFLINE_PATTERN = r'>(The file has been deleted|Requested page not found)' + +    LINK_FREE_PATTERN    = r'>Enter the code\s*:</em>\s*<span><img src="(.+?)"' +    LINK_PREMIUM_PATTERN = r'(http://\w+\.fastshare\.cz/download\.php\?id=\d+&)' + +    SLOT_ERROR   = "> 100% of FREE slots are full" +    CREDIT_ERROR = " credit for " + + +    def checkErrors(self): +        if self.SLOT_ERROR in self.html: +            errmsg = self.info['error'] = _("No free slots") +            self.retry(12, 60, errmsg) + +        if self.CREDIT_ERROR in self.html: +            errmsg = self.info['error'] = _("Not enough traffic left") +            self.logWarning(errmsg) +            self.resetAccount() + +        self.info.pop('error', None) + + +    def handleFree(self, pyfile): +        m = re.search(self.FREE_URL_PATTERN, self.html) +        if m: +            action, captcha_src = m.groups() +        else: +            self.error(_("FREE_URL_PATTERN not found")) + +        baseurl = "http://www.fastshare.cz" +        captcha = self.decryptCaptcha(urljoin(baseurl, captcha_src)) +        self.download(urljoin(baseurl, action), post={'code': captcha, 'btn.x': 77, 'btn.y': 18}) + + +    def checkFile(self, rules={}): +        check = self.checkDownload({ +            'paralell-dl'  : re.compile(r"<title>FastShare.cz</title>|<script>alert\('Pres FREE muzete stahovat jen jeden soubor najednou.'\)"), +            'wrong captcha': re.compile(r'Download for FREE'), +            'credit'       : re.compile(self.CREDIT_ERROR) +        }) + +        if check == "paralell-dl": +            self.retry(6, 10 * 60, _("Paralell download")) + +        elif check == "wrong captcha": +            self.retry(max_tries=5, reason=_("Wrong captcha")) + +        elif check == "credit": +            self.resetAccount() + +        return super(FastshareCz, self).checkFile(rules) diff --git a/pyload/plugin/hoster/FileApeCom.py b/pyload/plugin/hoster/FileApeCom.py new file mode 100644 index 000000000..4dc1442c5 --- /dev/null +++ b/pyload/plugin/hoster/FileApeCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class FileApeCom(DeadHoster): +    __name__    = "FileApeCom" +    __type__    = "hoster" +    __version__ = "0.12" + +    __pattern__ = r'http://(?:www\.)?fileape\.com/(index\.php\?act=download\&id=|dl/)\w+' +    __config__  = [] + +    __description__ = """FileApe.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("espes", "")] diff --git a/pyload/plugin/hoster/FileSharkPl.py b/pyload/plugin/hoster/FileSharkPl.py new file mode 100644 index 000000000..b0b0c558b --- /dev/null +++ b/pyload/plugin/hoster/FileSharkPl.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class FileSharkPl(SimpleHoster): +    __name__    = "FileSharkPl" +    __type__    = "hoster" +    __version__ = "0.10" + +    __pattern__ = r'http://(?:www\.)?fileshark\.pl/pobierz/\d+/\w+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """FileShark.pl hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("prOq", ""), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    NAME_PATTERN    = r'<h2 class="name-file">(?P<N>.+)</h2>' +    SIZE_PATTERN    = r'<p class="size-file">(.*?)<strong>(?P<S>\d+\.?\d*)\s(?P<U>\w+)</strong></p>' +    OFFLINE_PATTERN = r'(P|p)lik zosta. (usuni.ty|przeniesiony)' + +    LINK_FREE_PATTERN    = r'<a  rel="nofollow" href="(.*?)" class="btn-upload-free">' +    LINK_PREMIUM_PATTERN = r'<a rel="nofollow" href="(.*?)" class="btn-upload-premium">' + +    WAIT_PATTERN       = r'var timeToDownload = (\d+);' +    ERROR_PATTERN      = r'<p class="lead text-center alert alert-warning">(.*?)</p>' +    IP_ERROR_PATTERN   = r'Strona jest dost.pna wy..cznie dla u.ytkownik.w znajduj.cych si. na terenie Polski' +    SLOT_ERROR_PATTERN = r'Osi.gni.to maksymaln. liczb. .ci.ganych jednocze.nie plik.w\.' + +    CAPTCHA_PATTERN = r'<img src="data:image/jpeg;base64,(.*?)" title="captcha"' +    TOKEN_PATTERN   = r'name="form\[_token\]" value="(.*?)" />' + + +    def setup(self): +        self.resumeDownload = True + +        if self.premium: +            self.multiDL = True +            self.limitDL = 20 +        else: +            self.multiDL = False + + +    def checkErrors(self): +        # check if file is now available for download (-> file name can be found in html body) +        m = re.search(self.WAIT_PATTERN, self.html) +        if m: +            errmsg = self.info['error'] = _("Another download already run") +            self.retry(15, int(m.group(1)), errmsg) + +        m = re.search(self.ERROR_PATTERN, self.html) +        if m: +            alert = m.group(1) + +            if re.match(self.IP_ERROR_PATTERN, alert): +                self.fail(_("Only connections from Polish IP are allowed")) + +            elif re.match(self.SLOT_ERROR_PATTERN, alert): +                errmsg = self.info['error'] = _("No free download slots available") +                self.logWarning(errmsg) +                self.retry(10, 30 * 60, _("Still no free download slots available")) + +            else: +                self.info['error'] = alert +                self.retry(10, 10 * 60, _("Try again later")) + +        self.info.pop('error', None) + + +    def handleFree(self, pyfile): +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.error(_("Download url not found")) + +        link = urljoin("http://fileshark.pl", m.group(1)) + +        self.html = self.load(link) + +        m = re.search(self.WAIT_PATTERN, self.html) +        if m: +            seconds = int(m.group(1)) +            self.logDebug("Wait %s seconds" % seconds) +            self.wait(seconds) + +        action, inputs = self.parseHtmlForm('action=""') + +        m = re.search(self.TOKEN_PATTERN, self.html) +        if m is None: +            self.retry(reason=_("Captcha form not found")) + +        inputs['form[_token]'] = m.group(1) + +        m = re.search(self.CAPTCHA_PATTERN, self.html) +        if m is None: +            self.retry(reason=_("Captcha image not found")) + +        tmp_load  = self.load +        self.load = self._decode64  #: work-around: injects decode64 inside decryptCaptcha + +        inputs['form[captcha]'] = self.decryptCaptcha(m.group(1), imgtype='jpeg') +        inputs['form[start]'] = "" + +        self.load = tmp_load + +        self.download(link, post=inputs, disposition=True) + + +    def _decode64(self, data, *args, **kwargs): +        return data.decode('base64') diff --git a/pyload/plugin/hoster/FileStoreTo.py b/pyload/plugin/hoster/FileStoreTo.py new file mode 100644 index 000000000..10d24c1b0 --- /dev/null +++ b/pyload/plugin/hoster/FileStoreTo.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class FileStoreTo(SimpleHoster): +    __name__    = "FileStoreTo" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?filestore\.to/\?d=(?P<ID>\w+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """FileStore.to hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    INFO_PATTERN         = r'File: <span.*?>(?P<N>.+?)<.*>Size: (?P<S>[\d.,]+) (?P<U>[\w^_]+)' +    OFFLINE_PATTERN      = r'>Download-Datei wurde nicht gefunden<' +    TEMP_OFFLINE_PATTERN = r'>Der Download ist nicht bereit !<' + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True + + +    def handleFree(self, pyfile): +        self.wait(10) +        self.link = self.load("http://filestore.to/ajax/download.php", +                              get={'D': re.search(r'"D=(\w+)', self.html).group(1)}) diff --git a/pyload/plugin/hoster/FilebeerInfo.py b/pyload/plugin/hoster/FilebeerInfo.py new file mode 100644 index 000000000..34f3969c2 --- /dev/null +++ b/pyload/plugin/hoster/FilebeerInfo.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class FilebeerInfo(DeadHoster): +    __name__    = "FilebeerInfo" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?filebeer\.info/(?!\d*~f)(?P<ID>\w+)' +    __config__  = [] + +    __description__ = """Filebeer.info plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/hoster/FilecloudIo.py b/pyload/plugin/hoster/FilecloudIo.py new file mode 100644 index 000000000..601f72892 --- /dev/null +++ b/pyload/plugin/hoster/FilecloudIo.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.utils import json_loads +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class FilecloudIo(SimpleHoster): +    __name__    = "FilecloudIo" +    __type__    = "hoster" +    __version__ = "0.08" + +    __pattern__ = r'http://(?:www\.)?(?:filecloud\.io|ifile\.it|mihd\.net)/(?P<ID>\w+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Filecloud.io hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    LOGIN_ACCOUNT = True + +    NAME_PATTERN         = r'id="aliasSpan">(?P<N>.*?)  <' +    SIZE_PATTERN         = r'{var __ab1 = (?P<S>\d+);}' +    OFFLINE_PATTERN      = r'l10n\.(FILES__DOESNT_EXIST|REMOVED)' +    TEMP_OFFLINE_PATTERN = r'l10n\.FILES__WARNING' + +    UKEY_PATTERN = r'\'ukey\'\s*:\'(\w+)' +    AB1_PATTERN  = r'if\( __ab1 == \'(\w+)\' \)' + +    ERROR_MSG_PATTERN = r'var __error_msg\s*=\s*l10n\.(.*?);' + +    RECAPTCHA_PATTERN = r'var __recaptcha_public\s*=\s*\'(.+?)\';' + +    LINK_FREE_PATTERN = r'"(http://s\d+\.filecloud\.io/%s/\d+/.*?)"' + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True +        self.chunkLimit     = 1 + + +    def handleFree(self, pyfile): +        data = {"ukey": self.info['pattern']['ID']} + +        m = re.search(self.AB1_PATTERN, self.html) +        if m is None: +            self.error(_("__AB1")) +        data['__ab1'] = m.group(1) + +        recaptcha = ReCaptcha(self) + +        m = re.search(self.RECAPTCHA_PATTERN, self.html) +        captcha_key = m.group(1) if m else recaptcha.detect_key() + +        if captcha_key is None: +            self.error(_("ReCaptcha key not found")) + +        response, challenge = recaptcha.challenge(captcha_key) +        self.account.form_data = {"recaptcha_challenge_field": challenge, +                                  "recaptcha_response_field" : response} +        self.account.relogin(self.user) +        self.retry(2) + +        json_url = "http://filecloud.io/download-request.json" +        res = self.load(json_url, post=data) +        self.logDebug(res) +        res = json_loads(res) + +        if "error" in res and res['error']: +            self.fail(res) + +        self.logDebug(res) +        if res['captcha']: +            data['ctype'] = "recaptcha" + +            for _i in xrange(5): +                data['recaptcha_response'], data['recaptcha_challenge'] = recaptcha.challenge(captcha_key) + +                json_url = "http://filecloud.io/download-request.json" +                res = self.load(json_url, post=data) +                self.logDebug(res) +                res = json_loads(res) + +                if "retry" in res and res['retry']: +                    self.invalidCaptcha() +                else: +                    self.correctCaptcha() +                    break +            else: +                self.fail(_("Incorrect captcha")) + +        if res['dl']: +            self.html = self.load('http://filecloud.io/download.html') + +            m = re.search(self.LINK_FREE_PATTERN % self.info['pattern']['ID'], self.html) +            if m is None: +                self.error(_("LINK_FREE_PATTERN not found")) + +            if "size" in self.info and self.info['size']: +                self.check_data = {"size": int(self.info['size'])} + +            self.link = m.group(1) +        else: +            self.fail(_("Unexpected server response")) + + +    def handlePremium(self, pyfile): +        akey = self.account.getAccountData(self.user)['akey'] +        ukey = self.info['pattern']['ID'] +        self.logDebug("Akey: %s | Ukey: %s" % (akey, ukey)) +        rep = self.load("http://api.filecloud.io/api-fetch_download_url.api", +                        post={"akey": akey, "ukey": ukey}) +        self.logDebug("FetchDownloadUrl: " + rep) +        rep = json_loads(rep) +        if rep['status'] == 'ok': +            self.link = rep['download_url'] +        else: +            self.fail(rep['message']) diff --git a/pyload/plugin/hoster/FilefactoryCom.py b/pyload/plugin/hoster/FilefactoryCom.py new file mode 100644 index 000000000..c6b857307 --- /dev/null +++ b/pyload/plugin/hoster/FilefactoryCom.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from pyload.network.RequestFactory import getURL +from pyload.plugin.internal.SimpleHoster import SimpleHoster, parseFileInfo + + +def getInfo(urls): +    for url in urls: +        h = getURL(url, just_header=True) +        m = re.search(r'Location: (.+)\r\n', h) +        if m and not re.match(m.group(1), FilefactoryCom.__pattern__):  #: It's a direct link! Skipping +            yield (url, 0, 3, url) +        else:  #: It's a standard html page +            yield parseFileInfo(FilefactoryCom, url, getURL(url)) + + +class FilefactoryCom(SimpleHoster): +    __name__    = "FilefactoryCom" +    __type__    = "hoster" +    __version__ = "0.54" + +    __pattern__ = r'https?://(?:www\.)?filefactory\.com/(file|trafficshare/\w+)/\w+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Filefactory.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    INFO_PATTERN = r'<div id="file_name"[^>]*>\s*<h2>(?P<N>[^<]+)</h2>\s*<div id="file_info">\s*(?P<S>[\d.,]+) (?P<U>[\w^_]+) uploaded' +    OFFLINE_PATTERN = r'<h2>File Removed</h2>|This file is no longer available' + +    LINK_FREE_PATTERN = LINK_PREMIUM_PATTERN = r'"([^"]+filefactory\.com/get.+?)"' + +    WAIT_PATTERN = r'<div id="countdown_clock" data-delay="(\d+)">' +    PREMIUM_ONLY_PATTERN = r'>Premium Account Required' + +    COOKIES = [("filefactory.com", "locale", "en_US.utf8")] + + +    def handleFree(self, pyfile): +        if "Currently only Premium Members can download files larger than" in self.html: +            self.fail(_("File too large for free download")) +        elif "All free download slots on this server are currently in use" in self.html: +            self.retry(50, 15 * 60, _("All free slots are busy")) + +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.error(_("Free download link not found")) + +        self.link = m.group(1) + +        m = re.search(self.WAIT_PATTERN, self.html) +        if m: +            self.wait(m.group(1)) + + +    def checkFile(self, rules={}): +        check = self.checkDownload({'multiple': "You are currently downloading too many files at once.", +                                    'error'   : '<div id="errorMessage">'}) + +        if check == "multiple": +            self.logDebug("Parallel downloads detected; waiting 15 minutes") +            self.retry(wait_time=15 * 60, reason=_("Parallel downloads")) + +        elif check == "error": +            self.error(_("Unknown error")) + +        return super(FilefactoryCom, self).checkFile(rules) + + +    def handlePremium(self, pyfile): +        self.link = self.directLink(self.load(pyfile.url, just_header=True)) + +        if not self.link: +            html = self.load(pyfile.url) +            m = re.search(self.LINK_PREMIUM_PATTERN, html) +            if m: +                self.link = m.group(1) +            else: +                self.error(_("Premium download link not found")) diff --git a/pyload/plugin/hoster/FilejungleCom.py b/pyload/plugin/hoster/FilejungleCom.py new file mode 100644 index 000000000..025b98aed --- /dev/null +++ b/pyload/plugin/hoster/FilejungleCom.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.hoster.FileserveCom import FileserveCom, checkFile +from pyload.plugin.Plugin import chunks + + +class FilejungleCom(FileserveCom): +    __name__    = "FilejungleCom" +    __type__    = "hoster" +    __version__ = "0.51" + +    __pattern__ = r'http://(?:www\.)?filejungle\.com/f/(?P<ID>[^/]+)' + +    __description__ = """Filejungle.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    URLS = ["http://www.filejungle.com/f/", "http://www.filejungle.com/check_links.php", +            "http://www.filejungle.com/checkReCaptcha.php"] +    LINKCHECK_TR = r'<li>\s*(<div class="col1">.*?)</li>' +    LINKCHECK_TD = r'<div class="(?:col )?col\d">(?:<.*?>| )*([^<]*)' + +    LONG_WAIT_PATTERN = r'<h1>Please wait for (\d+) (\w+)\s*to download the next file\.</h1>' + + +def getInfo(urls): +    for chunk in chunks(urls, 100): +        yield checkFile(FilejungleCom, chunk) diff --git a/pyload/plugin/hoster/FileomCom.py b/pyload/plugin/hoster/FileomCom.py new file mode 100644 index 000000000..cdfc15dd9 --- /dev/null +++ b/pyload/plugin/hoster/FileomCom.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://fileom.com/gycaytyzdw3g/random.bin.html + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class FileomCom(XFSHoster): +    __name__    = "FileomCom" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'https?://(?:www\.)?fileom\.com/\w{12}' + +    __description__ = """Fileom.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    NAME_PATTERN = r'Filename: <span>(?P<N>.+?)<' +    SIZE_PATTERN = r'File Size: <span class="size">(?P<S>[\d.,]+) (?P<U>[\w^_]+)' + +    LINK_PATTERN = r'var url2 = \'(.+?)\';' + + +    def setup(self): +        self.multiDL = True +        self.chunkLimit = 1 +        self.resumeDownload = self.premium diff --git a/pyload/plugin/hoster/FilepostCom.py b/pyload/plugin/hoster/FilepostCom.py new file mode 100644 index 000000000..82ba5f16f --- /dev/null +++ b/pyload/plugin/hoster/FilepostCom.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.utils import json_loads +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class FilepostCom(SimpleHoster): +    __name__    = "FilepostCom" +    __type__    = "hoster" +    __version__ = "0.33" + +    __pattern__ = r'https?://(?:www\.)?(?:filepost\.com/files|fp\.io)/(?P<ID>[^/]+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Filepost.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    INFO_PATTERN = r'<input type="text" id="url" value=\'<a href.*?>(?P<N>[^>]+?) - (?P<S>[\d.,]+) (?P<U>[\w^_]+)</a>\' class="inp_text"/>' +    OFFLINE_PATTERN = r'class="error_msg_title"> Invalid or Deleted File. </div>|<div class="file_info file_info_deleted">' + +    PREMIUM_ONLY_PATTERN = r'members only. Please upgrade to premium|a premium membership is required to download this file' +    RECAPTCHA_PATTERN = r'Captcha.init\({\s*key:\s*\'(.+?)\'' +    FLP_TOKEN_PATTERN = r'set_store_options\({token: \'(.+?)\'' + + +    def handleFree(self, pyfile): +        m = re.search(self.FLP_TOKEN_PATTERN, self.html) +        if m is None: +            self.error(_("Token")) +        flp_token = m.group(1) + +        m = re.search(self.RECAPTCHA_PATTERN, self.html) +        if m is None: +            self.error(_("Captcha key")) +        captcha_key = m.group(1) + +        # Get wait time +        get_dict = {'SID': self.req.cj.getCookie('SID'), 'JsHttpRequest': str(int(time.time() * 10000)) + '-xml'} +        post_dict = {'action': 'set_download', 'token': flp_token, 'code': self.info['pattern']['ID']} +        wait_time = int(self.getJsonResponse(get_dict, post_dict, 'wait_time')) + +        if wait_time > 0: +            self.wait(wait_time) + +        post_dict = {"token": flp_token, "code": self.info['pattern']['ID'], "file_pass": ''} + +        if 'var is_pass_exists = true;' in self.html: +            # Solve password +            password = self.getPassword() + +            if password: +                self.logInfo(_("Password protected link, trying ") + file_pass) + +                get_dict['JsHttpRequest'] = str(int(time.time() * 10000)) + '-xml' +                post_dict['file_pass'] = file_pass + +                self.link = self.getJsonResponse(get_dict, post_dict, 'link') + +                if not self.link: +                    self.fail(_("Incorrect password")) +            else: +                self.fail(_("No password found")) + +        else: +            # Solve recaptcha +            recaptcha = ReCaptcha(self) + +            for i in xrange(5): +                get_dict['JsHttpRequest'] = str(int(time.time() * 10000)) + '-xml' +                if i: +                    post_dict['recaptcha_response_field'], post_dict['recaptcha_challenge_field'] = recaptcha.challenge( +                        captcha_key) +                    self.logDebug(u"RECAPTCHA: %s : %s : %s" % ( +                        captcha_key, post_dict['recaptcha_challenge_field'], post_dict['recaptcha_response_field'])) + +                self.link = self.getJsonResponse(get_dict, post_dict, 'link') + +            else: +                self.fail(_("Invalid captcha")) + + +    def getJsonResponse(self, get_dict, post_dict, field): +        res = json_loads(self.load('https://filepost.com/files/get/', get=get_dict, post=post_dict)) + +        self.logDebug(res) + +        if not 'js' in res: +            self.error(_("JSON %s 1") % field) + +        # i changed js_answer to res['js'] since js_answer is nowhere set. +        # i don't know the JSON-HTTP specs in detail, but the previous author +        # accessed res['js']['error'] as well as js_answer['error']. +        # see the two lines commented out with  "# ~?". +        if 'error' in res['js']: + +            if res['js']['error'] == 'download_delay': +                self.retry(wait_time=res['js']['params']['next_download']) +                # ~? self.retry(wait_time=js_answer['params']['next_download']) + +            elif 'Wrong file password' in res['js']['error'] \ +                 or 'You entered a wrong CAPTCHA code' in res['js']['error'] \ +                 or 'CAPTCHA Code nicht korrekt' in res['js']['error']: +                return None + +            elif 'CAPTCHA' in res['js']['error']: +                self.logDebug("Error response is unknown, but mentions CAPTCHA") +                return None + +            else: +                self.fail(res['js']['error']) + +        if not 'answer' in res['js'] or not field in res['js']['answer']: +            self.error(_("JSON %s 2") % field) + +        return res['js']['answer'][field] diff --git a/pyload/plugin/hoster/FilepupNet.py b/pyload/plugin/hoster/FilepupNet.py new file mode 100644 index 000000000..80f4fc1c8 --- /dev/null +++ b/pyload/plugin/hoster/FilepupNet.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://www.filepup.net/files/k5w4ZVoF1410184283.html +# http://www.filepup.net/files/R4GBq9XH1410186553.html + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class FilepupNet(SimpleHoster): +    __name__    = "FilepupNet" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?filepup\.net/files/\w+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Filepup.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    NAME_PATTERN = r'>(?P<N>.+?)</h1>' +    SIZE_PATTERN = r'class="fa fa-archive"></i> \((?P<S>[\d.,]+) (?P<U>[\w^_]+)' + +    OFFLINE_PATTERN = r'>This file has been deleted' + +    LINK_FREE_PATTERN = r'(http://www\.filepup\.net/get/.+?)\'' + + +    def setup(self): +        self.multiDL = False +        self.chunkLimit = 1 + + +    def handleFree(self, pyfile): +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.error(_("Download link not found")) + +        dl_link = m.group(1) +        self.download(dl_link, post={'task': "download"}) diff --git a/pyload/plugin/hoster/FilerNet.py b/pyload/plugin/hoster/FilerNet.py new file mode 100644 index 000000000..ab9d71e3f --- /dev/null +++ b/pyload/plugin/hoster/FilerNet.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://filer.net/get/ivgf5ztw53et3ogd +# http://filer.net/get/hgo14gzcng3scbvv + +import pycurl +import re + +from urlparse import urljoin + +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class FilerNet(SimpleHoster): +    __name__    = "FilerNet" +    __type__    = "hoster" +    __version__ = "0.19" + +    __pattern__ = r'https?://(?:www\.)?filer\.net/get/\w+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Filer.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it"), +                     ("Walter Purcaro", "vuolter@gmail.com")] + +    INFO_PATTERN    = r'<h1 class="page-header">Free Download (?P<N>\S+) <small>(?P<S>[\w.]+) (?P<U>[\w^_]+)</small></h1>' +    OFFLINE_PATTERN = r'Nicht gefunden' + +    WAIT_PATTERN = r'musst du <span id="time">(\d+)' + +    LINK_FREE_PATTERN = LINK_PREMIUM_PATTERN = r'href="([^"]+)">Get download</a>' + +    def handleFree(self, pyfile): +        inputs = self.parseHtmlForm(input_names={'token': re.compile(r'.+')})[1] +        if 'token' not in inputs: +            self.error(_("Unable to detect token")) + +        self.html = self.load(pyfile.url, post={'token': inputs['token']}, decode=True) + +        inputs = self.parseHtmlForm(input_names={'hash': re.compile(r'.+')})[1] +        if 'hash' not in inputs: +            self.error(_("Unable to detect hash")) + +        recaptcha           = ReCaptcha(self) +        response, challenge = recaptcha.challenge() + +        #@NOTE: Work-around for v0.4.9 just_header issue +        #@TODO: Check for v0.4.10 +        self.req.http.c.setopt(pycurl.FOLLOWLOCATION, 0) +        self.load(pyfile.url, post={'recaptcha_challenge_field': challenge, +                                    'recaptcha_response_field': response, +                                    'hash': inputs['hash']}) +        self.req.http.c.setopt(pycurl.FOLLOWLOCATION, 1) + +        if 'location' in self.req.http.header.lower(): +            self.link = re.search(r'location: (\S+)', self.req.http.header, re.I).group(1) +            self.correctCaptcha() +        else: +            self.invalidCaptcha() diff --git a/pyload/plugin/hoster/FilerioCom.py b/pyload/plugin/hoster/FilerioCom.py new file mode 100644 index 000000000..9290bee84 --- /dev/null +++ b/pyload/plugin/hoster/FilerioCom.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class FilerioCom(XFSHoster): +    __name__    = "FilerioCom" +    __type__    = "hoster" +    __version__ = "0.07" + +    __pattern__ = r'http://(?:www\.)?(filerio\.(in|com)|filekeen\.com)/\w{12}' + +    __description__ = """FileRio.in hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    URL_REPLACEMENTS = [(r'filekeen\.com', "filerio.in")] + +    OFFLINE_PATTERN = r'>"File Not Found|File has been removed' diff --git a/pyload/plugin/hoster/FilesMailRu.py b/pyload/plugin/hoster/FilesMailRu.py new file mode 100644 index 000000000..09da46b01 --- /dev/null +++ b/pyload/plugin/hoster/FilesMailRu.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.network.RequestFactory import getURL +from pyload.plugin.Hoster import Hoster +from pyload.plugin.Plugin import chunks + + +def getInfo(urls): +    result = [] +    for chunk in chunks(urls, 10): +        for url in chunk: +            html = getURL(url) +            if r'<div class="errorMessage mb10">' in html: +                result.append((url, 0, 1, url)) +            elif r'Page cannot be displayed' in html: +                result.append((url, 0, 1, url)) +            else: +                try: +                    url_pattern = '<a href="(.+?)" onclick="return Act\(this\, \'dlink\'\, event\)">(.+?)</a>' +                    file_name = re.search(url_pattern, html).group(0).split(', event)">')[1].split('</a>')[0] +                    result.append((file_name, 0, 2, url)) +                except Exception: +                    pass + +        # status 1=OFFLINE, 2=OK, 3=UNKNOWN +        # result.append((#name,#size,#status,#url)) +        yield result + + +class FilesMailRu(Hoster): +    __name__    = "FilesMailRu" +    __type__    = "hoster" +    __version__ = "0.32" + +    __pattern__ = r'http://(?:www\.)?files\.mail\.ru/.+' + +    __description__ = """Files.mail.ru hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("oZiRiz", "ich@oziriz.de")] + + +    def setup(self): +        self.multiDL = bool(self.account) + + +    def process(self, pyfile): +        self.html = self.load(pyfile.url) +        self.url_pattern = '<a href="(.+?)" onclick="return Act\(this\, \'dlink\'\, event\)">(.+?)</a>' + +        #marks the file as "offline" when the pattern was found on the html-page''' +        if r'<div class="errorMessage mb10">' in self.html: +            self.offline() + +        elif r'Page cannot be displayed' in self.html: +            self.offline() + +        #the filename that will be showed in the list (e.g. test.part1.rar)''' +        pyfile.name = self.getFileName() + +        #prepare and download''' +        if not self.account: +            self.prepare() +            self.download(self.getFileUrl()) +            self.myPostProcess() +        else: +            self.download(self.getFileUrl()) +            self.myPostProcess() + + +    def prepare(self): +        """You have to wait some seconds. Otherwise you will get a 40Byte HTML Page instead of the file you expected""" +        self.setWait(10) +        self.wait() +        return True + + +    def getFileUrl(self): +        """gives you the URL to the file. Extracted from the Files.mail.ru HTML-page stored in self.html""" +        return re.search(self.url_pattern, self.html).group(0).split('<a href="')[1].split('" onclick="return Act')[0] + + +    def getFileName(self): +        """gives you the Name for each file. Also extracted from the HTML-Page""" +        return re.search(self.url_pattern, self.html).group(0).split(', event)">')[1].split('</a>')[0] + + +    def myPostProcess(self): +        # searches the file for HTMl-Code. Sometimes the Redirect +        # doesn't work (maybe a curl Problem) and you get only a small +        # HTML file and the Download is marked as "finished" +        # then the download will be restarted. It's only bad for these +        # who want download a HTML-File (it's one in a million ;-) ) +        # +        # The maximum UploadSize allowed on files.mail.ru at the moment is 100MB +        # so i set it to check every download because sometimes there are downloads +        # that contain the HTML-Text and 60MB ZEROs after that in a xyzfile.part1.rar file +        # (Loading 100MB in to ram is not an option) +        check = self.checkDownload({"html": "<meta name="}, read_size=50000) +        if check == "html": +            self.logInfo(_( +                "There was HTML Code in the Downloaded File (%s)...redirect error? The Download will be restarted." % +                self.pyfile.name)) +            self.retry() diff --git a/pyload/plugin/hoster/FileserveCom.py b/pyload/plugin/hoster/FileserveCom.py new file mode 100644 index 000000000..34ab3d790 --- /dev/null +++ b/pyload/plugin/hoster/FileserveCom.py @@ -0,0 +1,216 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.utils import json_loads +from pyload.network.RequestFactory import getURL +from pyload.plugin.Hoster import Hoster +from pyload.plugin.Plugin import chunks +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import secondsToMidnight +from pyload.utils import parseFileSize + + +def checkFile(plugin, urls): +    html = getURL(plugin.URLS[1], post={"urls": "\n".join(urls)}, decode=True) + +    file_info = [] +    for li in re.finditer(plugin.LINKCHECK_TR, html, re.S): +        try: +            cols = re.findall(plugin.LINKCHECK_TD, li.group(1)) +            if cols: +                file_info.append(( +                    cols[1] if cols[1] != '--' else cols[0], +                    parseFileSize(cols[2]) if cols[2] != '--' else 0, +                    2 if cols[3].startswith('Available') else 1, +                    cols[0])) +        except Exception, e: +            continue + +    return file_info + + +class FileserveCom(Hoster): +    __name__    = "FileserveCom" +    __type__    = "hoster" +    __version__ = "0.54" + +    __pattern__ = r'http://(?:www\.)?fileserve\.com/file/(?P<ID>[^/]+)' + +    __description__ = """Fileserve.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("mkaay", "mkaay@mkaay.de"), +                       ("Paul King", ""), +                       ("zoidberg", "zoidberg@mujmail.cz")] + + +    URLS = ["http://www.fileserve.com/file/", "http://www.fileserve.com/link-checker.php", +            "http://www.fileserve.com/checkReCaptcha.php"] +    LINKCHECK_TR = r'<tr>\s*(<td>http://www\.fileserve\.com/file/.*?)</tr>' +    LINKCHECK_TD = r'<td>(?:<.*?>| )*([^<]*)' + +    CAPTCHA_KEY_PATTERN = r'var reCAPTCHA_publickey=\'(.+?)\'' +    LONG_WAIT_PATTERN = r'<li class="title">You need to wait (\d+) (\w+) to start another download\.</li>' +    LINK_EXPIRED_PATTERN = r'Your download link has expired' +    DAILY_LIMIT_PATTERN = r'Your daily download limit has been reached' +    NOT_LOGGED_IN_PATTERN = r'<form (name="loginDialogBoxForm"|id="login_form")|<li><a href="/login\.php">Login</a></li>' + + +    def setup(self): +        self.resumeDownload = self.multiDL = self.premium +        self.file_id = re.match(self.__pattern__, self.pyfile.url).group('ID') +        self.url     = "%s%s" % (self.URLS[0], self.file_id) + +        self.logDebug("File ID: %s URL: %s" % (self.file_id, self.url)) + + +    def process(self, pyfile): +        pyfile.name, pyfile.size, status, self.url = checkFile(self, [self.url])[0] +        if status != 2: +            self.offline() +        self.logDebug("File Name: %s Size: %d" % (pyfile.name, pyfile.size)) + +        if self.premium: +            self.handlePremium(pyfile) +        else: +            self.handleFree(pyfile) + + +    def handleFree(self, pyfile): +        self.html = self.load(self.url) +        action = self.load(self.url, post={"checkDownload": "check"}, decode=True) +        action = json_loads(action) +        self.logDebug(action) + +        if "fail" in action: +            if action['fail'] == "timeLimit": +                self.html = self.load(self.url, post={"checkDownload": "showError", "errorType": "timeLimit"}, +                                      decode=True) + +                self.doLongWait(re.search(self.LONG_WAIT_PATTERN, self.html)) + +            elif action['fail'] == "parallelDownload": +                self.logWarning(_("Parallel download error, now waiting 60s")) +                self.retry(wait_time=60, reason=_("parallelDownload")) + +            else: +                self.fail(_("Download check returned: %s") % action['fail']) + +        elif "success" in action: +            if action['success'] == "showCaptcha": +                self.doCaptcha() +                self.doTimmer() +            elif action['success'] == "showTimmer": +                self.doTimmer() + +        else: +            self.error(_("Unknown server response")) + +        # show download link +        res = self.load(self.url, post={"downloadLink": "show"}, decode=True) +        self.logDebug("Show downloadLink response: %s" % res) +        if "fail" in res: +            self.error(_("Couldn't retrieve download url")) + +        # this may either download our file or forward us to an error page +        self.download(self.url, post={"download": "normal"}) +        self.logDebug(self.req.http.lastEffectiveURL) + +        check = self.checkDownload({"expired": self.LINK_EXPIRED_PATTERN, +                                    "wait"   : re.compile(self.LONG_WAIT_PATTERN), +                                    "limit"  : self.DAILY_LIMIT_PATTERN}) + +        if check == "expired": +            self.logDebug("Download link was expired") +            self.retry() + +        elif check == "wait": +            self.doLongWait(self.lastCheck) + +        elif check == "limit": +            self.logWarning(_("Download limited reached for today")) +            self.setWait(secondsToMidnight(gmt=2), True) +            self.wait() +            self.retry() + +        self.thread.m.reconnecting.wait(3)  # Ease issue with later downloads appearing to be in parallel + + +    def doTimmer(self): +        res = self.load(self.url, post={"downloadLink": "wait"}, decode=True) +        self.logDebug("Wait response: %s" % res[:80]) + +        if "fail" in res: +            self.fail(_("Failed getting wait time")) + +        if self.__name__ == "FilejungleCom": +            m = re.search(r'"waitTime":(\d+)', res) +            if m is None: +                self.fail(_("Cannot get wait time")) +            wait_time = int(m.group(1)) +        else: +            wait_time = int(res) + 3 + +        self.setWait(wait_time) +        self.wait() + + +    def doCaptcha(self): +        captcha_key = re.search(self.CAPTCHA_KEY_PATTERN, self.html).group(1) +        recaptcha = ReCaptcha(self) + +        for _i in xrange(5): +            response, challenge = recaptcha.challenge(captcha_key) +            res = json_loads(self.load(self.URLS[2], +                                       post={'recaptcha_challenge_field'  : challenge, +                                             'recaptcha_response_field'   : response, +                                             'recaptcha_shortencode_field': self.file_id})) +            if not res['success']: +                self.invalidCaptcha() +            else: +                self.correctCaptcha() +                break +        else: +            self.fail(_("Invalid captcha")) + + +    def doLongWait(self, m): +        wait_time = (int(m.group(1)) * {'seconds': 1, 'minutes': 60, 'hours': 3600}[m.group(2)]) if m else 12 * 60 +        self.setWait(wait_time, True) +        self.wait() +        self.retry() + + +    def handlePremium(self, pyfile): +        premium_url = None +        if self.__name__ == "FileserveCom": +            #try api download +            res = self.load("http://app.fileserve.com/api/download/premium/", +                            post={"username": self.user, +                                  "password": self.account.getAccountData(self.user)['password'], +                                  "shorten": self.file_id}, +                            decode=True) +            if res: +                res = json_loads(res) +                if res['error_code'] == "302": +                    premium_url = res['next'] +                elif res['error_code'] in ["305", "500"]: +                    self.tempOffline() +                elif res['error_code'] in ["403", "605"]: +                    self.resetAccount() +                elif res['error_code'] in ["606", "607", "608"]: +                    self.offline() +                else: +                    self.logError(res['error_code'], res['error_message']) + +        self.download(premium_url or pyfile.url) + +        if not premium_url and self.checkDownload({"login": re.compile(self.NOT_LOGGED_IN_PATTERN)}): +            self.account.relogin(self.user) +            self.retry(reason=_("Not logged in")) + + +def getInfo(urls): +    for chunk in chunks(urls, 100): +        yield checkFile(FileserveCom, chunk) diff --git a/pyload/plugin/hoster/FileshareInUa.py b/pyload/plugin/hoster/FileshareInUa.py new file mode 100644 index 000000000..afda590c0 --- /dev/null +++ b/pyload/plugin/hoster/FileshareInUa.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class FileshareInUa(DeadHoster): +    __name__    = "FileshareInUa" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'https?://(?:www\.)?fileshare\.in\.ua/\w{7}' +    __config__  = [] + +    __description__ = """Fileshare.in.ua hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("fwannmacher", "felipe@warhammerproject.com")] diff --git a/pyload/plugin/hoster/FilesonicCom.py b/pyload/plugin/hoster/FilesonicCom.py new file mode 100644 index 000000000..4ba0f3e50 --- /dev/null +++ b/pyload/plugin/hoster/FilesonicCom.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class FilesonicCom(DeadHoster): +    __name__    = "FilesonicCom" +    __type__    = "hoster" +    __version__ = "0.35" + +    __pattern__ = r'http://(?:www\.)?filesonic\.com/file/\w+' +    __config__  = [] + +    __description__ = """Filesonic.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("paulking", "")] diff --git a/pyload/plugin/hoster/FilezyNet.py b/pyload/plugin/hoster/FilezyNet.py new file mode 100644 index 000000000..0ed326a22 --- /dev/null +++ b/pyload/plugin/hoster/FilezyNet.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class FilezyNet(DeadHoster): +    __name__    = "FilezyNet" +    __type__    = "hoster" +    __version__ = "0.20" + +    __pattern__ = r'http://(?:www\.)?filezy\.net/\w{12}' +    __config__  = [] + +    __description__ = """Filezy.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [] diff --git a/pyload/plugin/hoster/FiredriveCom.py b/pyload/plugin/hoster/FiredriveCom.py new file mode 100644 index 000000000..5879c4848 --- /dev/null +++ b/pyload/plugin/hoster/FiredriveCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class FiredriveCom(DeadHoster): +    __name__    = "FiredriveCom" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'https?://(?:www\.)?(firedrive|putlocker)\.com/(mobile/)?(file|embed)/(?P<ID>\w+)' +    __config__  = [] + +    __description__ = """Firedrive.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] diff --git a/pyload/plugin/hoster/FlyFilesNet.py b/pyload/plugin/hoster/FlyFilesNet.py new file mode 100644 index 000000000..a122199f8 --- /dev/null +++ b/pyload/plugin/hoster/FlyFilesNet.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +import re + +from urllib import unquote + +from pyload.network.RequestFactory import getURL +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class FlyFilesNet(SimpleHoster): +    __name__    = "FlyFilesNet" +    __type__    = "hoster" +    __version__ = "0.10" + +    __pattern__ = r'http://(?:www\.)?flyfiles\.net/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """FlyFiles.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [] + +    SESSION_PATTERN = r'flyfiles\.net/(.*)/.*' +    NAME_PATTERN = r'flyfiles\.net/.*/(.*)' + + +    def process(self, pyfile): +        name = re.search(self.NAME_PATTERN, pyfile.url).group(1) +        pyfile.name = unquote_plus(name) + +        session = re.search(self.SESSION_PATTERN, pyfile.url).group(1) + +        url = "http://flyfiles.net" + +        # get download URL +        parsed_url = getURL(url, post={"getDownLink": session}) +        self.logDebug("Parsed URL: %s" % parsed_url) + +        if parsed_url == '#downlink|' or parsed_url == "#downlink|#": +            self.logWarning(_("Could not get the download URL. Please wait 10 minutes")) +            self.wait(10 * 60, True) +            self.retry() + +        self.link = parsed_url.replace('#downlink|', '') diff --git a/pyload/plugin/hoster/FourSharedCom.py b/pyload/plugin/hoster/FourSharedCom.py new file mode 100644 index 000000000..8c15c5954 --- /dev/null +++ b/pyload/plugin/hoster/FourSharedCom.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class FourSharedCom(SimpleHoster): +    __name__    = "FourSharedCom" +    __type__    = "hoster" +    __version__ = "0.31" + +    __pattern__ = r'https?://(?:www\.)?4shared(\-china)?\.com/(account/)?(download|get|file|document|photo|video|audio|mp3|office|rar|zip|archive|music)/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """4Shared.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN = r'<meta name="title" content="(?P<N>.+?)"' +    SIZE_PATTERN = r'<span title="Size: (?P<S>[\d.,]+) (?P<U>[\w^_]+)">' +    OFFLINE_PATTERN = r'The file link that you requested is not valid\.|This file was deleted.' + +    NAME_REPLACEMENTS = [(r"&#(\d+).", lambda m: unichr(int(m.group(1))))] +    SIZE_REPLACEMENTS = [(",", "")] + +    DIRECT_LINK   = False +    LOGIN_ACCOUNT = True + +    LINK_FREE_PATTERN = r'name="d3link" value="(.*?)"' +    LINK_BTN_PATTERN  = r'id="btnLink" href="(.*?)"' + +    ID_PATTERN = r'name="d3fid" value="(.*?)"' + + +    def handleFree(self, pyfile): +        m = re.search(self.LINK_BTN_PATTERN, self.html) +        if m: +            link = m.group(1) +        else: +            link = re.sub(r'/(download|get|file|document|photo|video|audio)/', r'/get/', pyfile.url) + +        self.html = self.load(link) + +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.error(_("Download link")) + +        self.link = m.group(1) + +        try: +            m = re.search(self.ID_PATTERN, self.html) +            res = self.load('http://www.4shared.com/web/d2/getFreeDownloadLimitInfo?fileId=%s' % m.group(1)) +            self.logDebug(res) +        except Exception: +            pass + +        self.wait(20) diff --git a/pyload/plugin/hoster/FreakshareCom.py b/pyload/plugin/hoster/FreakshareCom.py new file mode 100644 index 000000000..078293120 --- /dev/null +++ b/pyload/plugin/hoster/FreakshareCom.py @@ -0,0 +1,182 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Hoster import Hoster +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import secondsToMidnight + + +class FreakshareCom(Hoster): +    __name__    = "FreakshareCom" +    __type__    = "hoster" +    __version__ = "0.40" + +    __pattern__ = r'http://(?:www\.)?freakshare\.(net|com)/files/\S*?/' + +    __description__ = """Freakshare.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("sitacuisses", "sitacuisses@yahoo.de"), +                       ("spoob", "spoob@pyload.org"), +                       ("mkaay", "mkaay@mkaay.de"), +                       ("Toilal", "toilal.dev@gmail.com")] + + +    def setup(self): +        self.multiDL = False +        self.req_opts = [] + + +    def process(self, pyfile): +        self.pyfile = pyfile + +        pyfile.url = pyfile.url.replace("freakshare.net/", "freakshare.com/") + +        if self.account: +            self.html = self.load(pyfile.url, cookies=False) +            pyfile.name = self.get_file_name() +            self.download(pyfile.url) + +        else: +            self.prepare() +            self.get_file_url() + +            self.download(pyfile.url, post=self.req_opts) + +            check = self.checkDownload({"bad"           : "bad try", +                                        "paralell"      : "> Sorry, you cant download more then 1 files at time. <", +                                        "empty"         : "Warning: Unknown: Filename cannot be empty", +                                        "wrong_captcha" : "Wrong Captcha!", +                                        "downloadserver": "No Downloadserver. Please try again later!"}) + +            if check == "bad": +                self.fail(_("Bad Try")) + +            elif check == "paralell": +                self.setWait(300, True) +                self.wait() +                self.retry() + +            elif check == "empty": +                self.fail(_("File not downloadable")) + +            elif check == "wrong_captcha": +                self.invalidCaptcha() +                self.retry() + +            elif check == "downloadserver": +                self.retry(5, 15 * 60, _("No Download server")) + + +    def prepare(self): +        pyfile = self.pyfile + +        self.download_html() + +        if not self.file_exists(): +            self.offline() + +        self.setWait(self.get_waiting_time()) + +        pyfile.name = self.get_file_name() +        pyfile.size = self.get_file_size() + +        self.wait() + +        return True + + +    def download_html(self): +        self.load("http://freakshare.com/index.php", {"language": "EN"})  # Set english language in server session +        self.html = self.load(self.pyfile.url) + + +    def get_file_url(self): +        """ returns the absolute downloadable filepath +        """ +        if not self.html: +            self.download_html() +        if not self.wantReconnect: +            self.req_opts = self.get_download_options()  # get the Post options for the Request +            #file_url = self.pyfile.url +            #return file_url +        else: +            self.offline() + + +    def get_file_name(self): +        if not self.html: +            self.download_html() + +        if not self.wantReconnect: +            m = re.search(r"<h1\sclass=\"box_heading\"\sstyle=\"text-align:center;\">([^ ]+)", self.html) +            if m: +                file_name = m.group(1) +            else: +                file_name = self.pyfile.url + +            return file_name +        else: +            return self.pyfile.url + + +    def get_file_size(self): +        size = 0 +        if not self.html: +            self.download_html() + +        if not self.wantReconnect: +            m = re.search(r"<h1\sclass=\"box_heading\"\sstyle=\"text-align:center;\">[^ ]+ - ([^ ]+) (\w\w)yte", self.html) +            if m: +                units = float(m.group(1).replace(",", "")) +                pow = {'KB': 1, 'MB': 2, 'GB': 3}[m.group(2)] +                size = int(units * 1024 ** pow) + +        return size + + +    def get_waiting_time(self): +        if not self.html: +            self.download_html() + +        if "Your Traffic is used up for today" in self.html: +            self.wantReconnect = True +            return secondsToMidnight(gmt=2) + +        timestring = re.search('\s*var\s(?:downloadWait|time)\s=\s(\d*)[\d.]*;', self.html) +        if timestring: +            return int(timestring.group(1)) +        else: +            return 60 + + +    def file_exists(self): +        """ returns True or False +        """ +        if not self.html: +            self.download_html() +        if re.search(r"This file does not exist!", self.html): +            return False +        else: +            return True + + +    def get_download_options(self): +        re_envelope = re.search(r".*?value=\"Free\sDownload\".*?\n*?(.*?<.*?>\n*)*?\n*\s*?</form>", +                                self.html).group(0)  # get the whole request +        to_sort = re.findall(r"<input\stype=\"hidden\"\svalue=\"(.*?)\"\sname=\"(.*?)\"\s\/>", re_envelope) +        request_options = dict((n, v) for (v, n) in to_sort) + +        herewego = self.load(self.pyfile.url, None, request_options)  # the actual download-Page + +        to_sort = re.findall(r"<input\stype=\".*?\"\svalue=\"(\S*?)\".*?name=\"(\S*?)\"\s.*?\/>", herewego) +        request_options = dict((n, v) for (v, n) in to_sort) + +        challenge = re.search(r"http://api\.recaptcha\.net/challenge\?k=(\w+)", herewego) + +        if challenge: +            re_captcha = ReCaptcha(self) +            (request_options['recaptcha_challenge_field'], +             request_options['recaptcha_response_field']) = re_captcha.challenge(challenge.group(1)) + +        return request_options diff --git a/pyload/plugin/hoster/FreeWayMe.py b/pyload/plugin/hoster/FreeWayMe.py new file mode 100644 index 000000000..560275eba --- /dev/null +++ b/pyload/plugin/hoster/FreeWayMe.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.MultiHoster import MultiHoster + + +class FreeWayMe(MultiHoster): +    __name__    = "FreeWayMe" +    __type__    = "hoster" +    __version__ = "0.16" + +    __pattern__ = r'https://(?:www\.)?free-way\.me/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """FreeWayMe multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Nicolas Giese", "james@free-way.me")] + + +    def setup(self): +        self.resumeDownload = False +        self.multiDL        = self.premium +        self.chunkLimit     = 1 + + +    def handlePremium(self, pyfile): +        user, data = self.account.selectAccount() + +        for _i in xrange(5): +            # try it five times +            header = self.load("https://www.free-way.me/load.php", +                               get={'multiget': 7, +                                    'url'     : pyfile.url, +                                    'user'    : user, +                                    'pw'      : self.account.getAccountData(user)['password'], +                                    'json'    : ""}, +                               just_header=True) + +            if 'location' in header: +                headers = self.load(header['location'], just_header=True) +                if headers['code'] == 500: +                    # error on 2nd stage +                    self.logError(_("Error [stage2]")) +                else: +                    # seems to work.. +                    self.download(header['location']) +                    break +            else: +                # error page first stage +                self.logError(_("Error [stage1]")) + +            #@TODO: handle errors diff --git a/pyload/plugin/hoster/FreevideoCz.py b/pyload/plugin/hoster/FreevideoCz.py new file mode 100644 index 000000000..af238c564 --- /dev/null +++ b/pyload/plugin/hoster/FreevideoCz.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class FreevideoCz(DeadHoster): +    __name__    = "FreevideoCz" +    __type__    = "hoster" +    __version__ = "0.30" + +    __pattern__ = r'http://(?:www\.)?freevideo\.cz/vase-videa/.+' +    __config__  = [] + +    __description__ = """Freevideo.cz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/hoster/FshareVn.py b/pyload/plugin/hoster/FshareVn.py new file mode 100644 index 000000000..73ce4e254 --- /dev/null +++ b/pyload/plugin/hoster/FshareVn.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from urlparse import urljoin + +from pyload.network.RequestFactory import getURL +from pyload.plugin.internal.SimpleHoster import SimpleHoster, parseFileInfo + + +def getInfo(urls): +    for url in urls: +        html = getURL("http://www.fshare.vn/check_link.php", +                      post={'action': "check_link", 'arrlinks': url}, +                      decode=True) + +        yield parseFileInfo(FshareVn, url, html) + + +def doubleDecode(m): +    return m.group(1).decode('raw_unicode_escape') + + +class FshareVn(SimpleHoster): +    __name__    = "FshareVn" +    __type__    = "hoster" +    __version__ = "0.20" + +    __pattern__ = r'http://(?:www\.)?fshare\.vn/file/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """FshareVn hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    INFO_PATTERN = r'<p>(?P<N>[^<]+)<\\/p>[\\trn\s]*<p>(?P<S>[\d.,]+)\s*(?P<U>[\w^_]+)<\\/p>' +    OFFLINE_PATTERN = r'<div class=\\"f_left file_w\\"|<\\/p>\\t\\t\\t\\t\\r\\n\\t\\t<p><\\/p>\\t\\t\\r\\n\\t\\t<p>0 KB<\\/p>' + +    NAME_REPLACEMENTS = [("(.*)", doubleDecode)] + +    LINK_FREE_PATTERN = r'action="(http://download.*?)[#"]' +    WAIT_PATTERN = ur'Lượt tải xuá»ng kế tiếp là:\s*(.*?)\s*<' + + +    def preload(self): +        self.html = self.load("http://www.fshare.vn/check_link.php", +                              post={'action': "check_link", 'arrlinks': pyfile.url}, +                              decode=True) + +        if isinstance(self.TEXT_ENCODING, basestring): +            self.html = unicode(self.html, self.TEXT_ENCODING) + + +    def handleFree(self, pyfile): +        self.html = self.load(pyfile.url, decode=True) + +        self.checkErrors() + +        action, inputs = self.parseHtmlForm('frm_download') +        url = urljoin(pyfile.url, action) + +        if not inputs: +            self.error(_("No FORM")) + +        elif 'link_file_pwd_dl' in inputs: +            password = self.getPassword() + +            if password: +                self.logInfo(_("Password protected link, trying ") + password) +                inputs['link_file_pwd_dl'] = password +                self.html = self.load(url, post=inputs, decode=True) + +                if 'name="link_file_pwd_dl"' in self.html: +                    self.fail(_("Incorrect password")) +            else: +                self.fail(_("No password found")) + +        else: +            self.html = self.load(url, post=inputs, decode=True) + +        self.checkErrors() + +        m = re.search(r'var count = (\d+)', self.html) +        self.setWait(int(m.group(1)) if m else 30) + +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.error(_("LINK_FREE_PATTERN not found")) + +        self.link = m.group(1) +        self.wait() + + +    def checkErrors(self): +        if '/error.php?' in self.req.lastEffectiveURL or u"Liên kết bạn chá»n khÃŽng tá»n" in self.html: +            self.offline() + +        m = re.search(self.WAIT_PATTERN, self.html) +        if m: +            self.logInfo(_("Wait until %s ICT") % m.group(1)) +            wait_until = time.mktime.time(time.strptime.time(m.group(1), "%d/%m/%Y %H:%M")) +            self.wait(wait_until - time.mktime.time(time.gmtime.time()) - 7 * 60 * 60, True) +            self.retry() +        elif '<ul class="message-error">' in self.html: +            msg = "Unknown error occured or wait time not parsed" +            self.logError(msg) +            self.retry(30, 2 * 60, msg) + +        self.info.pop('error', None) diff --git a/pyload/plugin/hoster/Ftp.py b/pyload/plugin/hoster/Ftp.py new file mode 100644 index 000000000..d7aaa730e --- /dev/null +++ b/pyload/plugin/hoster/Ftp.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- + +import pycurl +import re + +from urllib import quote, unquote +from urlparse import urlparse + +from pyload.plugin.Hoster import Hoster + + +class Ftp(Hoster): +    __name__    = "Ftp" +    __type__    = "hoster" +    __version__ = "0.49" + +    __pattern__ = r'(?:ftps?|sftp)://([\w.-]+(:[\w.-]+)?@)?[\w.-]+(:\d+)?/.+' + +    __description__ = """Download from ftp directory""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.com"), +                       ("mkaay", "mkaay@mkaay.de"), +                       ("zoidberg", "zoidberg@mujmail.cz")] + + +    def setup(self): +        self.chunkLimit = -1 +        self.resumeDownload = True + + +    #: Work-around to `filename*=UTF-8` bug; remove in 0.4.10 +    def download(self, url, get={}, post={}, ref=True, cookies=True, disposition=False): +        try: +            if disposition: +                content = urllib2.urlopen(url).info()['Content-Disposition'].split(';') +                self.pyfile.name = content[1].split('filename=')[1][1:-1] or self.pyfile.name +        finally: +            return super(Ftp, self).download(url, get, post, ref, cookies, False) + + +    def process(self, pyfile): +        parsed_url = urlparse(pyfile.url) +        netloc = parsed_url.netloc + +        pyfile.name = parsed_url.path.rpartition('/')[2] +        try: +            pyfile.name = unquote(str(pyfile.name)).decode('utf8') +        except Exception: +            pass + +        if not "@" in netloc: +            servers = [x['login'] for x in self.account.getAllAccounts()] if self.account else [] + +            if netloc in servers: +                self.logDebug("Logging on to %s" % netloc) +                self.req.addAuth(self.account.getAccountInfo(netloc)['password']) +            else: +                pwd = self.getPassword() +                if ':' in pwd: +                    self.req.addAuth(pwd) + +        self.req.http.c.setopt(pycurl.NOBODY, 1) + +        try: +            res = self.load(pyfile.url) +        except pycurl.error, e: +            self.fail(_("Error %d: %s") % e.args) + +        self.req.http.c.setopt(pycurl.NOBODY, 0) +        self.logDebug(self.req.http.header) + +        m = re.search(r"Content-Length:\s*(\d+)", res) +        if m: +            pyfile.size = int(m.group(1)) +            self.download(pyfile.url) +        else: +            #Naive ftp directory listing +            if re.search(r'^25\d.*?"', self.req.http.header, re.M): +                pyfile.url = pyfile.url.rstrip('/') +                pkgname = "/".join(pyfile.package().name, urlparse(pyfile.url).path.rpartition('/')[2]) +                pyfile.url += '/' +                self.req.http.c.setopt(48, 1)  # CURLOPT_DIRLISTONLY +                res = self.load(pyfile.url, decode=False) +                links = [pyfile.url + quote(x) for x in res.splitlines()] +                self.logDebug("LINKS", links) +                self.core.api.addPackage(pkgname, links) +            else: +                self.fail(_("Unexpected server response")) diff --git a/pyload/plugin/hoster/GamefrontCom.py b/pyload/plugin/hoster/GamefrontCom.py new file mode 100644 index 000000000..89e188010 --- /dev/null +++ b/pyload/plugin/hoster/GamefrontCom.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.network.RequestFactory import getURL +from pyload.plugin.Hoster import Hoster +from pyload.utils import parseFileSize + + +class GamefrontCom(Hoster): +    __name__    = "GamefrontCom" +    __type__    = "hoster" +    __version__ = "0.04" + +    __pattern__ = r'http://(?:www\.)?gamefront\.com/files/\w+' + +    __description__ = """Gamefront.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("fwannmacher", "felipe@warhammerproject.com")] + + +    PATTERN_FILENAME = r'<title>(.*?) | Game Front' +    PATTERN_FILESIZE = r'<dt>File Size:</dt>[\n\s]*<dd>(.*?)</dd>' +    PATTERN_OFFLINE = r'This file doesn\'t exist, or has been removed.' + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True +        self.chunkLimit     = -1 + + +    def process(self, pyfile): +        self.pyfile = pyfile +        self.html = self.load(pyfile.url, decode=True) + +        if not self._checkOnline(): +            self.offline() + +        pyfile.name = self._getName() + +        link = self._getLink() + +        if not link.startswith('http://'): +            link = "http://www.gamefront.com/" + link + +        self.download(link) + + +    def _checkOnline(self): +        if re.search(self.PATTERN_OFFLINE, self.html): +            return False +        else: +            return True + + +    def _getName(self): +        name = re.search(self.PATTERN_FILENAME, self.html) +        if name is None: +            self.fail(_("Plugin broken") + +        return name.group(1) + + +    def _getLink(self): +        self.html2 = self.load("http://www.gamefront.com/" + re.search("(files/service/thankyou\\?id=\w+)", +                                                                       self.html).group(1)) +        return re.search("<a href=\"(http://media\d+\.gamefront.com/.*)\">click here</a>", self.html2).group(1).replace("&", "&") + + +def getInfo(urls): +    result = [] + +    for url in urls: +        html = getURL(url) + +        if re.search(GamefrontCom.PATTERN_OFFLINE, html): +            result.append((url, 0, 1, url)) +        else: +            name = re.search(GamefrontCom.PATTERN_FILENAME, html) +            if name is None: +                result.append((url, 0, 1, url)) +            else: +                name = name.group(1) +                size = re.search(GamefrontCom.PATTERN_FILESIZE, html) +                size = parseFileSize(size.group(1)) + +                result.append((name, size, 3, url)) + +    yield result diff --git a/pyload/plugin/hoster/GigapetaCom.py b/pyload/plugin/hoster/GigapetaCom.py new file mode 100644 index 000000000..e9351eac1 --- /dev/null +++ b/pyload/plugin/hoster/GigapetaCom.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +import re + +from random import randint + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class GigapetaCom(SimpleHoster): +    __name__    = "GigapetaCom" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?gigapeta\.com/dl/\w+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """GigaPeta.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN = r'<img src=".*" alt="file" />-->\s*(?P<N>.*?)\s*</td>' +    SIZE_PATTERN = r'<th>\s*Size\s*</th>\s*<td>\s*(?P<S>.*?)\s*</td>' +    OFFLINE_PATTERN = r'<div id="page_error">' + +    COOKIES = [("gigapeta.com", "lang", "us")] + + +    def handleFree(self, pyfile): +        captcha_key = str(randint(1, 100000000)) +        captcha_url = "http://gigapeta.com/img/captcha.gif?x=%s" % captcha_key + +        for _i in xrange(5): +            self.checkErrors() + +            captcha = self.decryptCaptcha(captcha_url) +            self.html = self.load(pyfile.url, +                                  post={'captcha_key': captcha_key, +                                        'captcha'    : captcha, +                                        'download'   : "Download"}, +                                  follow_location=False) + +            m = re.search(r'Location\s*:\s*(.+)', self.req.http.header, re.I) +            if m: +                self.link = m.group(1).rstrip()  #@TODO: Remove .rstrip() in 0.4.10 +                break +            elif "Entered figures don`t coincide with the picture" in self.html: +                self.invalidCaptcha() +        else: +            self.fail(_("No valid captcha code entered")) + + + +    def checkErrors(self): +        if "All threads for IP" in self.html: +            self.logDebug("Your IP is already downloading a file") +            self.wait(5 * 60, True) +            self.retry() + +        self.info.pop('error', None) diff --git a/pyload/plugin/hoster/GooIm.py b/pyload/plugin/hoster/GooIm.py new file mode 100644 index 000000000..77a2603c9 --- /dev/null +++ b/pyload/plugin/hoster/GooIm.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# https://goo.im/devs/liquidsmooth/3.x/codina/Nightly/LS-KK-v3.2-2014-08-01-codina.zip + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class GooIm(SimpleHoster): +    __name__    = "GooIm" +    __type__    = "hoster" +    __version__ = "0.04" + +    __pattern__ = r'https?://(?:www\.)?goo\.im/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Goo.im hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de")] + + +    NAME_PATTERN = r'You will be redirected to .*(?P<N>[^/ ]+)  in' +    OFFLINE_PATTERN = r'The file you requested was not found' + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True + + +    def handleFree(self, pyfile): +        self.wait(10) +        self.link = pyfile.url + diff --git a/pyload/plugin/hoster/GoogledriveCom.py b/pyload/plugin/hoster/GoogledriveCom.py new file mode 100644 index 000000000..746377a53 --- /dev/null +++ b/pyload/plugin/hoster/GoogledriveCom.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -* +# +# Test links: +#   https://drive.google.com/file/d/0B6RNTe4ygItBQm15RnJiTmMyckU/view?pli=1 + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster +from pyload.utils import html_unescape + + +class GoogledriveCom(SimpleHoster): +    __name__    = "GoogledriveCom" +    __type__    = "hoster" +    __version__ = "0.07" + +    __pattern__ = r'https?://(?:www\.)?drive\.google\.com/file/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Drive.google.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de")] + + +    NAME_PATTERN    = r'"og:title" content="(?P<N>.*?)">' +    OFFLINE_PATTERN = r'align="center"><p class="errorMessage"' + + +    def setup(self): +        self.multiDL        = True +        self.resumeDownload = True +        self.chunkLimit     = 1 + + +    def handleFree(self, pyfile): +        try: +            link1 = re.search(r'"(https://docs.google.com/uc\?id.*?export=download)",', +                              self.html.decode('unicode-escape')).group(1) + +        except AttributeError: +            self.error(_("Hop #1 not found")) + +        else: +            self.logDebug("Next hop: %s" % link1) + +        self.html = self.load(link1).decode('unicode-escape') + +        try: +            link2 = html_unescape(re.search(r'href="(/uc\?export=download.*?)">', +                                  self.html).group(1)) + +        except AttributeError: +            self.error(_("Hop #2 not found")) + +        else: +            self.logDebug("Next hop: %s" % link2) + +        link3 = self.load("https://docs.google.com" + link2, just_header=True) +        self.logDebug("DL-Link: %s" % link3['location']) + +        self.link = link3['location'] diff --git a/pyload/plugin/hoster/HellshareCz.py b/pyload/plugin/hoster/HellshareCz.py new file mode 100644 index 000000000..06dbe2178 --- /dev/null +++ b/pyload/plugin/hoster/HellshareCz.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +from urlparse import urljoin + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class HellshareCz(SimpleHoster): +    __name__    = "HellshareCz" +    __type__    = "hoster" +    __version__ = "0.85" + +    __pattern__ = r'http://(?:www\.)?hellshare\.(?:cz|com|sk|hu|pl)/[^?]*/\d+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Hellshare.cz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    CHECK_TRAFFIC = True +    LOGIN_ACCOUNT = True + +    NAME_PATTERN    = r'<h1 id="filename"[^>]*>(?P<N>[^<]+)</h1>' +    SIZE_PATTERN    = r'<strong id="FileSize_master">(?P<S>[\d.,]+) (?P<U>[\w^_]+)</strong>' +    OFFLINE_PATTERN = r'<h1>File not found.</h1>' + +    LINK_FREE_PATTERN = LINK_PREMIUM_PATTERN = r'<a href="([^?]+/(\d+)/\?do=(fileDownloadButton|relatedFileDownloadButton-\2)-showDownloadWindow)"' + + +    def setup(self): +        self.resumeDownload = self.multiDL = bool(self.account) +        self.chunkLimit = 1 diff --git a/pyload/plugin/hoster/HellspyCz.py b/pyload/plugin/hoster/HellspyCz.py new file mode 100644 index 000000000..f8d05cfe8 --- /dev/null +++ b/pyload/plugin/hoster/HellspyCz.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class HellspyCz(DeadHoster): +    __name__    = "HellspyCz" +    __type__    = "hoster" +    __version__ = "0.28" + +    __pattern__ = r'http://(?:www\.)?(?:hellspy\.(?:cz|com|sk|hu|pl)|sciagaj\.pl)(/\S+/\d+)' +    __config__  = [] + +    __description__ = """HellSpy.cz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/hoster/HotfileCom.py b/pyload/plugin/hoster/HotfileCom.py new file mode 100644 index 000000000..9491669b0 --- /dev/null +++ b/pyload/plugin/hoster/HotfileCom.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class HotfileCom(DeadHoster): +    __name__    = "HotfileCom" +    __type__    = "hoster" +    __version__ = "0.37" + +    __pattern__ = r'https?://(?:www\.)?hotfile\.com/dl/\d+/\w+' +    __config__  = [] + +    __description__ = """Hotfile.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("sitacuisses", "sitacuisses@yhoo.de"), +                       ("spoob", "spoob@pyload.org"), +                       ("mkaay", "mkaay@mkaay.de"), +                       ("JoKoT3", "jokot3@gmail.com")] diff --git a/pyload/plugin/hoster/HugefilesNet.py b/pyload/plugin/hoster/HugefilesNet.py new file mode 100644 index 000000000..828e4e79c --- /dev/null +++ b/pyload/plugin/hoster/HugefilesNet.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class HugefilesNet(XFSHoster): +    __name__    = "HugefilesNet" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?hugefiles\.net/\w{12}' + +    __description__ = """Hugefiles.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    SIZE_PATTERN = r'File Size:</span>\s*<span.*?>(?P<S>[^<]+)</span></div>' + +    FORM_INPUTS_MAP = {'ctype': re.compile(r'\d+')} diff --git a/pyload/plugin/hoster/HundredEightyUploadCom.py b/pyload/plugin/hoster/HundredEightyUploadCom.py new file mode 100644 index 000000000..1abdf7212 --- /dev/null +++ b/pyload/plugin/hoster/HundredEightyUploadCom.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class HundredEightyUploadCom(XFSHoster): +    __name__    = "HundredEightyUploadCom" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?180upload\.com/\w{12}' + +    __description__ = """180upload.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    OFFLINE_PATTERN = r'>File Not Found' diff --git a/pyload/plugin/hoster/IFileWs.py b/pyload/plugin/hoster/IFileWs.py new file mode 100644 index 000000000..62b83fe25 --- /dev/null +++ b/pyload/plugin/hoster/IFileWs.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class IFileWs(DeadHoster): +    __name__    = "IFileWs" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?ifile\.ws/\w{12}' +    __config__  = [] + +    __description__ = """Ifile.ws hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("z00nx", "z00nx0@gmail.com")] diff --git a/pyload/plugin/hoster/IcyFilesCom.py b/pyload/plugin/hoster/IcyFilesCom.py new file mode 100644 index 000000000..48ce78ff5 --- /dev/null +++ b/pyload/plugin/hoster/IcyFilesCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class IcyFilesCom(DeadHoster): +    __name__    = "IcyFilesCom" +    __type__    = "hoster" +    __version__ = "0.06" + +    __pattern__ = r'http://(?:www\.)?icyfiles\.com/(.+)' +    __config__  = [] + +    __description__ = """IcyFiles.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("godofdream", "soilfiction@gmail.com")] diff --git a/pyload/plugin/hoster/IfileIt.py b/pyload/plugin/hoster/IfileIt.py new file mode 100644 index 000000000..1b851477a --- /dev/null +++ b/pyload/plugin/hoster/IfileIt.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class IfileIt(DeadHoster): +    __name__    = "IfileIt" +    __type__    = "hoster" +    __version__ = "0.29" + +    __pattern__ = r'^unmatchable$' +    __config__  = [] + +    __description__ = """Ifile.it""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/hoster/IfolderRu.py b/pyload/plugin/hoster/IfolderRu.py new file mode 100644 index 000000000..f87c01e66 --- /dev/null +++ b/pyload/plugin/hoster/IfolderRu.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class IfolderRu(SimpleHoster): +    __name__    = "IfolderRu" +    __type__    = "hoster" +    __version__ = "0.39" + +    __pattern__ = r'http://(?:www)?(files\.)?(ifolder\.ru|metalarea\.org|rusfolder\.(com|net|ru))/(files/)?(?P<ID>\d+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Ifolder.ru hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    SIZE_REPLACEMENTS = [(u'Ðб', 'KB'), (u'Ðб', 'MB'), (u'Ðб', 'GB')] + +    NAME_PATTERN    = ur'(?:<div><span>)?ÐазваМОе:(?:</span>)? <b>(?P<N>[^<]+)</b><(?:/div|br)>' +    SIZE_PATTERN    = ur'(?:<div><span>)?РазЌеÑ:(?:</span>)? <b>(?P<S>[^<]+)</b><(?:/div|br)>' +    OFFLINE_PATTERN = ur'<p>Ѐайл ÐœÐŸÐŒÐµÑ <b>.*?</b> (Ме МайЎеМ|ÑЎалеМ) !!!</p>' + +    SESSION_ID_PATTERN = r'<input type="hidden" name="session" value="(.+?)"' +    INTS_SESSION_PATTERN = r'\(\'ints_session\'\);\s*if\(tag\)\{tag\.value = "(.+?)";\}' +    HIDDEN_INPUT_PATTERN = r'var v = .*?name=\'(.+?)\' value=\'1\'' + +    LINK_FREE_PATTERN = r'<a href="(.+?)" class="downloadbutton_files"' + +    WRONG_CAPTCHA_PATTERN = ur'<font color=Red>МевеÑМÑй кПЎ,<br>ввеЎОÑе еÑе Ñаз</font><br>' + + +    def setup(self): +        self.resumeDownload = self.multiDL = bool(self.account) +        self.chunkLimit     = 1 + + +    def handleFree(self, pyfile): +        url = "http://rusfolder.com/%s" % self.info['pattern']['ID'] +        self.html = self.load("http://rusfolder.com/%s" % self.info['pattern']['ID'], decode=True) +        self.getFileInfo() + +        session_id = re.search(self.SESSION_ID_PATTERN, self.html).groups() + +        captcha_url = "http://ints.rusfolder.com/random/images/?session=%s" % session_id +        for _i in xrange(5): +            action, inputs = self.parseHtmlForm('id="download-step-one-form"') +            inputs['confirmed_number'] = self.decryptCaptcha(captcha_url, cookies=True) +            inputs['action'] = '1' +            self.logDebug(inputs) + +            self.html = self.load(url, decode=True, post=inputs) +            if self.WRONG_CAPTCHA_PATTERN in self.html: +                self.invalidCaptcha() +            else: +                break +        else: +            self.fail(_("Invalid captcha")) + +        self.link = re.search(self.LINK_FREE_PATTERN, self.html).group(1) + diff --git a/pyload/plugin/hoster/JumbofilesCom.py b/pyload/plugin/hoster/JumbofilesCom.py new file mode 100644 index 000000000..380e94a4b --- /dev/null +++ b/pyload/plugin/hoster/JumbofilesCom.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class JumbofilesCom(SimpleHoster): +    __name__    = "JumbofilesCom" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?jumbofiles\.com/(?P<ID>\w{12})' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """JumboFiles.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("godofdream", "soilfiction@gmail.com")] + + +    INFO_PATTERN = r'<TR><TD>(?P<N>[^<]+?)\s*<small>\((?P<S>[\d.,]+)\s*(?P<U>[\w^_]+)' +    OFFLINE_PATTERN = r'Not Found or Deleted / Disabled due to inactivity or DMCA' +    LINK_FREE_PATTERN = r'<meta http-equiv="refresh" content="10;url=(.+)">' + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True + + +    def handleFree(self, pyfile): +        post_data = {"id": self.info['pattern']['ID'], "op": "download3", "rand": ""} +        html = self.load(self.pyfile.url, post=post_data, decode=True) +        self.link = re.search(self.LINK_FREE_PATTERN, html).group(1) diff --git a/pyload/plugin/hoster/JunocloudMe.py b/pyload/plugin/hoster/JunocloudMe.py new file mode 100644 index 000000000..4fc6457d7 --- /dev/null +++ b/pyload/plugin/hoster/JunocloudMe.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class JunocloudMe(XFSHoster): +    __name__    = "JunocloudMe" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:\w+\.)?junocloud\.me/\w{12}' + +    __description__ = """Junocloud.me hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("guidobelix", "guidobelix@hotmail.it")] + + +    URL_REPLACEMENTS = [(r'//(www\.)?junocloud', "//dl3.junocloud")] + +    OFFLINE_PATTERN      = r'>No such file with this filename<' +    TEMP_OFFLINE_PATTERN = r'The page may have been renamed, removed or be temporarily unavailable.<' diff --git a/pyload/plugin/hoster/Keep2ShareCc.py b/pyload/plugin/hoster/Keep2ShareCc.py new file mode 100644 index 000000000..6e70e3962 --- /dev/null +++ b/pyload/plugin/hoster/Keep2ShareCc.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class Keep2ShareCc(SimpleHoster): +    __name__    = "Keep2ShareCc" +    __type__    = "hoster" +    __version__ = "0.21" + +    __pattern__ = r'https?://(?:www\.)?(keep2share|k2s|keep2s)\.cc/file/(?P<ID>\w+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Keep2Share.cc hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    URL_REPLACEMENTS = [(__pattern__ + ".*", "http://keep2s.cc/file/\g<ID>")] + +    NAME_PATTERN = r'File: <span>(?P<N>.+)</span>' +    SIZE_PATTERN = r'Size: (?P<S>[^<]+)</div>' + +    OFFLINE_PATTERN      = r'File not found or deleted|Sorry, this file is blocked or deleted|Error 404' +    TEMP_OFFLINE_PATTERN = r'Downloading blocked due to' + +    LINK_FREE_PATTERN    = r'"(.+?url.html\?file=.+?)"|window\.location\.href = \'(.+?)\';' +    LINK_PREMIUM_PATTERN = r'window\.location\.href = \'(.+?)\';' + +    CAPTCHA_PATTERN = r'src="(/file/captcha\.html.+?)"' + +    WAIT_PATTERN         = r'Please wait ([\d:]+) to download this file' +    TEMP_ERROR_PATTERN   = r'>\s*(Download count files exceed|Traffic limit exceed|Free account does not allow to download more than one file at the same time)' +    ERROR_PATTERN        = r'>\s*(Free user can\'t download large files|You no can access to this file|This download available only for premium users|This is private file)' + + +    def checkErrors(self): +        m = re.search(self.TEMP_ERROR_PATTERN, self.html) +        if m: +            self.info['error'] = m.group(1) +            self.wantReconnect = True +            self.retry(wait_time=30 * 60, reason=m.group(0)) + +        m = re.search(self.ERROR_PATTERN, self.html) +        if m: +            errmsg = self.info['error'] = m.group(1) +            self.error(errmsg) + +        m = re.search(self.WAIT_PATTERN, self.html) +        if m: +            self.logDebug("Hoster told us to wait for %s" % m.group(1)) + +            # string to time convert courtesy of https://stackoverflow.com/questions/10663720 +            ftr = [3600, 60, 1] +            wait_time = sum(a * b for a, b in zip(ftr, map(int, m.group(1).split(':')))) + +            self.wantReconnect = True +            self.retry(wait_time=wait_time, reason="Please wait to download this file") + +        self.info.pop('error', None) + + +    def handleFree(self, pyfile): +        self.fid  = re.search(r'<input type="hidden" name="slow_id" value="(.+?)">', self.html).group(1) +        self.html = self.load(pyfile.url, post={'yt0': '', 'slow_id': self.fid}) + +        # self.logDebug(self.fid) +        # self.logDebug(pyfile.url) + +        self.checkErrors() + +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.handleCaptcha() +            self.wait(31) +            self.html = self.load(pyfile.url) + +            m = re.search(self.LINK_FREE_PATTERN, self.html) +            if m is None: +                self.error(_("Free download link not found")) + +        self.link = m.group(1) +    def handleCaptcha(self): +        post_data = {'free'               : 1, +                     'freeDownloadRequest': 1, +                     'uniqueId'           : self.fid, +                     'yt0'                : ''} + +        m = re.search(r'id="(captcha\-form)"', self.html) +        self.logDebug("captcha-form found %s" % m) + +        m = re.search(self.CAPTCHA_PATTERN, self.html) +        self.logDebug("CAPTCHA_PATTERN found %s" % m) +        if m: +            captcha_url = urljoin("http://keep2s.cc/", m.group(1)) +            post_data['CaptchaForm[code]'] = self.decryptCaptcha(captcha_url) +        else: +            recaptcha = ReCaptcha(self) +            response, challenge = recaptcha.challenge() +            post_data.update({'recaptcha_challenge_field': challenge, +                              'recaptcha_response_field' : response}) + +        self.html = self.load(self.pyfile.url, post=post_data) + +        if 'verification code is incorrect' not in self.html: +            self.correctCaptcha() +        else: +            self.invalidCaptcha() diff --git a/pyload/plugin/hoster/KickloadCom.py b/pyload/plugin/hoster/KickloadCom.py new file mode 100644 index 000000000..d0acb7636 --- /dev/null +++ b/pyload/plugin/hoster/KickloadCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class KickloadCom(DeadHoster): +    __name__    = "KickloadCom" +    __type__    = "hoster" +    __version__ = "0.21" + +    __pattern__ = r'http://(?:www\.)?kickload\.com/get/.+' +    __config__  = [] + +    __description__ = """Kickload.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de")] diff --git a/pyload/plugin/hoster/KingfilesNet.py b/pyload/plugin/hoster/KingfilesNet.py new file mode 100644 index 000000000..b90fba851 --- /dev/null +++ b/pyload/plugin/hoster/KingfilesNet.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.captcha.SolveMedia import SolveMedia +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class KingfilesNet(SimpleHoster): +    __name__    = "KingfilesNet" +    __type__    = "hoster" +    __version__ = "0.07" + +    __pattern__ = r'http://(?:www\.)?kingfiles\.net/(?P<ID>\w{12})' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Kingfiles.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de"), +                     ("Walter Purcaro", "vuolter@gmail.com")] + +    NAME_PATTERN = r'name="fname" value="(?P<N>.+?)">' +    SIZE_PATTERN = r'>Size: .+?">(?P<S>[\d.,]+) (?P<U>[\w^_]+)' + +    OFFLINE_PATTERN = r'>(File Not Found</b><br><br>|File Not Found</h2>)' + +    RAND_ID_PATTERN = r'type=\"hidden\" name=\"rand\" value=\"(.+)\">' + +    LINK_FREE_PATTERN = r'var download_url = \'(.+)\';' + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True + +    def handleFree(self, pyfile): +        # Click the free user button +        post_data = {'op'         : "download1", +                     'usr_login'  : "", +                     'id'         : self.info['pattern']['ID'], +                     'fname'      : pyfile.name, +                     'referer'    : "", +                     'method_free': "+"} + +        self.html = self.load(pyfile.url, post=post_data, decode=True) + +        solvemedia = SolveMedia(self) +        response, challenge = solvemedia.challenge() + +        # Make the downloadlink appear and load the file +        m = re.search(self.RAND_ID_PATTERN, self.html) +        if m is None: +            self.error(_("Random key not found")) + +        rand = m.group(1) +        self.logDebug("rand = ", rand) + +        post_data = {'op'              : "download2", +                     'id'              : self.info['pattern']['ID'], +                     'rand'            : rand, +                     'referer'         : pyfile.url, +                     'method_free'     : "+", +                     'method_premium'  : "", +                     'adcopy_response' : response, +                     'adcopy_challenge': challenge, +                     'down_direct'     : "1"} + +        self.html = self.load(pyfile.url, post=post_data, decode=True) + +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.error(_("Download url not found")) + +        self.link = m.group(1) diff --git a/pyload/plugin/hoster/LemUploadsCom.py b/pyload/plugin/hoster/LemUploadsCom.py new file mode 100644 index 000000000..c7f2e8045 --- /dev/null +++ b/pyload/plugin/hoster/LemUploadsCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class LemUploadsCom(DeadHoster): +    __name__    = "LemUploadsCom" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'https?://(?:www\.)?lemuploads\.com/\w{12}' +    __config__  = [] + +    __description__ = """LemUploads.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")] diff --git a/pyload/plugin/hoster/LetitbitNet.py b/pyload/plugin/hoster/LetitbitNet.py new file mode 100644 index 000000000..32b695b10 --- /dev/null +++ b/pyload/plugin/hoster/LetitbitNet.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- +# +# API Documentation: +# http://api.letitbit.net/reg/static/api.pdf +# +# Test links: +# http://letitbit.net/download/07874.0b5709a7d3beee2408bb1f2eefce/random.bin.html + +import re + +from urlparse import urljoin + +from pyload.utils import json_loads, json_dumps +from pyload.network.RequestFactory import getURL +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster, secondsToMidnight + + +def api_response(url): +    json_data = ["yw7XQy2v9", ["download/info", {"link": url}]] +    api_rep   = getURL("http://api.letitbit.net/json", +                       post={'r': json_dumps(json_data)}) +    return json_loads(api_rep) + + +def getInfo(urls): +    for url in urls: +        api_rep = api_response(url) +        if api_rep['status'] == 'OK': +            info = api_rep['data'][0] +            yield (info['name'], info['size'], 2, url) +        else: +            yield (url, 0, 1, url) + + +class LetitbitNet(SimpleHoster): +    __name__    = "LetitbitNet" +    __type__    = "hoster" +    __version__ = "0.30" + +    __pattern__ = r'https?://(?:www\.)?(letitbit|shareflare)\.net/download/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Letitbit.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("z00nx", "z00nx0@gmail.com")] + + +    URL_REPLACEMENTS = [(r"(?<=http://)([^/]+)", "letitbit.net")] + +    SECONDS_PATTERN = r'seconds\s*=\s*(\d+);' +    CAPTCHA_CONTROL_FIELD = r'recaptcha_control_field\s=\s\'(.+?)\'' + + +    def setup(self): +        self.resumeDownload = True + + +    def handleFree(self, pyfile): +        action, inputs = self.parseHtmlForm('id="ifree_form"') +        if not action: +            self.error(_("ifree_form")) + +        pyfile.size = float(inputs['sssize']) +        self.logDebug(action, inputs) +        inputs['desc'] = "" + +        self.html = self.load(urljoin("http://letitbit.net/", action), post=inputs) + +        m = re.search(self.SECONDS_PATTERN, self.html) +        seconds = int(m.group(1)) if m else 60 + +        self.logDebug("Seconds found", seconds) + +        m = re.search(self.CAPTCHA_CONTROL_FIELD, self.html) +        recaptcha_control_field = m.group(1) + +        self.logDebug("ReCaptcha control field found", recaptcha_control_field) + +        self.wait(seconds) + +        res = self.load("http://letitbit.net/ajax/download3.php", post=" ") +        if res != '1': +            self.error(_("Unknown response - ajax_check_url")) + +        self.logDebug(res) + +        recaptcha = ReCaptcha(self) +        response, challenge = recaptcha.challenge() + +        post_data = {"recaptcha_challenge_field": challenge, +                     "recaptcha_response_field": response, +                     "recaptcha_control_field": recaptcha_control_field} + +        self.logDebug("Post data to send", post_data) + +        res = self.load("http://letitbit.net/ajax/check_recaptcha.php", post=post_data) + +        self.logDebug(res) + +        if not res: +            self.invalidCaptcha() + +        if res == "error_free_download_blocked": +            self.logWarning(_("Daily limit reached")) +            self.wait(secondsToMidnight(gmt=2), True) + +        if res == "error_wrong_captcha": +            self.invalidCaptcha() +            self.retry() + +        elif res.startswith('['): +            urls = json_loads(res) + +        elif res.startswith('http://'): +            urls = [res] + +        else: +            self.error(_("Unknown response - captcha check")) + +        self.link = urls[0] + + +    def handlePremium(self, pyfile): +        api_key = self.user +        premium_key = self.account.getAccountData(self.user)['password'] + +        json_data = [api_key, ["download/direct_links", {"pass": premium_key, "link": pyfile.url}]] +        api_rep = self.load('http://api.letitbit.net/json', post={'r': json_dumps(json_data)}) +        self.logDebug("API Data: " + api_rep) +        api_rep = json_loads(api_rep) + +        if api_rep['status'] == 'FAIL': +            self.fail(api_rep['data']) + +        self.link = api_rep['data'][0][0] diff --git a/pyload/plugin/hoster/LinksnappyCom.py b/pyload/plugin/hoster/LinksnappyCom.py new file mode 100644 index 000000000..a898b21b9 --- /dev/null +++ b/pyload/plugin/hoster/LinksnappyCom.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urlsplit + +from pyload.utils import json_loads, json_dumps +from pyload.plugin.internal.MultiHoster import MultiHoster + + +class LinksnappyCom(MultiHoster): +    __name__    = "LinksnappyCom" +    __type__    = "hoster" +    __version__ = "0.08" + +    __pattern__ = r'https?://(?:[^/]+\.)?linksnappy\.com' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Linksnappy.com multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    SINGLE_CHUNK_HOSTERS = ["easybytez.com"] + + +    def handlePremium(self, pyfile): +        host        = self._get_host(pyfile.url) +        json_params = json_dumps({'link'    : pyfile.url, +                                  'type'    : host, +                                  'username': self.user, +                                  'password': self.account.getAccountData(self.user)['password']}) + +        r = self.load("http://gen.linksnappy.com/genAPI.php", +                      post={'genLinks': json_params}) + +        self.logDebug("JSON data: " + r) + +        j = json_loads(r)['links'][0] + +        if j['error']: +            self.error(_("Error converting the link")) + +        pyfile.name = j['filename'] +        self.link   = j['generated'] + +        if host in self.SINGLE_CHUNK_HOSTERS: +            self.chunkLimit = 1 +        else: +            self.setup() + + +    @staticmethod +    def _get_host(url): +        host = urlsplit(url).netloc +        return re.search(r'[\w-]+\.\w+$', host).group(0) diff --git a/pyload/plugin/hoster/LoadTo.py b/pyload/plugin/hoster/LoadTo.py new file mode 100644 index 000000000..19818e7a3 --- /dev/null +++ b/pyload/plugin/hoster/LoadTo.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://www.load.to/JWydcofUY6/random.bin +# http://www.load.to/oeSmrfkXE/random100.bin + +import re + +from pyload.plugin.captcha.SolveMedia import SolveMedia +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class LoadTo(SimpleHoster): +    __name__    = "LoadTo" +    __type__    = "hoster" +    __version__ = "0.22" + +    __pattern__ = r'http://(?:www\.)?load\.to/\w+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Load.to hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("halfman", "Pulpan3@gmail.com"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    NAME_PATTERN = r'<h1>(?P<N>.+)</h1>' +    SIZE_PATTERN = r'Size: (?P<S>[\d.,]+) (?P<U>[\w^_]+)' +    OFFLINE_PATTERN = r'>Can\'t find file' + +    LINK_FREE_PATTERN = r'<form method="post" action="(.+?)"' +    WAIT_PATTERN = r'type="submit" value="Download \((\d+)\)"' + +    URL_REPLACEMENTS = [(r'(\w)$', r'\1/')] + + +    def setup(self): +        self.multiDL = True +        self.chunkLimit = 1 + + +    def handleFree(self, pyfile): +        # Search for Download URL +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.error(_("LINK_FREE_PATTERN not found")) + +        self.link = m.group(1) + +        # Set Timer - may be obsolete +        m = re.search(self.WAIT_PATTERN, self.html) +        if m: +            self.wait(m.group(1)) + +        # Load.to is using solvemedia captchas since ~july 2014: +        solvemedia  = SolveMedia(self) +        captcha_key = solvemedia.detect_key() + +        if captcha_key: +            response, challenge = solvemedia.challenge(captcha_key) +            self.download(self.link, +                          post={'adcopy_challenge': challenge, +                                'adcopy_response' : response, +                                'returnUrl'       : pyfile.url}) diff --git a/pyload/plugin/hoster/LomafileCom.py b/pyload/plugin/hoster/LomafileCom.py new file mode 100644 index 000000000..678c93ea7 --- /dev/null +++ b/pyload/plugin/hoster/LomafileCom.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class LomafileCom(DeadHoster): +    __name__    = "LomafileCom" +    __type__    = "hoster" +    __version__ = "0.52" + +    __pattern__ = r'http://lomafile\.com/\w{12}' +    __config__  = [] + +    __description__ = """Lomafile.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("nath_schwarz", "nathan.notwhite@gmail.com"), +                       ("guidobelix", "guidobelix@hotmail.it")] diff --git a/pyload/plugin/hoster/LuckyShareNet.py b/pyload/plugin/hoster/LuckyShareNet.py new file mode 100644 index 000000000..bb9563fc2 --- /dev/null +++ b/pyload/plugin/hoster/LuckyShareNet.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +import re + +from bottle import json_loads + +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class LuckyShareNet(SimpleHoster): +    __name__    = "LuckyShareNet" +    __type__    = "hoster" +    __version__ = "0.06" + +    __pattern__ = r'https?://(?:www\.)?luckyshare\.net/(?P<ID>\d{10,})' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """LuckyShare.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    INFO_PATTERN = r'<h1 class=\'file_name\'>(?P<N>\S+)</h1>\s*<span class=\'file_size\'>Filesize: (?P<S>[\d.,]+)(?P<U>[\w^_]+)</span>' +    OFFLINE_PATTERN = r'There is no such file available' + + +    def parseJson(self, rep): +        if 'AJAX Error' in rep: +            html = self.load(self.pyfile.url, decode=True) +            m = re.search(r"waitingtime = (\d+);", html) +            if m: +                seconds = int(m.group(1)) +                self.logDebug("You have to wait %d seconds between free downloads" % seconds) +                self.retry(wait_time=seconds) +            else: +                self.error(_("Unable to detect wait time between free downloads")) +        elif 'Hash expired' in rep: +            self.retry(reason=_("Hash expired")) +        return json_loads(rep) + + +    # TODO: There should be a filesize limit for free downloads +    # TODO: Some files could not be downloaded in free mode +    def handleFree(self, pyfile): +        rep = self.load(r"http://luckyshare.net/download/request/type/time/file/" + self.info['pattern']['ID'], decode=True) + +        self.logDebug("JSON: " + rep) + +        json = self.parseJson(rep) +        self.wait(json['time']) + +        recaptcha = ReCaptcha(self) + +        for _i in xrange(5): +            response, challenge = recaptcha.challenge() +            rep = self.load(r"http://luckyshare.net/download/verify/challenge/%s/response/%s/hash/%s" % +                            (challenge, response, json['hash']), decode=True) +            self.logDebug("JSON: " + rep) +            if 'link' in rep: +                json.update(self.parseJson(rep)) +                self.correctCaptcha() +                break +            elif 'Verification failed' in rep: +                self.invalidCaptcha() +            else: +                self.error(_("Unable to get downlaod link")) + +        if not json['link']: +            self.fail(_("No Download url retrieved/all captcha attempts failed")) + +        self.link = json['link'] + diff --git a/pyload/plugin/hoster/MediafireCom.py b/pyload/plugin/hoster/MediafireCom.py new file mode 100644 index 000000000..683d2272c --- /dev/null +++ b/pyload/plugin/hoster/MediafireCom.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.captcha.SolveMedia import SolveMedia +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class MediafireCom(SimpleHoster): +    __name__    = "MediafireCom" +    __type__    = "hoster" +    __version__ = "0.86" + +    __pattern__ = r'https?://(?:www\.)?mediafire\.com/(file/|view/\??|download(\.php\?|/)|\?)\w{15}' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Mediafire.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    NAME_PATTERN    = r'<META NAME="description" CONTENT="(?P<N>.+?)"/>' +    SIZE_PATTERN    = r'<li>File size: <span>(?P<S>[\d.,]+) (?P<U>[\w^_]+)' +    INFO_PATTERN    = r'oFileSharePopup\.ald\(\'.*?\',\'(?P<N>.+?)\',\'(?P<S>[\d.,]+)\s*(?P<U>[\w^_]+)\',\'\',\'(?P<H>.+?)\'\)' +    OFFLINE_PATTERN = r'class="error_msg_title"' + +    LINK_FREE_PATTERN = r'kNO = "(.+?)"' + +    PASSWORD_PATTERN = r'<form name="form_password"' + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True + + +    def handleFree(self, pyfile): +        solvemedia  = SolveMedia(self) +        captcha_key = solvemedia.detect_key() + +        if captcha_key: +            response, challenge = solvemedia.challenge(captcha_key) +            self.html = self.load(pyfile.url, +                                  post={'adcopy_challenge': challenge, +                                        'adcopy_response' : response}, +                                  decode=True) + +        if self.PASSWORD_PATTERN in self.html: +            password = self.getPassword() + +            if not password: +                self.fail(_("No password found")) +            else: +                self.logInfo(_("Password protected link, trying: ") + password) +                self.html = self.load(self.link, post={'downloadp': password}) + +                if self.PASSWORD_PATTERN in self.html: +                    self.fail(_("Incorrect password")) + +        return super(MediafireCom, self).handleFree(pyfile) diff --git a/pyload/plugin/hoster/MegaCoNz.py b/pyload/plugin/hoster/MegaCoNz.py new file mode 100644 index 000000000..e2506ee3f --- /dev/null +++ b/pyload/plugin/hoster/MegaCoNz.py @@ -0,0 +1,217 @@ +# -*- coding: utf-8 -*- + +import os +import random +import re + +from array import array +from base64 import standard_b64decode + +from Crypto.Cipher import AES +from Crypto.Util import Counter +# from pycurl import SSL_CIPHER_LIST + +from pyload.utils import json_loads, json_dumps +from pyload.plugin.Hoster import Hoster +from pyload.utils import decode, fs_decode, fs_encode + + +############################ General errors ################################### +# EINTERNAL            (-1): An internal error has occurred. Please submit a bug report, detailing the exact circumstances in which this error occurred +# EARGS                (-2): You have passed invalid arguments to this command +# EAGAIN               (-3): (always at the request level) A temporary congestion or server malfunction prevented your request from being processed. No data was altered. Retry. Retries must be spaced with exponential backoff +# ERATELIMIT           (-4): You have exceeded your command weight per time quota. Please wait a few seconds, then try again (this should never happen in sane real-life applications) +# +############################ Upload errors #################################### +# EFAILED              (-5): The upload failed. Please restart it from scratch +# ETOOMANY             (-6): Too many concurrent IP addresses are accessing this upload target URL +# ERANGE               (-7): The upload file packet is out of range or not starting and ending on a chunk boundary +# EEXPIRED             (-8): The upload target URL you are trying to access has expired. Please request a fresh one +# +############################ Stream/System errors ############################# +# ENOENT               (-9): Object (typically, node or user) not found +# ECIRCULAR           (-10): Circular linkage attempted +# EACCESS             (-11): Access violation (e.g., trying to write to a read-only share) +# EEXIST              (-12): Trying to create an object that already exists +# EINCOMPLETE         (-13): Trying to access an incomplete resource +# EKEY                (-14): A decryption operation failed (never returned by the API) +# ESID                (-15): Invalid or expired user session, please relogin +# EBLOCKED            (-16): User blocked +# EOVERQUOTA          (-17): Request over quota +# ETEMPUNAVAIL        (-18): Resource temporarily not available, please try again later +# ETOOMANYCONNECTIONS (-19): Too many connections on this resource +# EWRITE              (-20): Write failed +# EREAD               (-21): Read failed +# EAPPKEY             (-22): Invalid application key; request not processed + + +class MegaCoNz(Hoster): +    __name__    = "MegaCoNz" +    __type__    = "hoster" +    __version__ = "0.26" + +    __pattern__ = r'(?:https?://(?:www\.)?mega\.co\.nz/|mega:|chrome:.+?)#(?P<TYPE>N|)!(?P<ID>[\w^_]+)!(?P<KEY>[\w,-]+)' + +    __description__ = """Mega.co.nz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "ranan@pyload.org"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    API_URL     = "https://eu.api.mega.co.nz/cs" +    FILE_SUFFIX = ".crypted" + + +    def b64_decode(self, data): +        data = data.replace("-", "+").replace("_", "/") +        return standard_b64decode(data + '=' * (-len(data) % 4)) + + +    def getCipherKey(self, key): +        """ Construct the cipher key from the given data """ +        a = array("I", self.b64_decode(key)) + +        k        = array("I", (a[0] ^ a[4], a[1] ^ a[5], a[2] ^ a[6], a[3] ^ a[7])) +        iv       = a[4:6] + array("I", (0, 0)) +        meta_mac = a[6:8] + +        return k, iv, meta_mac + + +    def api_response(self, **kwargs): +        """ Dispatch a call to the api, see https://mega.co.nz/#developers """ + +        # generate a session id, no idea where to obtain elsewhere +        uid = random.randint(10 << 9, 10 ** 10) + +        res = self.load(self.API_URL, get={'id': uid}, post=json_dumps([kwargs])) +        self.logDebug("Api Response: " + res) +        return json_loads(res) + + +    def decryptAttr(self, data, key): +        k, iv, meta_mac = self.getCipherKey(key) +        cbc             = AES.new(k, AES.MODE_CBC, "\0" * 16) +        attr            = decode(cbc.decrypt(self.b64_decode(data))) + +        self.logDebug("Decrypted Attr: %s" % attr) +        if not attr.startswith("MEGA"): +            self.fail(_("Decryption failed")) + +        # Data is padded, 0-bytes must be stripped +        return json_loads(re.search(r'{.+?}', attr).group(0)) + + +    def decryptFile(self, key): +        """  Decrypts the file at lastDownload` """ + +        # upper 64 bit of counter start +        n = self.b64_decode(key)[16:24] + +        # convert counter to long and shift bytes +        k, iv, meta_mac = self.getCipherKey(key) +        ctr             = Counter.new(128, initial_value=long(n.encode("hex"), 16) << 64) +        cipher          = AES.new(k, AES.MODE_CTR, counter=ctr) + +        self.pyfile.setStatus("decrypting") +        self.pyfile.setProgress(0) + +        file_crypted   = fs_encode(self.lastDownload) +        file_decrypted = file_crypted.rsplit(self.FILE_SUFFIX)[0] + +        try: +            f  = open(file_crypted, "rb") +            df = open(file_decrypted, "wb") + +        except IOError, e: +            self.fail(e) + +        chunk_size = 2 ** 15  # buffer size, 32k +        # file_mac   = [0, 0, 0, 0]  # calculate CBC-MAC for checksum + +        chunks = os.path.getsize(file_crypted) / chunk_size + 1 +        for i in xrange(chunks): +            buf = f.read(chunk_size) +            if not buf: +                break + +            chunk = cipher.decrypt(buf) +            df.write(chunk) + +            self.pyfile.setProgress(int((100.0 / chunks) * i)) + +            # chunk_mac = [iv[0], iv[1], iv[0], iv[1]] +            # for i in xrange(0, chunk_size, 16): +                # block = chunk[i:i+16] +                # if len(block) % 16: +                    # block += '=' * (16 - (len(block) % 16)) +                # block = array("I", block) + +                # chunk_mac = [chunk_mac[0] ^ a_[0], chunk_mac[1] ^ block[1], chunk_mac[2] ^ block[2], chunk_mac[3] ^ block[3]] +                # chunk_mac = aes_cbc_encrypt_a32(chunk_mac, k) + +            # file_mac = [file_mac[0] ^ chunk_mac[0], file_mac[1] ^ chunk_mac[1], file_mac[2] ^ chunk_mac[2], file_mac[3] ^ chunk_mac[3]] +            # file_mac = aes_cbc_encrypt_a32(file_mac, k) + +        self.pyfile.setProgress(100) + +        f.close() +        df.close() + +        # if file_mac[0] ^ file_mac[1], file_mac[2] ^ file_mac[3] != meta_mac: +            # os.remove(file_decrypted) +            # self.fail(_("Checksum mismatch")) + +        os.remove(file_crypted) +        self.lastDownload = fs_decode(file_decrypted) + + +    def checkError(self, code): +        ecode = abs(code) + +        if ecode in (9, 16, 21): +            self.offline() + +        elif ecode in (3, 13, 17, 18, 19): +            self.tempOffline() + +        elif ecode in (1, 4, 6, 10, 15, 21): +            self.retry(5, 30, _("Error code: [%s]") % -ecode) + +        else: +            self.fail(_("Error code: [%s]") % -ecode) + + +    def process(self, pyfile): +        pattern = re.match(self.__pattern__, pyfile.url).groupdict() +        id      = pattern['ID'] +        key     = pattern['KEY'] +        public  = pattern['TYPE'] == '' + +        self.logDebug("ID: %s" % id, "Key: %s" % key, "Type: %s" % ("public" if public else "node")) + +        # g is for requesting a download url +        # this is similar to the calls in the mega js app, documentation is very bad +        if public: +            mega = self.api_response(a="g", g=1, p=id, ssl=1)[0] +        else: +            mega = self.api_response(a="g", g=1, n=id, ssl=1)[0] + +        if isinstance(mega, int): +            self.checkError(mega) +        elif "e" in mega: +            self.checkError(mega['e']) + +        attr = self.decryptAttr(mega['at'], key) + +        pyfile.name = attr['n'] + self.FILE_SUFFIX +        pyfile.size = mega['s'] + +        # self.req.http.c.setopt(SSL_CIPHER_LIST, "RC4-MD5:DEFAULT") + +        self.download(mega['g']) + +        self.decryptFile(key) + +        # Everything is finished and final name can be set +        pyfile.name = attr['n'] diff --git a/pyload/plugin/hoster/MegaDebridEu.py b/pyload/plugin/hoster/MegaDebridEu.py new file mode 100644 index 000000000..28707ce6f --- /dev/null +++ b/pyload/plugin/hoster/MegaDebridEu.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +import re + +from urllib import unquote_plus + +from pyload.utils import json_loads +from pyload.plugin.internal.MultiHoster import MultiHoster + + +class MegaDebridEu(MultiHoster): +    __name__    = "MegaDebridEu" +    __type__    = "hoster" +    __version__ = "0.47" + +    __pattern__ = r'http://((?:www\d+\.|s\d+\.)?mega-debrid\.eu|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/download/file/[\w^_]+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """mega-debrid.eu multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("D.Ducatel", "dducatel@je-geek.fr")] + + +    API_URL = "https://www.mega-debrid.eu/api.php" + + +    def api_load(self): +        """ +        Connexion to the mega-debrid API +        Return True if succeed +        """ +        user, data = self.account.selectAccount() +        jsonResponse = self.load(self.API_URL, +                                 get={'action': 'connectUser', 'login': user, 'password': data['password']}) +        res = json_loads(jsonResponse) + +        if res['response_code'] == "ok": +            self.token = res['token'] +            return True +        else: +            return False + + +    def handlePremium(self, pyfile): +        """ +        Debrid a link +        Return The debrided link if succeed or original link if fail +        """ +        if not self.api_load(): +            self.error("Unable to connect to remote API") + +        jsonResponse = self.load(self.API_URL, +                                 get={'action': 'getLink', 'token': self.token}, +                                 post={'link': pyfile.url}) + +        res = json_loads(jsonResponse) +        if res['response_code'] == "ok": +            self.link = res['debridLink'][1:-1] + + diff --git a/pyload/plugin/hoster/MegaFilesSe.py b/pyload/plugin/hoster/MegaFilesSe.py new file mode 100644 index 000000000..6a1a24beb --- /dev/null +++ b/pyload/plugin/hoster/MegaFilesSe.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class MegaFilesSe(DeadHoster): +    __name__    = "MegaFilesSe" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?megafiles\.se/\w{12}' +    __config__  = [] + +    __description__ = """MegaFiles.se hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")] diff --git a/pyload/plugin/hoster/MegaRapidCz.py b/pyload/plugin/hoster/MegaRapidCz.py new file mode 100644 index 000000000..25f696e08 --- /dev/null +++ b/pyload/plugin/hoster/MegaRapidCz.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- + +import re + +from pycurl import HTTPHEADER + +from pyload.network.HTTPRequest import BadHeader +from pyload.network.RequestFactory import getRequest +from pyload.plugin.internal.SimpleHoster import SimpleHoster, parseFileInfo + + +def getInfo(urls): +    h = getRequest() +    h.c.setopt(HTTPHEADER, +               ["Accept: text/html", +                "User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0"]) + +    for url in urls: +        html = h.load(url, decode=True) +        yield parseFileInfo(MegaRapidCz, url, html) + + +class MegaRapidCz(SimpleHoster): +    __name__    = "MegaRapidCz" +    __type__    = "hoster" +    __version__ = "0.56" + +    __pattern__ = r'http://(?:www\.)?(share|mega)rapid\.cz/soubor/\d+/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """MegaRapid.cz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("MikyWoW", "mikywow@seznam.cz"), +                       ("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    NAME_PATTERN = r'<h1.*?><span.*?>(?:<a.*?>)?(?P<N>[^<]+)' +    SIZE_PATTERN = r'<td class="i">Velikost:</td>\s*<td class="h"><strong>\s*(?P<S>[\d.,]+) (?P<U>[\w^_]+)</strong></td>' +    OFFLINE_PATTERN = ur'Nastala chyba 404|Soubor byl smazán' + +    CHECK_TRAFFIC = True + +    LINK_PREMIUM_PATTERN = r'<a href="(.+?)" title="Stahnout">([^<]+)</a>' + +    ERR_LOGIN_PATTERN  = ur'<div class="error_div"><strong>Stahovánà je pÅÃstupné pouze pÅihlášenÜm uÅŸivatelům' +    ERR_CREDIT_PATTERN = ur'<div class="error_div"><strong>Stahovánà zdarma je moÅŸné jen pÅes náš' + + +    def setup(self): +        self.chunkLimit = 1 + + +    def handlePremium(self, pyfile): +        m = re.search(self.LINK_PREMIUM_PATTERN, self.html) +        if m: +            self.link = m.group(1) +        else: +            if re.search(self.ERR_LOGIN_PATTERN, self.html): +                self.relogin(self.user) +                self.retry(wait_time=60, reason=_("User login failed")) +            elif re.search(self.ERR_CREDIT_PATTERN, self.html): +                self.fail(_("Not enough credit left")) +            else: +                self.fail(_("Download link not found")) diff --git a/pyload/plugin/hoster/MegaRapidoNet.py b/pyload/plugin/hoster/MegaRapidoNet.py new file mode 100644 index 000000000..f9d091507 --- /dev/null +++ b/pyload/plugin/hoster/MegaRapidoNet.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- + +from random import randint + +from pyload.plugin.internal.MultiHoster import MultiHoster + + +def random_with_N_digits(n): +    rand = "0." +    not_zero = 0 +    for i in range(1, n + 1): +        r = randint(0, 9) +        if(r > 0): +            not_zero += 1 +        rand += str(r) + +    if not_zero > 0: +        return rand +    else: +        return random_with_N_digits(n) + + +class MegaRapidoNet(MultiHoster): +    __name__    = "MegaRapidoNet" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?\w+\.megarapido\.net/\?file=\w+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """MegaRapido.net multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Kagenoshin", "kagenoshin@gmx.ch")] + + +    LINK_PREMIUM_PATTERN = r'<\s*?a[^>]*?title\s*?=\s*?["\'].*?download["\'][^>]*?href=["\']([^"\']+)' + +    ERROR_PATTERN = r'<\s*?div[^>]*?class\s*?=\s*?["\']?alert-message error.*?>([^<]*)' + + +    def handlePremium(self, pyfile): +        self.html = self.load("http://megarapido.net/gerar.php", +                         post={'rand'     :random_with_N_digits(16), +                               'urllist'  : pyfile.url, +                               'links'    : pyfile.url, +                               'exibir'   : "normal", +                               'usar'     : "premium", +                               'user'     : self.account.getAccountInfo(self.user).get('sid', None), +                               'autoreset': ""}) + +        if "desloga e loga novamente para gerar seus links" in self.html.lower(): +            self.error("You have logged in at another place") + +        return super(MegaRapidoNet, self).handlePremium(pyfile) diff --git a/pyload/plugin/hoster/MegacrypterCom.py b/pyload/plugin/hoster/MegacrypterCom.py new file mode 100644 index 000000000..f8fcb97fe --- /dev/null +++ b/pyload/plugin/hoster/MegacrypterCom.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.utils import json_loads, json_dumps + +from pyload.plugin.hoster.MegaCoNz import MegaCoNz + + +class MegacrypterCom(MegaCoNz): +    __name__    = "MegacrypterCom" +    __type__    = "hoster" +    __version__ = "0.22" + +    __pattern__ = r'https?://\w{0,10}\.?megacrypter\.com/[\w!-]+' + +    __description__ = """Megacrypter.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("GonzaloSR", "gonzalo@gonzalosr.com")] + + +    API_URL = "http://megacrypter.com/api" +    FILE_SUFFIX = ".crypted" + + +    def api_response(self, **kwargs): +        """ Dispatch a call to the api, see megacrypter.com/api_doc """ +        self.logDebug("JSON request: " + json_dumps(kwargs)) +        res = self.load(self.API_URL, post=json_dumps(kwargs)) +        self.logDebug("API Response: " + res) +        return json_loads(res) + + +    def process(self, pyfile): +        # match is guaranteed because plugin was chosen to handle url +        node = re.match(self.__pattern__, pyfile.url).group(0) + +        # get Mega.co.nz link info +        info = self.api_response(link=node, m="info") + +        # get crypted file URL +        dl = self.api_response(link=node, m="dl") + +        # TODO: map error codes, implement password protection +        # if info['pass'] is True: +        #    crypted_file_key, md5_file_key = info['key'].split("#") + +        key = self.b64_decode(info['key']) + +        pyfile.name = info['name'] + self.FILE_SUFFIX + +        self.download(dl['url']) + +        self.decryptFile(key) + +        # Everything is finished and final name can be set +        pyfile.name = info['name'] diff --git a/pyload/plugin/hoster/MegareleaseOrg.py b/pyload/plugin/hoster/MegareleaseOrg.py new file mode 100644 index 000000000..541a1823c --- /dev/null +++ b/pyload/plugin/hoster/MegareleaseOrg.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class MegareleaseOrg(DeadHoster): +    __name__    = "MegareleaseOrg" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'https?://(?:www\.)?megarelease\.org/\w{12}' +    __config__  = [] + +    __description__ = """Megarelease.org hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("derek3x", "derek3x@vmail.me"), +                       ("stickell", "l.stickell@yahoo.it")] diff --git a/pyload/plugin/hoster/MegasharesCom.py b/pyload/plugin/hoster/MegasharesCom.py new file mode 100644 index 000000000..f7ad28f68 --- /dev/null +++ b/pyload/plugin/hoster/MegasharesCom.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class MegasharesCom(SimpleHoster): +    __name__    = "MegasharesCom" +    __type__    = "hoster" +    __version__ = "0.28" + +    __pattern__ = r'http://(?:www\.)?(d\d{2}\.)?megashares\.com/((index\.php)?\?d\d{2}=|dl/)\w+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Megashares.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    NAME_PATTERN = r'<h1 class="black xxl"[^>]*title="(?P<N>.+?)">' +    SIZE_PATTERN = r'<strong><span class="black">Filesize:</span></strong> (?P<S>[\d.,]+) (?P<U>[\w^_]+)' +    OFFLINE_PATTERN = r'<dd class="red">(Invalid Link Request|Link has been deleted|Invalid link)' + +    LINK_PATTERN = r'<div id="show_download_button_%d".*?>\s*<a href="(.+?)">' + +    PASSPORT_LEFT_PATTERN = r'Your Download Passport is: <.*?>(\w+).*?You have.*?<.*?>.*?([\d.]+) (\w+)' +    PASSPORT_RENEW_PATTERN = r'(\d+):<strong>(\d+)</strong>:<strong>(\d+)</strong>' +    REACTIVATE_NUM_PATTERN = r'<input[^>]*id="random_num" value="(\d+)" />' +    REACTIVATE_PASSPORT_PATTERN = r'<input[^>]*id="passport_num" value="(\w+)" />' +    REQUEST_URI_PATTERN = r'var request_uri = "(.+?)";' +    NO_SLOTS_PATTERN = r'<dd class="red">All download slots for this link are currently filled' + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = self.premium + + +    def handlePremium(self, pyfile): +        self.handleDownload(True) + + +    def handleFree(self, pyfile): +        if self.NO_SLOTS_PATTERN in self.html: +            self.retry(wait_time=5 * 60) + +        m = re.search(self.REACTIVATE_PASSPORT_PATTERN, self.html) +        if m: +            passport_num = m.group(1) +            request_uri = re.search(self.REQUEST_URI_PATTERN, self.html).group(1) + +            for _i in xrange(5): +                random_num = re.search(self.REACTIVATE_NUM_PATTERN, self.html).group(1) + +                verifyinput = self.decryptCaptcha("http://d01.megashares.com/index.php", +                                                  get={'secgfx': "gfx", 'random_num': random_num}) + +                self.logInfo(_("Reactivating passport %s: %s %s") % (passport_num, random_num, verifyinput)) + +                res = self.load("http://d01.megashares.com%s" % request_uri, +                                get={'rs'      : "check_passport_renewal", +                                     'rsargs[]': verifyinput, +                                     'rsargs[]': random_num, +                                     'rsargs[]': passport_num, +                                     'rsargs[]': "replace_sec_pprenewal", +                                     'rsrnd[]' : str(int(time.time() * 1000))}) + +                if 'Thank you for reactivating your passport.' in res: +                    self.correctCaptcha() +                    self.retry() +                else: +                    self.invalidCaptcha() +            else: +                self.fail(_("Failed to reactivate passport")) + +        m = re.search(self.PASSPORT_RENEW_PATTERN, self.html) +        if m: +            time = [int(x) for x in m.groups()] +            renew = time[0] + (time[1] * 60) + (time[2] * 60) +            self.logDebug("Waiting %d seconds for a new passport" % renew) +            self.retry(wait_time=renew, reason=_("Passport renewal")) + +        # Check traffic left on passport +        m = re.search(self.PASSPORT_LEFT_PATTERN, self.html, re.M | re.S) +        if m is None: +            self.fail(_("Passport not found")) + +        self.logInfo(_("Download passport: %s") % m.group(1)) +        data_left = float(m.group(2)) * 1024 ** {'B': 0, 'KB': 1, 'MB': 2, 'GB': 3}[m.group(3)] +        self.logInfo(_("Data left: %s %s (%d MB needed)") % (m.group(2), m.group(3), self.pyfile.size / 1048576)) + +        if not data_left: +            self.retry(wait_time=600, reason=_("Passport renewal")) + +        self.handleDownload(False) + + +    def handleDownload(self, premium=False): +        # Find download link; +        m = re.search(self.LINK_PATTERN % (1 if premium else 2), self.html) +        msg = _('%s download URL' % ('Premium' if premium else 'Free')) +        if m is None: +            self.error(msg) + +        self.link = m.group(1) +        self.logDebug("%s: %s" % (msg, self.link)) + diff --git a/pyload/plugin/hoster/MegauploadCom.py b/pyload/plugin/hoster/MegauploadCom.py new file mode 100644 index 000000000..2e26b630d --- /dev/null +++ b/pyload/plugin/hoster/MegauploadCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class MegauploadCom(DeadHoster): +    __name__    = "MegauploadCom" +    __type__    = "hoster" +    __version__ = "0.31" + +    __pattern__ = r'http://(?:www\.)?megaupload\.com/\?.*&?(d|v)=\w+' +    __config__  = [] + +    __description__ = """Megaupload.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org")] diff --git a/pyload/plugin/hoster/MegavideoCom.py b/pyload/plugin/hoster/MegavideoCom.py new file mode 100644 index 000000000..d87967cd9 --- /dev/null +++ b/pyload/plugin/hoster/MegavideoCom.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class MegavideoCom(DeadHoster): +    __name__    = "MegavideoCom" +    __type__    = "hoster" +    __version__ = "0.21" + +    __pattern__ = r'http://(?:www\.)?megavideo\.com/\?.*&?(d|v)=\w+' +    __config__  = [] + +    __description__ = """Megavideo.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("mkaay", "mkaay@mkaay.de")] diff --git a/pyload/plugin/hoster/MovReelCom.py b/pyload/plugin/hoster/MovReelCom.py new file mode 100644 index 000000000..d1833c947 --- /dev/null +++ b/pyload/plugin/hoster/MovReelCom.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class MovReelCom(XFSHoster): +    __name__    = "MovReelCom" +    __type__    = "hoster" +    __version__ = "1.24" + +    __pattern__ = r'http://(?:www\.)?movreel\.com/\w{12}' + +    __description__ = """MovReel.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("JorisV83", "jorisv83-pyload@yahoo.com")] + + +    LINK_PATTERN = r'<a href="(.+?)">Download Link' + diff --git a/pyload/plugin/hoster/MultihostersCom.py b/pyload/plugin/hoster/MultihostersCom.py new file mode 100644 index 000000000..3c9e81242 --- /dev/null +++ b/pyload/plugin/hoster/MultihostersCom.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.hoster.ZeveraCom import ZeveraCom + + +class MultihostersCom(ZeveraCom): +    __name__    = "MultihostersCom" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'https?://(?:www\.)multihosters\.com/(getFiles\.ashx|Members/download\.ashx)\?.*ourl=.+' + +    __description__ = """Multihosters.com multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("tjeh", "tjeh@gmx.net")] diff --git a/pyload/plugin/hoster/MultishareCz.py b/pyload/plugin/hoster/MultishareCz.py new file mode 100644 index 000000000..2444a1675 --- /dev/null +++ b/pyload/plugin/hoster/MultishareCz.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +import re + +from random import random + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class MultishareCz(SimpleHoster): +    __name__    = "MultishareCz" +    __type__    = "hoster" +    __version__ = "0.40" + +    __pattern__ = r'http://(?:www\.)?multishare\.cz/stahnout/(?P<ID>\d+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """MultiShare.cz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    SIZE_REPLACEMENTS = [(' ', '')] + +    CHECK_TRAFFIC = True +    MULTI_HOSTER  = True + +    INFO_PATTERN    = ur'(?:<li>Název|Soubor): <strong>(?P<N>[^<]+)</strong><(?:/li><li|br)>Velikost: <strong>(?P<S>[^<]+)</strong>' +    OFFLINE_PATTERN = ur'<h1>Stáhnout soubor</h1><p><strong>PoÅŸadovanÜ soubor neexistuje.</strong></p>' + + +    def handleFree(self, pyfile): +        self.download("http://www.multishare.cz/html/download_free.php", get={'ID': self.info['pattern']['ID']}) + + +    def handlePremium(self, pyfile): +        self.download("http://www.multishare.cz/html/download_premium.php", get={'ID': self.info['pattern']['ID']}) + + +    def handleMulti(self, pyfile): +        self.html = self.load('http://www.multishare.cz/html/mms_ajax.php', post={"link": pyfile.url}, decode=True) + +        self.checkInfo() + +        if not self.checkTrafficLeft(): +            self.fail(_("Not enough credit left to download file")) + +        self.download("http://dl%d.mms.multishare.cz/html/mms_process.php" % round(random() * 10000 * random()), +                      get={'u_ID'  : self.acc_info['u_ID'], +                           'u_hash': self.acc_info['u_hash'], +                           'link'  : pyfile.url}, +                      disposition=True) diff --git a/pyload/plugin/hoster/MyfastfileCom.py b/pyload/plugin/hoster/MyfastfileCom.py new file mode 100644 index 000000000..81f2ffa78 --- /dev/null +++ b/pyload/plugin/hoster/MyfastfileCom.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.utils import json_loads +from pyload.plugin.internal.MultiHoster import MultiHoster + + +class MyfastfileCom(MultiHoster): +    __name__    = "MyfastfileCom" +    __type__    = "hoster" +    __version__ = "0.08" + +    __pattern__ = r'http://\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/dl/' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Myfastfile.com multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + + +    def setup(self): +        self.chunkLimit = -1 + + +    def handlePremium(self, pyfile): +        self.html = self.load('http://myfastfile.com/api.php', +                         get={'user': self.user, 'pass': self.account.getAccountData(self.user)['password'], +                              'link': pyfile.url}) +        self.logDebug("JSON data: " + self.html) + +        self.html = json_loads(self.html) +        if self.html['status'] != 'ok': +            self.fail(_("Unable to unrestrict link")) + +        self.link = self.html['link'] diff --git a/pyload/plugin/hoster/MystoreTo.py b/pyload/plugin/hoster/MystoreTo.py new file mode 100644 index 000000000..4791d0096 --- /dev/null +++ b/pyload/plugin/hoster/MystoreTo.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# +# Test link: +#   http://mystore.to/dl/mxcA50jKfP + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class MystoreTo(SimpleHoster): +    __name__    = "MystoreTo" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'https?://(?:www\.)?mystore\.to/dl/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Mystore.to hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "")] + + +    NAME_PATTERN    = r'<h1>(?P<N>.+?)<' +    SIZE_PATTERN    = r'FILESIZE: (?P<S>[\d\.,]+) (?P<U>[\w^_]+)' +    OFFLINE_PATTERN = r'>file not found<' + + +    def setup(self): +        self.chunkLimit     = 1 +        self.resumeDownload = True +        self.multiDL        = True + + +    def handleFree(self, pyfile): +        try: +            fid = re.search(r'wert="(.+?)"', self.html).group(1) + +        except AttributeError: +            self.error(_("File-ID not found")) + +        self.link = self.load("http://mystore.to/api/download", +                              post={'FID': fid}) diff --git a/pyload/plugin/hoster/MyvideoDe.py b/pyload/plugin/hoster/MyvideoDe.py new file mode 100644 index 000000000..4b5fa99ca --- /dev/null +++ b/pyload/plugin/hoster/MyvideoDe.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Hoster import Hoster +from pyload.utils import html_unescape + + +class MyvideoDe(Hoster): +    __name__    = "MyvideoDe" +    __type__    = "hoster" +    __version__ = "0.90" + +    __pattern__ = r'http://(?:www\.)?myvideo\.de/watch/' + +    __description__ = """Myvideo.de hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org")] + + +    def process(self, pyfile): +        self.pyfile = pyfile +        self.download_html() +        pyfile.name = self.get_file_name() +        self.download(self.get_file_url()) + + +    def download_html(self): +        self.html = self.load(self.pyfile.url) + + +    def get_file_url(self): +        videoId = re.search(r"addVariable\('_videoid','(.*)'\);p.addParam\('quality'", self.html).group(1) +        videoServer = re.search("rel='image_src' href='(.*)thumbs/.*' />", self.html).group(1) +        file_url = videoServer + videoId + ".flv" +        return file_url + + +    def get_file_name(self): +        file_name_pattern = r"<h1 class='globalHd'>(.*)</h1>" +        return html_unescape(re.search(file_name_pattern, self.html).group(1).replace("/", "") + '.flv') + + +    def file_exists(self): +        self.download_html() +        self.load(str(self.pyfile.url), cookies=False, just_header=True) +        if self.req.lastEffectiveURL == "http://www.myvideo.de/": +            return False +        return True diff --git a/pyload/plugin/hoster/NahrajCz.py b/pyload/plugin/hoster/NahrajCz.py new file mode 100644 index 000000000..5e594ce64 --- /dev/null +++ b/pyload/plugin/hoster/NahrajCz.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class NahrajCz(DeadHoster): +    __name__    = "NahrajCz" +    __type__    = "hoster" +    __version__ = "0.21" + +    __pattern__ = r'http://(?:www\.)?nahraj\.cz/content/download/.+' +    __config__  = [] + +    __description__ = """Nahraj.cz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/hoster/NarodRu.py b/pyload/plugin/hoster/NarodRu.py new file mode 100644 index 000000000..e587ece44 --- /dev/null +++ b/pyload/plugin/hoster/NarodRu.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +import re + +from random import random + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class NarodRu(SimpleHoster): +    __name__    = "NarodRu" +    __type__    = "hoster" +    __version__ = "0.12" + +    __pattern__ = r'http://(?:www\.)?narod(\.yandex)?\.ru/(disk|start/\d+\.\w+-narod\.yandex\.ru)/(?P<ID>\d+)/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Narod.ru hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN = r'<dt class="name">(?:<[^<]*>)*(?P<N>[^<]+)</dt>' +    SIZE_PATTERN = r'<dd class="size">(?P<S>\d[^<]*)</dd>' +    OFFLINE_PATTERN = r'<title>404</title>|Ѐайл ÑЎалеМ Ñ ÑеÑвОÑа|ÐакПМÑОлÑÑ ÑÑПк Ñ
ÑÐ°ÐœÐµÐœÐžÑ Ñайла\.' + +    SIZE_REPLACEMENTS = [(u'ÐÐ', 'KB'), (u'ÐÐ', 'MB'), (u'ÐÐ', 'GB')] +    URL_REPLACEMENTS = [("narod.yandex.ru/", "narod.ru/"), +                             (r"/start/\d+\.\w+-narod\.yandex\.ru/(\d{6,15})/\w+/(\w+)", r"/disk/\1/\2")] + +    CAPTCHA_PATTERN = r'<number url="(.*?)">(\w+)</number>' +    LINK_FREE_PATTERN = r'<a class="h-link" rel="yandex_bar" href="(.+?)">' + + +    def handleFree(self, pyfile): +        for _i in xrange(5): +            self.html = self.load('http://narod.ru/disk/getcapchaxml/?rnd=%d' % int(random() * 777)) + +            m = re.search(self.CAPTCHA_PATTERN, self.html) +            if m is None: +                self.error(_("Captcha")) + +            post_data = {"action": "sendcapcha"} +            captcha_url, post_data['key'] = m.groups() +            post_data['rep'] = self.decryptCaptcha(captcha_url) + +            self.html = self.load(pyfile.url, post=post_data, decode=True) + +            m = re.search(self.LINK_FREE_PATTERN, self.html) +            if m: +                self.link = 'http://narod.ru' + m.group(1) +                self.correctCaptcha() +                break + +            elif u'<b class="error-msg"><strong>ÐÑОблОÑÑ?</strong>' in self.html: +                self.invalidCaptcha() + +            else: +                self.error(_("Download link")) + +        else: +            self.fail(_("No valid captcha code entered")) + diff --git a/pyload/plugin/hoster/NetloadIn.py b/pyload/plugin/hoster/NetloadIn.py new file mode 100644 index 000000000..9c049668b --- /dev/null +++ b/pyload/plugin/hoster/NetloadIn.py @@ -0,0 +1,298 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from urlparse import urljoin + +from pyload.network.RequestFactory import getURL +from pyload.plugin.Hoster import Hoster +from pyload.plugin.Plugin import chunks +from pyload.plugin.captcha.ReCaptcha import ReCaptcha + + +def getInfo(urls): +    ##  returns list of tupels (name, size (in bytes), status (see database.File), url) + +    apiurl = "http://api.netload.in/info.php" +    id_regex = re.compile(NetloadIn.__pattern__) +    urls_per_query = 80 + +    for chunk in chunks(urls, urls_per_query): +        ids = "" +        for url in chunk: +            match = id_regex.search(url) +            if match: +                ids = ids + match.group('ID') + ";" + +        api = getURL(apiurl, +                     get={'auth'   : "Zf9SnQh9WiReEsb18akjvQGqT0I830e8", +                          'bz'     : 1, +                          'md5'    : 1, +                          'file_id': ids}, +                     decode=True) + +        if api is None or len(api) < 10: +            self.logDebug("Prefetch failed") +            return + +        if api.find("unknown_auth") >= 0: +            self.logDebug("Outdated auth code") +            return + +        result = [] + +        for i, r in enumerate(api.splitlines()): +            try: +                tmp = r.split(";") + +                try: +                    size = int(tmp[2]) +                except Exception: +                    size = 0 + +                result.append((tmp[1], size, 2 if tmp[3] == "online" else 1, chunk[i] )) + +            except Exception: +                self.logDebug("Error while processing response: %s" % r) + +        yield result + + +class NetloadIn(Hoster): +    __name__    = "NetloadIn" +    __type__    = "hoster" +    __version__ = "0.49" + +    __pattern__ = r'https?://(?:www\.)?netload\.in/(?P<PATH>datei|index\.php\?id=10&file_id=)(?P<ID>\w+)' + +    __description__ = """Netload.in hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org"), +                       ("RaNaN", "ranan@pyload.org"), +                       ("Gregy", "gregy@gregy.cz")] + + +    RECAPTCHA_KEY = "6LcLJMQSAAAAAJzquPUPKNovIhbK6LpSqCjYrsR1" + + +    def setup(self): +        self.multiDL = self.resumeDownload = self.premium + + +    def process(self, pyfile): +        self.url = pyfile.url + +        self.prepare() + +        pyfile.setStatus("downloading") + +        self.proceed(self.url) + + +    def prepare(self): +        self.api_load() + +        if self.api_data and self.api_data['filename']: +            self.pyfile.name = self.api_data['filename'] + +        if self.premium: +            self.logDebug("Use Premium Account") + +            settings = self.load("http://www.netload.in/index.php", get={'id': 2, 'lang': "en"}) + +            if '<option value="2" selected="selected">Direkter Download' in settings: +                self.logDebug("Using direct download") +                return True +            else: +                self.logDebug("Direct downloads not enabled. Parsing html for a download URL") + +        if self.download_html(): +            return True +        else: +            self.fail(_("Failed")) +            return False + + +    def api_load(self, n=0): +        url      = self.url +        id_regex = re.compile(self.__pattern__) +        match    = id_regex.search(url) + +        if match: +            #normalize url +            self.url = 'http://www.netload.in/datei%s.htm' % match.group('ID') +            self.logDebug("URL: %s" % self.url) +        else: +            self.api_data = False +            return + +        apiurl = "http://api.netload.in/info.php" +        html = self.load(apiurl, cookies=False, +                        get={"file_id": match.group('ID'), "auth": "Zf9SnQh9WiReEsb18akjvQGqT0I830e8", "bz": "1", +                             "md5": "1"}, decode=True).strip() +        if not html and n <= 3: +            self.setWait(2) +            self.wait() +            self.api_load(n + 1) +            return + +        self.logDebug("APIDATA: " + html) + +        self.api_data = {} + +        if html and ";" in html and html not in ("unknown file_data", "unknown_server_data", "No input file specified."): +            lines = html.split(";") +            self.api_data['exists']   = True +            self.api_data['fileid']   = lines[0] +            self.api_data['filename'] = lines[1] +            self.api_data['size']     = lines[2] +            self.api_data['status']   = lines[3] + +            if self.api_data['status'] == "online": +                self.api_data['checksum'] = lines[4].strip() +            else: +                self.api_data = False  # check manually since api data is useless sometimes + +            if lines[0] == lines[1] and lines[2] == "0":  # useless api data +                self.api_data = False +        else: +            self.api_data = False + + +    def final_wait(self, page): +        wait_time = self.get_wait_time.time(page) + +        self.setWait(wait_time) + +        self.logDebug("Final wait %d seconds" % wait_time) + +        self.wait() + +        self.url = self.get_file_url(page) + + +    def check_free_wait(self, page): +        if ">An access request has been made from IP address <" in page: +            self.wantReconnect = True +            self.setWait(self.get_wait_time.time(page) or 30) +            self.wait() +            return True +        else: +            return False + + +    def download_html(self): +        page = self.load(self.url, decode=True) + +        if "/share/templates/download_hddcrash.tpl" in page: +            self.logError(_("Netload HDD Crash")) +            self.fail(_("File temporarily not available")) + +        if not self.api_data: +            self.logDebug("API Data may be useless, get details from html page") + +            if "* The file was deleted" in page: +                self.offline() + +            name = re.search(r'class="dl_first_filename">([^<]+)', page, re.M) +            # the found filename is not truncated +            if name: +                name = name.group(1).strip() +                if not name.endswith(".."): +                    self.pyfile.name = name + +        captchawaited = False + +        for i in xrange(5): +            if not page: +                page = self.load(self.url) +                t = time.time() + 30 + +            if "/share/templates/download_hddcrash.tpl" in page: +                self.logError(_("Netload HDD Crash")) +                self.fail(_("File temporarily not available")) + +            self.logDebug("Try number %d " % i) + +            if ">Your download is being prepared.<" in page: +                self.logDebug("We will prepare your download") +                self.final_wait(page) +                return True + +            self.logDebug("Trying to find captcha") + +            try: +                url_captcha_html = re.search(r'(index.php\?id=10&.*&captcha=1)', page).group(1).replace("amp;", "") + +            except Exception, e: +                self.logDebug("Exception during Captcha regex: %s" % e.message) +                page = None + +            else: +                url_captcha_html = urljoin("http://netload.in/", url_captcha_html) +                break + +        self.html = self.load(url_captcha_html) + +        recaptcha = ReCaptcha(self) + +        for _i in xrange(5): +            response, challenge = recaptcha.challenge(self.RECAPTCHA_KEY) + +            response_page = self.load("http://www.netload.in/index.php?id=10", +                                      post={'captcha_check'            : '1', +                                            'recaptcha_challenge_field': challenge, +                                            'recaptcha_response_field' : response, +                                            'file_id'                  : self.api_data['fileid'], +                                            'Download_Next'            : ''}) +            if "Orange_Link" in response_page: +                break + +            if self.check_free_wait(response_page): +                self.logDebug("Had to wait for next free slot, trying again") +                return self.download_html() + +            else: +                download_url = self.get_file_url(response_page) +                self.logDebug("Download URL after get_file: " + download_url) +                if not download_url.startswith("http://"): +                    self.error(_("Download url: %s") % download_url) +                self.wait() + +                self.url = download_url +                return True + + +    def get_file_url(self, page): +        try: +            file_url_pattern = r'<a class="Orange_Link" href="(http://.+)".?>Or click here' +            attempt = re.search(file_url_pattern, page) +            if attempt: +                return attempt.group(1) +            else: +                self.logDebug("Backup try for final link") +                file_url_pattern = r'<a href="(.+)" class="Orange_Link">Click here' +                attempt = re.search(file_url_pattern, page) +                return "http://netload.in/" + attempt.group(1) + +        except Exception, e: +            self.logDebug("Getting final link failed", e.message) +            return None + + +    def get_wait_time.time(self, page): +        return int(re.search(r"countdown\((.+),'change\(\)'\)", page).group(1)) / 100 + + +    def proceed(self, url): +        self.download(url, disposition=True) + +        check = self.checkDownload({'empty'  : re.compile(r'^$'), +                                    'offline': re.compile("The file was deleted")}) +        if check == "empty": +            self.logInfo(_("Downloaded File was empty")) +            self.retry() + +        elif check == "offline": +            self.offline() diff --git a/pyload/plugin/hoster/NitroflareCom.py b/pyload/plugin/hoster/NitroflareCom.py new file mode 100644 index 000000000..d2bcd871d --- /dev/null +++ b/pyload/plugin/hoster/NitroflareCom.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class NitroflareCom(SimpleHoster): +    __name__    = "NitroflareCom" +    __type__    = "hoster" +    __version__ = "0.09" + +    __pattern__ = r'https?://(?:www\.)?nitroflare\.com/view/(?P<ID>[\w^_]+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Nitroflare.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("sahil", "sahilshekhawat01@gmail.com"), +                       ("Walter Purcaro", "vuolter@gmail.com"), +                       ("Stickell", "l.stickell@yahoo.it")] + +    # URL_REPLACEMENTS = [("http://", "https://")] + +    INFO_PATTERN    = r'title="(?P<N>.+?)".+>(?P<S>[\d.,]+) (?P<U>[\w^_]+)' +    OFFLINE_PATTERN = r'>File doesn\'t exist' + +    LINK_FREE_PATTERN = r'(https?://[\w\-]+\.nitroflare\.com/.+?)"' + +    RECAPTCHA_KEY = "6Lenx_USAAAAAF5L1pmTWvWcH73dipAEzNnmNLgy" + +    PREMIUM_ONLY_PATTERN = r'This file is available with Premium only' +    WAIT_PATTERN         = r'You have to wait .+?<' +    ERROR_PATTERN        = r'downloading is not possible' + + +    def checkErrors(self): +        if not self.html: +            return + +        if not self.premium and re.search(self.PREMIUM_ONLY_PATTERN, self.html): +            self.fail(_("Link require a premium account to be handled")) + +        elif hasattr(self, 'WAIT_PATTERN'): +            m = re.search(self.WAIT_PATTERN, self.html) +            if m: +                wait_time = sum(int(v) * {"hr": 3600, "hour": 3600, "min": 60, "sec": 1}[u.lower()] for v, u in +                                re.findall(r'(\d+)\s*(hr|hour|min|sec)', m.group(0), re.I)) +                self.wait(wait_time, wait_time > 300) +                return + +        elif hasattr(self, 'ERROR_PATTERN'): +            m = re.search(self.ERROR_PATTERN, self.html) +            if m: +                errmsg = self.info['error'] = m.group(1) +                self.error(errmsg) + +        self.info.pop('error', None) + + +    def handleFree(self, pyfile): +        # used here to load the cookies which will be required later +        self.load(pyfile.url, post={'goToFreePage': ""}) + +        self.load("http://nitroflare.com/ajax/setCookie.php", post={'fileId': self.info['pattern']['ID']}) +        self.html = self.load("http://nitroflare.com/ajax/freeDownload.php", +                              post={'method': "startTimer", 'fileId': self.info['pattern']['ID']}) + +        self.checkErrors() + +        try: +            js_file   = self.load("http://nitroflare.com/js/downloadFree.js?v=1.0.1") +            var_time  = re.search("var time = (\\d+);", js_file) +            wait_time = int(var_time.groups()[0]) + +        except Exception: +            wait_time = 60 + +        self.wait(wait_time) + +        recaptcha = ReCaptcha(self) +        response, challenge = recaptcha.challenge(self.RECAPTCHA_KEY) + +        self.html = self.load("http://nitroflare.com/ajax/freeDownload.php", +                              post={'method'                   : "fetchDownload", +                                    'recaptcha_challenge_field': challenge, +                                    'recaptcha_response_field' : response}) + +        if "The captcha wasn't entered correctly" in self.html: +            self.logWarning("The captcha wasn't entered correctly") +            return + +        if "You have to fill the captcha" in self.html: +            self.logWarning("Captcha unfilled") +            return + +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m: +            self.link = m.group(1) +        else: +            self.logError("Unable to detect direct link") diff --git a/pyload/plugin/hoster/NoPremiumPl.py b/pyload/plugin/hoster/NoPremiumPl.py new file mode 100644 index 000000000..c80d2ef6c --- /dev/null +++ b/pyload/plugin/hoster/NoPremiumPl.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- + +from pyload.utils import json_loads +from pyload.plugin.internal.MultiHoster import MultiHoster + + +class NoPremiumPl(MultiHoster): +    __name__    = "NoPremiumPl" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'https?://direct\.nopremium\.pl.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """NoPremium.pl multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("goddie", "dev@nopremium.pl")] + + +    API_URL = "http://crypt.nopremium.pl" + +    API_QUERY = {'site'    : "nopremium", +                 'output'  : "json", +                 'username': "", +                 'password': "", +                 'url'     : ""} + +    ERROR_CODES = {0 : "[%s] Incorrect login credentials", +                   1 : "[%s] Not enough transfer to download - top-up your account", +                   2 : "[%s] Incorrect / dead link", +                   3 : "[%s] Error connecting to hosting, try again later", +                   9 : "[%s] Premium account has expired", +                   15: "[%s] Hosting no longer supported", +                   80: "[%s] Too many incorrect login attempts, account blocked for 24h"} + + +    def prepare(self): +        super(NoPremiumPl, self).prepare() + +        data = self.account.getAccountData(self.user) + +        self.usr = data['usr'] +        self.pwd = data['pwd'] + + +    def runFileQuery(self, url, mode=None): +        query = self.API_QUERY.copy() + +        query["username"] = self.usr +        query["password"] = self.pwd +        query["url"]      = url + +        if mode == "fileinfo": +            query['check'] = 2 +            query['loc']   = 1 + +        self.logDebug(query) + +        return self.load(self.API_URL, post=query) + + +    def handleFree(self, pyfile): +        try: +            data = self.runFileQuery(pyfile.url, 'fileinfo') + +        except Exception: +            self.logDebug("runFileQuery error") +            self.tempOffline() + +        try: +            parsed = json_loads(data) + +        except Exception: +            self.logDebug("loads error") +            self.tempOffline() + +        self.logDebug(parsed) + +        if "errno" in parsed.keys(): +            if parsed["errno"] in self.ERROR_CODES: +                # error code in known +                self.fail(self.ERROR_CODES[parsed["errno"]] % self.__name__) +            else: +                # error code isn't yet added to plugin +                self.fail( +                    parsed["errstring"] +                    or _("Unknown error (code: %s)") % parsed["errno"] +                ) + +        if "sdownload" in parsed: +            if parsed["sdownload"] == "1": +                self.fail( +                    _("Download from %s is possible only using NoPremium.pl website \ +                    directly") % parsed["hosting"]) + +        pyfile.name = parsed["filename"] +        pyfile.size = parsed["filesize"] + +        try: +            self.link = self.runFileQuery(pyfile.url, 'filedownload') + +        except Exception: +            self.logDebug("runFileQuery error #2") +            self.tempOffline() diff --git a/pyload/plugin/hoster/NosuploadCom.py b/pyload/plugin/hoster/NosuploadCom.py new file mode 100644 index 000000000..3d785dd90 --- /dev/null +++ b/pyload/plugin/hoster/NosuploadCom.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class NosuploadCom(XFSHoster): +    __name__    = "NosuploadCom" +    __type__    = "hoster" +    __version__ = "0.31" + +    __pattern__ = r'http://(?:www\.)?nosupload\.com/\?d=\w{12}' + +    __description__ = """Nosupload.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("igel", "igelkun@myopera.com")] + + +    SIZE_PATTERN = r'<p><strong>Size:</strong> (?P<S>[\d.,]+) (?P<U>[\w^_]+)</p>' +    LINK_PATTERN = r'<a class="select" href="(http://.+?)">Download</a>' + +    WAIT_PATTERN = r'Please wait.*?>(\d+)</span>' + + +    def getDownloadLink(self): +        # stage1: press the "Free Download" button +        data = self.getPostParameters() +        self.html = self.load(self.pyfile.url, post=data, decode=True) + +        # stage2: wait some time and press the "Download File" button +        data = self.getPostParameters() +        wait_time = re.search(self.WAIT_PATTERN, self.html, re.M | re.S).group(1) +        self.logDebug("Hoster told us to wait %s seconds" % wait_time) +        self.wait(wait_time) +        self.html = self.load(self.pyfile.url, post=data, decode=True) + +        # stage3: get the download link +        return re.search(self.LINK_PATTERN, self.html, re.S).group(1) diff --git a/pyload/plugin/hoster/NovafileCom.py b/pyload/plugin/hoster/NovafileCom.py new file mode 100644 index 000000000..3bb4760ce --- /dev/null +++ b/pyload/plugin/hoster/NovafileCom.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://novafile.com/vfun4z6o2cit +# http://novafile.com/s6zrr5wemuz4 + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class NovafileCom(XFSHoster): +    __name__    = "NovafileCom" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?novafile\.com/\w{12}' + +    __description__ = """Novafile.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    ERROR_PATTERN = r'class="alert.+?alert-separate".*?>\s*(?:<p>)?(.*?)\s*</' +    WAIT_PATTERN  = r'<p>Please wait <span id="count".*?>(\d+)</span> seconds</p>' + +    LINK_PATTERN = r'<a href="(http://s\d+\.novafile\.com/.*?)" class="btn btn-green">Download File</a>' diff --git a/pyload/plugin/hoster/NowDownloadSx.py b/pyload/plugin/hoster/NowDownloadSx.py new file mode 100644 index 000000000..77b1b1d27 --- /dev/null +++ b/pyload/plugin/hoster/NowDownloadSx.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster +from pyload.utils import fixup + + +class NowDownloadSx(SimpleHoster): +    __name__    = "NowDownloadSx" +    __type__    = "hoster" +    __version__ = "0.09" + +    __pattern__ = r'http://(?:www\.)?(nowdownload\.[a-zA-Z]{2,}/(dl/|download\.php.+?id=|mobile/(#/files/|.+?id=))|likeupload\.org/)\w+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """NowDownload.sx hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("godofdream", "soilfiction@gmail.com"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    INFO_PATTERN = r'Downloading</span> <br> (?P<N>.*) (?P<S>[\d.,]+) (?P<U>[\w^_]+) </h4>' +    OFFLINE_PATTERN = r'>This file does not exist' + +    TOKEN_PATTERN = r'"(/api/token\.php\?token=\w+)"' +    CONTINUE_PATTERN = r'"(/dl2/\w+/\w+)"' +    WAIT_PATTERN = r'\.countdown\(\{until: \+(\d+),' +    LINK_FREE_PATTERN = r'(http://s\d+\.coolcdn\.info/nowdownload/.+?)["\']' + +    NAME_REPLACEMENTS = [("&#?\w+;", fixup), (r'<.*?>', '')] + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True +        self.chunkLimit     = -1 + + +    def handleFree(self, pyfile): +        tokenlink = re.search(self.TOKEN_PATTERN, self.html) +        continuelink = re.search(self.CONTINUE_PATTERN, self.html) +        if tokenlink is None or continuelink is None: +            self.error() + +        m = re.search(self.WAIT_PATTERN, self.html) +        if m: +            wait = int(m.group(1)) +        else: +            wait = 60 + +        baseurl = "http://www.nowdownload.at" +        self.html = self.load(baseurl + str(tokenlink.group(1))) +        self.wait(wait) + +        self.html = self.load(baseurl + str(continuelink.group(1))) + +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.error(_("Download link not found")) + +        self.link = m.group(1) + diff --git a/pyload/plugin/hoster/NowVideoSx.py b/pyload/plugin/hoster/NowVideoSx.py new file mode 100644 index 000000000..423f08ccd --- /dev/null +++ b/pyload/plugin/hoster/NowVideoSx.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class NowVideoSx(SimpleHoster): +    __name__    = "NowVideoSx" +    __type__    = "hoster" +    __version__ = "0.12" + +    __pattern__ = r'http://(?:www\.)?nowvideo\.[a-zA-Z]{2,}/(video/|mobile/(#/videos/|.+?id=))(?P<ID>\w+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """NowVideo.sx hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    URL_REPLACEMENTS = [(__pattern__ + ".*", r'http://www.nowvideo.sx/video/\g<ID>')] + +    NAME_PATTERN = r'<h4>(?P<N>.+?)<' +    OFFLINE_PATTERN = r'>This file no longer exists' + +    LINK_FREE_PATTERN = r'<source src="(.+?)"' +    LINK_PREMIUM_PATTERN = r'<div id="content_player" >\s*<a href="(.+?)"' + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True + + +    def handleFree(self, pyfile): +        self.html = self.load("http://www.nowvideo.sx/mobile/video.php", get={'id': self.info['pattern']['ID']}) + +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.error(_("Free download link not found")) + +        self.link = m.group(1) + diff --git a/pyload/plugin/hoster/OboomCom.py b/pyload/plugin/hoster/OboomCom.py new file mode 100644 index 000000000..dca6903ca --- /dev/null +++ b/pyload/plugin/hoster/OboomCom.py @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# https://www.oboom.com/B7CYZIEB/10Mio.dat + +import re + +from pyload.utils import json_loads +from pyload.plugin.Hoster import Hoster +from pyload.plugin.captcha.ReCaptcha import ReCaptcha + + +class OboomCom(Hoster): +    __name__    = "OboomCom" +    __type__    = "hoster" +    __version__ = "0.31" + +    __pattern__ = r'https?://(?:www\.)?oboom\.com/(#(id=|/)?)?(?P<ID>\w{8})' + +    __description__ = """oboom.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stanley", "stanley.foerster@gmail.com")] + + +    RECAPTCHA_KEY = "6LdqpO0SAAAAAJGHXo63HyalP7H4qlRs_vff0kJX" + + +    def setup(self): +        self.chunkLimit = 1 +        self.multiDL = self.resumeDownload = self.premium + + +    def process(self, pyfile): +        self.pyfile.url.replace(".com/#id=", ".com/#") +        self.pyfile.url.replace(".com/#/", ".com/#") +        self.getFileId(self.pyfile.url) +        self.getSessionToken() +        self.getFileInfo(self.sessionToken, self.fileId) +        self.pyfile.name = self.fileName +        self.pyfile.size = self.fileSize +        if not self.premium: +            self.solveCaptcha() +        self.getDownloadTicket() +        self.download("https://%s/1.0/dlh" % self.downloadDomain, get={"ticket": self.downloadTicket, "http_errors": 0}) + + +    def loadUrl(self, url, get=None): +        if get is None: +            get = dict() +        return json_loads(self.load(url, get, decode=True)) + + +    def getFileId(self, url): +        self.fileId = re.match(OboomCom.__pattern__, url).group('ID') + + +    def getSessionToken(self): +        if self.premium: +            accountInfo = self.account.getAccountInfo(self.user, True) +            if "session" in accountInfo: +                self.sessionToken = accountInfo['session'] +            else: +                self.fail(_("Could not retrieve premium session")) +        else: +            apiUrl = "https://www.oboom.com/1.0/guestsession" +            result = self.loadUrl(apiUrl) +            if result[0] == 200: +                self.sessionToken = result[1] +            else: +                self.fail(_("Could not retrieve token for guest session. Error code: %s") % result[0]) + + +    def solveCaptcha(self): +        recaptcha = ReCaptcha(self) + +        for _i in xrange(5): +            response, challenge = recaptcha.challenge(self.RECAPTCHA_KEY) +            apiUrl = "https://www.oboom.com/1.0/download/ticket" +            params = {"recaptcha_challenge_field": challenge, +                      "recaptcha_response_field": response, +                      "download_id": self.fileId, +                      "token": self.sessionToken} +            result = self.loadUrl(apiUrl, params) + +            if result[0] == 200: +                self.downloadToken = result[1] +                self.downloadAuth = result[2] +                self.correctCaptcha() +                self.setWait(30) +                self.wait() +                break + +            elif result[0] == 400: +                if result[1] == "incorrect-captcha-sol": +                    self.invalidCaptcha() +                elif result[1] == "captcha-timeout": +                    self.invalidCaptcha() +                elif result[1] == "forbidden": +                    self.retry(5, 15 * 60, _("Service unavailable")) + +            elif result[0] == 403: +                if result[1] == -1:  # another download is running +                    self.setWait(15 * 60) +                else: +                    self.setWait(result[1], True) +                self.wait() +                self.retry(5) +        else: +            self.invalidCaptcha() +            self.fail(_("Received invalid captcha 5 times")) + + +    def getFileInfo(self, token, fileId): +        apiUrl = "https://api.oboom.com/1.0/info" +        params = {"token": token, "items": fileId, "http_errors": 0} + +        result = self.loadUrl(apiUrl, params) +        if result[0] == 200: +            item = result[1][0] +            if item['state'] == "online": +                self.fileSize = item['size'] +                self.fileName = item['name'] +            else: +                self.offline() +        else: +            self.fail(_("Could not retrieve file info. Error code %s: %s") % (result[0], result[1])) + + +    def getDownloadTicket(self): +        apiUrl = "https://api.oboom.com/1/dl" +        params = {"item": self.fileId, "http_errors": 0} +        if self.premium: +            params['token'] = self.sessionToken +        else: +            params['token'] = self.downloadToken +            params['auth'] = self.downloadAuth + +        result = self.loadUrl(apiUrl, params) +        if result[0] == 200: +            self.downloadDomain = result[1] +            self.downloadTicket = result[2] +        elif result[0] == 421: +            self.retry(wait_time=result[2] + 60, reason=_("Connection limit exceeded")) +        else: +            self.fail(_("Could not retrieve download ticket. Error code: %s") % result[0]) diff --git a/pyload/plugin/hoster/OneFichierCom.py b/pyload/plugin/hoster/OneFichierCom.py new file mode 100644 index 000000000..fc79b7502 --- /dev/null +++ b/pyload/plugin/hoster/OneFichierCom.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class OneFichierCom(SimpleHoster): +    __name__    = "OneFichierCom" +    __type__    = "hoster" +    __version__ = "0.83" + +    __pattern__ = r'https?://(?:www\.)?(?:(?P<ID1>\w+)\.)?(?P<HOST>1fichier\.com|alterupload\.com|cjoint\.net|d(es)?fichiers\.com|dl4free\.com|megadl\.fr|mesfichiers\.org|piecejointe\.net|pjointe\.com|tenvoi\.com)(?:/\?(?P<ID2>\w+))?' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """1fichier.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("fragonib", "fragonib[AT]yahoo[DOT]es"), +                       ("the-razer", "daniel_ AT gmx DOT net"), +                       ("zoidberg", "zoidberg@mujmail.cz"), +                       ("imclem", ""), +                       ("stickell", "l.stickell@yahoo.it"), +                       ("Elrick69", "elrick69[AT]rocketmail[DOT]com"), +                       ("Walter Purcaro", "vuolter@gmail.com"), +                       ("Ludovic Lehmann", "ludo.lehmann@gmail.com")] + + +    NAME_PATTERN = r'>FileName :</td>\s*<td.*>(?P<N>.+?)<' +    SIZE_PATTERN = r'>Size :</td>\s*<td.*>(?P<S>[\d.,]+) (?P<U>[\w^_]+)' + +    OFFLINE_PATTERN = r'File not found !\s*<' + +    COOKIES     = [("1fichier.com", "LG", "en")] + +    WAIT_PATTERN = r'>You must wait \d+ minutes' + + +    def setup(self): +        self.multiDL        = self.premium +        self.resumeDownload = True + + +    def handleFree(self, pyfile): +        id = self.info['pattern']['ID1'] or self.info['pattern']['ID2'] +        url, inputs = self.parseHtmlForm('action="https://1fichier.com/\?%s' % id) + +        if not url: +            self.fail(_("Download link not found")) + +        if "pass" in inputs: +            inputs['pass'] = self.getPassword() + +        inputs['submit'] = "Download" + +        self.download(url, post=inputs) + + +    def handlePremium(self, pyfile): +        self.download(pyfile.url, post={'dl': "Download", 'did': 0}) diff --git a/pyload/plugin/hoster/OronCom.py b/pyload/plugin/hoster/OronCom.py new file mode 100644 index 000000000..9a5207367 --- /dev/null +++ b/pyload/plugin/hoster/OronCom.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class OronCom(DeadHoster): +    __name__    = "OronCom" +    __type__    = "hoster" +    __version__ = "0.14" + +    __pattern__ = r'https?://(?:www\.)?oron\.com/\w{12}' +    __config__  = [] + +    __description__ = """Oron.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("chrox", "chrox@pyload.org"), +                       ("DHMH", "DHMH@pyload.org")] diff --git a/pyload/plugin/hoster/OverLoadMe.py b/pyload/plugin/hoster/OverLoadMe.py new file mode 100644 index 000000000..1aab372b1 --- /dev/null +++ b/pyload/plugin/hoster/OverLoadMe.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +import re + +from random import randrange +from urllib import unquote + +from pyload.utils import json_loads +from pyload.plugin.internal.MultiHoster import MultiHoster +from pyload.utils import parseFileSize + + +class OverLoadMe(MultiHoster): +    __name__    = "OverLoadMe" +    __type__    = "hoster" +    __version__ = "0.11" + +    __pattern__ = r'https?://.*overload\.me/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Over-Load.me multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("marley", "marley@over-load.me")] + + +    def setup(self): +        self.chunkLimit = 5 + + +    def handlePremium(self, pyfile): +        https = "https" if self.getConfig('ssl') else "http" +        data  = self.account.getAccountData(self.user) +        page  = self.load(https + "://api.over-load.me/getdownload.php", +                          get={'auth': data['password'], +                               'link': pyfile.url}) + +        data = json_loads(page) + +        self.logDebug(data) + +        if data['error'] == 1: +            self.logWarning(data['msg']) +            self.tempOffline() +        else: +            if pyfile.name and pyfile.name.endswith('.tmp') and data['filename']: +                pyfile.name = data['filename'] +                pyfile.size = parseFileSize(data['filesize']) + +            http_repl = ["http://", "https://"] +            self.link = data['downloadlink'].replace(*http_repl if self.getConfig('ssl') else http_repl[::-1]) + + diff --git a/pyload/plugin/hoster/PandaplaNet.py b/pyload/plugin/hoster/PandaplaNet.py new file mode 100644 index 000000000..2a61a69c4 --- /dev/null +++ b/pyload/plugin/hoster/PandaplaNet.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class PandaplaNet(DeadHoster): +    __name__    = "PandaplaNet" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?pandapla\.net/\w{12}' +    __config__  = [] + +    __description__ = """Pandapla.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")] diff --git a/pyload/plugin/hoster/PornhostCom.py b/pyload/plugin/hoster/PornhostCom.py new file mode 100644 index 000000000..70ce4f32e --- /dev/null +++ b/pyload/plugin/hoster/PornhostCom.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Hoster import Hoster + + +class PornhostCom(Hoster): +    __name__    = "PornhostCom" +    __type__    = "hoster" +    __version__ = "0.20" + +    __pattern__ = r'http://(?:www\.)?pornhost\.com/(\d+/\d+\.html|\d+)' + +    __description__ = """Pornhost.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de")] + + +    def process(self, pyfile): +        self.download_html() +        if not self.file_exists(): +            self.offline() + +        pyfile.name = self.get_file_name() +        self.download(self.get_file_url()) + + +    # Old interface +    def download_html(self): +        url = self.pyfile.url +        self.html = self.load(url) + + +    def get_file_url(self): +        """ returns the absolute downloadable filepath +        """ +        if not self.html: +            self.download_html() + +        url = re.search(r'download this file</label>.*?<a href="(.*?)"', self.html) +        if url is None: +            url = re.search(r'"(http://dl\d+\.pornhost\.com/files/.*?/.*?/.*?/.*?/.*?/.*?\..*?)"', self.html) +            if url is None: +                url = re.search(r'width: 894px; height: 675px">.*?<img src="(.*?)"', self.html) +                if url is None: +                    url = re.search(r'"http://file\d+\.pornhost\.com/\d+/.*?"', +                                    self.html)  # TODO: fix this one since it doesn't match + +        return url.group(1).strip() + + +    def get_file_name(self): +        if not self.html: +            self.download_html() + +        name = re.search(r'<title>pornhost\.com - free file hosting with a twist - gallery(.*?)</title>', self.html) +        if name is None: +            name = re.search(r'id="url" value="http://www\.pornhost\.com/(.*?)/"', self.html) +            if name is None: +                name = re.search(r'<title>pornhost\.com - free file hosting with a twist -(.*?)</title>', self.html) +                if name is None: +                    name = re.search(r'"http://file\d+\.pornhost\.com/.*?/(.*?)"', self.html) + +        name = name.group(1).strip() + ".flv" + +        return name + + +    def file_exists(self): +        """ returns True or False +        """ +        if not self.html: +            self.download_html() + +        if re.search(r'gallery not found|You will be redirected to', self.html): +            return False +        else: +            return True diff --git a/pyload/plugin/hoster/PornhubCom.py b/pyload/plugin/hoster/PornhubCom.py new file mode 100644 index 000000000..08ff52891 --- /dev/null +++ b/pyload/plugin/hoster/PornhubCom.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Hoster import Hoster + + +class PornhubCom(Hoster): +    __name__    = "PornhubCom" +    __type__    = "hoster" +    __version__ = "0.50" + +    __pattern__ = r'http://(?:www\.)?pornhub\.com/view_video\.php\?viewkey=\w+' + +    __description__ = """Pornhub.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de")] + + +    def process(self, pyfile): +        self.download_html() +        if not self.file_exists(): +            self.offline() + +        pyfile.name = self.get_file_name() +        self.download(self.get_file_url()) + + +    def download_html(self): +        url = self.pyfile.url +        self.html = self.load(url) + + +    def get_file_url(self): +        """ returns the absolute downloadable filepath +        """ +        if not self.html: +            self.download_html() + +        url = "http://www.pornhub.com//gateway.php" +        video_id = self.pyfile.url.split('=')[-1] +        # thanks to jD team for this one  v +        post_data = "\x00\x03\x00\x00\x00\x01\x00\x0c\x70\x6c\x61\x79\x65\x72\x43\x6f\x6e\x66\x69\x67\x00\x02\x2f\x31\x00\x00\x00\x44\x0a\x00\x00\x00\x03\x02\x00" +        post_data += chr(len(video_id)) +        post_data += video_id +        post_data += "\x02\x00\x02\x2d\x31\x02\x00\x20" +        post_data += "add299463d4410c6d1b1c418868225f7" + +        content = self.load(url, post=str(post_data)) + +        new_content = "" +        for x in content: +            if ord(x) < 32 or ord(x) > 176: +                new_content += '#' +            else: +                new_content += x + +        content = new_content + +        return re.search(r'flv_url.*(http.*?)##post_roll', content).group(1) + + +    def get_file_name(self): +        if not self.html: +            self.download_html() + +        m = re.search(r'<title.+?>([^<]+) - ', self.html) +        if m: +            name = m.group(1) +        else: +            matches = re.findall('<h1>(.*?)</h1>', self.html) +            if len(matches) > 1: +                name = matches[1] +            else: +                name = matches[0] + +        return name + '.flv' + + +    def file_exists(self): +        """ returns True or False +        """ +        if not self.html: +            self.download_html() + +        if re.search(r'This video is no longer in our database or is in conversion', self.html): +            return False +        else: +            return True diff --git a/pyload/plugin/hoster/PotloadCom.py b/pyload/plugin/hoster/PotloadCom.py new file mode 100644 index 000000000..c1e96ff77 --- /dev/null +++ b/pyload/plugin/hoster/PotloadCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class PotloadCom(DeadHoster): +    __name__    = "PotloadCom" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?potload\.com/\w{12}' +    __config__  = [] + +    __description__ = """Potload.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] diff --git a/pyload/plugin/hoster/PremiumTo.py b/pyload/plugin/hoster/PremiumTo.py new file mode 100644 index 000000000..46ecf0b36 --- /dev/null +++ b/pyload/plugin/hoster/PremiumTo.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +from os import remove + +from pyload.plugin.internal.MultiHoster import MultiHoster +from pyload.utils import fs_encode + + +class PremiumTo(MultiHoster): +    __name__    = "PremiumTo" +    __type__    = "hoster" +    __version__ = "0.22" + +    __pattern__ = r'^unmatchable$' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Premium.to multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org"), +                       ("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    CHECK_TRAFFIC = True + + +    def handlePremium(self, pyfile): +        #raise timeout to 2min +        self.download("http://premium.to/api/getfile.php", +                      get={'username': self.account.username, +                           'password': self.account.password, +                           'link'    : pyfile.url}, +                      disposition=True) + + +    def checkFile(self, rules={}): +        if self.checkDownload({'nopremium': "No premium account available"}): +            self.retry(60, 5 * 60, "No premium account available") + +        err = '' +        if self.req.http.code == '420': +            # Custom error code send - fail +            file = fs_encode(self.lastDownload) +            with open(file, "rb") as f: +                err = f.read(256).strip() +            remove(file) + +        if err: +            self.fail(err) + +        return super(PremiumTo, self).checkFile(rules) diff --git a/pyload/plugin/hoster/PremiumizeMe.py b/pyload/plugin/hoster/PremiumizeMe.py new file mode 100644 index 000000000..c6dcfa794 --- /dev/null +++ b/pyload/plugin/hoster/PremiumizeMe.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- + +from pyload.utils import json_loads +from pyload.plugin.internal.MultiHoster import MultiHoster + + +class PremiumizeMe(MultiHoster): +    __name__    = "PremiumizeMe" +    __type__    = "hoster" +    __version__ = "0.16" + +    __pattern__ = r'^unmatchable$'  #: Since we want to allow the user to specify the list of hoster to use we let MultiHoster.activate +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Premiumize.me multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Florian Franzen", "FlorianFranzen@gmail.com")] + + +    def handlePremium(self, pyfile): +        # In some cases hostsers do not supply us with a filename at download, so we +        # are going to set a fall back filename (e.g. for freakshare or xfileshare) +        pyfile.name = pyfile.name.split('/').pop()  # Remove everthing before last slash + +        # Correction for automatic assigned filename: Removing html at end if needed +        suffix_to_remove = ["html", "htm", "php", "php3", "asp", "shtm", "shtml", "cfml", "cfm"] +        temp = pyfile.name.split('.') +        if temp.pop() in suffix_to_remove: +            pyfile.name = ".".join(temp) + +        # Get account data +        user, data = self.account.selectAccount() + +        # Get rewritten link using the premiumize.me api v1 (see https://secure.premiumize.me/?show=api) +        data = json_loads(self.load("https://api.premiumize.me/pm-api/v1.php", +                                    get={'method'       : "directdownloadlink", +                                         'params[login]': user, +                                         'params[pass]' : data['password'], +                                         'params[link]' : pyfile.url})) + +        # Check status and decide what to do +        status = data['status'] + +        if status == 200: +            self.link = data['result']['location'] +            return + +        elif status == 400: +            self.fail(_("Invalid link")) + +        elif status == 404: +            self.offline() + +        elif status >= 500: +            self.tempOffline() + +        else: +            self.fail(data['statusmessage']) diff --git a/pyload/plugin/hoster/PromptfileCom.py b/pyload/plugin/hoster/PromptfileCom.py new file mode 100644 index 000000000..f2e5431ec --- /dev/null +++ b/pyload/plugin/hoster/PromptfileCom.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class PromptfileCom(SimpleHoster): +    __name__    = "PromptfileCom" +    __type__    = "hoster" +    __version__ = "0.13" + +    __pattern__ = r'https?://(?:www\.)?promptfile\.com/' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Promptfile.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("igel", "igelkun@myopera.com")] + + +    INFO_PATTERN = r'<span style=".+?" title=".+?">(?P<N>.*?) \((?P<S>[\d.,]+) (?P<U>[\w^_]+)\)</span>' +    OFFLINE_PATTERN = r'<span style=".+?" title="File Not Found">File Not Found</span>' + +    CHASH_PATTERN = r'<input type="hidden" name="chash" value="(.+?)" />' +    LINK_FREE_PATTERN = r'<a href=\"(.+)\" target=\"_blank\" class=\"view_dl_link\">Download File</a>' + + +    def handleFree(self, pyfile): +        # STAGE 1: get link to continue +        m = re.search(self.CHASH_PATTERN, self.html) +        if m is None: +            self.error(_("CHASH_PATTERN not found")) +        chash = m.group(1) +        self.logDebug("Read chash %s" % chash) +        # continue to stage2 +        self.html = self.load(pyfile.url, decode=True, post={'chash': chash}) + +        # STAGE 2: get the direct link +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.error(_("LINK_FREE_PATTERN not found")) + +        self.link = m.group(1) diff --git a/pyload/plugin/hoster/PrzeklejPl.py b/pyload/plugin/hoster/PrzeklejPl.py new file mode 100644 index 000000000..d6437e82e --- /dev/null +++ b/pyload/plugin/hoster/PrzeklejPl.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class PrzeklejPl(DeadHoster): +    __name__    = "PrzeklejPl" +    __type__    = "hoster" +    __version__ = "0.11" + +    __pattern__ = r'http://(?:www\.)?przeklej\.pl/plik/.+' +    __config__  = [] + +    __description__ = """Przeklej.pl hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/hoster/PutdriveCom.py b/pyload/plugin/hoster/PutdriveCom.py new file mode 100644 index 000000000..562951e81 --- /dev/null +++ b/pyload/plugin/hoster/PutdriveCom.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.hoster.ZeveraCom import ZeveraCom + + +class PutdriveCom(ZeveraCom): +    __name__    = "PutdriveCom" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'https?://(?:www\.)putdrive\.com/(getFiles\.ashx|Members/download\.ashx)\?.*ourl=.+' + +    __description__ = """Multihosters.com multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] diff --git a/pyload/plugin/hoster/QuickshareCz.py b/pyload/plugin/hoster/QuickshareCz.py new file mode 100644 index 000000000..fb6e4e85c --- /dev/null +++ b/pyload/plugin/hoster/QuickshareCz.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class QuickshareCz(SimpleHoster): +    __name__    = "QuickshareCz" +    __type__    = "hoster" +    __version__ = "0.56" + +    __pattern__ = r'http://(?:[^/]*\.)?quickshare\.cz/stahnout-soubor/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Quickshare.cz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN = r'<th width="145px">Název:</th>\s*<td style="word-wrap:break-word;">(?P<N>[^<]+)</td>' +    SIZE_PATTERN = r'<th>Velikost:</th>\s*<td>(?P<S>[\d.,]+) (?P<U>[\w^_]+)</td>' +    OFFLINE_PATTERN = r'<script type="text/javascript">location\.href=\'/chyba\';</script>' + + +    def process(self, pyfile): +        self.html = self.load(pyfile.url, decode=True) +        self.getFileInfo() + +        # parse js variables +        self.jsvars = dict((x, y.strip("'")) for x, y in re.findall(r"var (\w+) = ([\d.]+|'.+?')", self.html)) +        self.logDebug(self.jsvars) +        pyfile.name = self.jsvars['ID3'] + +        # determine download type - free or premium +        if self.premium: +            if 'UU_prihlasen' in self.jsvars: +                if self.jsvars['UU_prihlasen'] == '0': +                    self.logWarning(_("User not logged in")) +                    self.relogin(self.user) +                    self.retry() +                elif float(self.jsvars['UU_kredit']) < float(self.jsvars['kredit_odecet']): +                    self.logWarning(_("Not enough credit left")) +                    self.premium = False + +        if self.premium: +            self.handlePremium(pyfile) +        else: +            self.handleFree(pyfile) + +        if self.checkDownload({"error": re.compile(r"\AChyba!")}, max_size=100): +            self.fail(_("File not m or plugin defect")) + + +    def handleFree(self, pyfile): +        # get download url +        download_url = '%s/download.php' % self.jsvars['server'] +        data = dict((x, self.jsvars[x]) for x in self.jsvars if x in ("ID1", "ID2", "ID3", "ID4")) +        self.logDebug("FREE URL1:" + download_url, data) + +        self.load(download_url, post=data, follow_location=False) +        self.header = self.req.http.header + +        m = re.search(r'Location\s*:\s*(.+)', self.header, re.I) +        if m is None: +            self.fail(_("File not found")) + +        self.link = m.group(1).rstrip()  #@TODO: Remove .rstrip() in 0.4.10 +        self.logDebug("FREE URL2:" + self.link) + +        # check errors +        m = re.search(r'/chyba/(\d+)', self.link) +        if m: +            if m.group(1) == '1': +                self.retry(60, 2 * 60, "This IP is already downloading") +            elif m.group(1) == '2': +                self.retry(60, 60, "No free slots available") +            else: +                self.fail(_("Error %d") % m.group(1)) + + +    def handlePremium(self, pyfile): +        download_url = '%s/download_premium.php' % self.jsvars['server'] +        data = dict((x, self.jsvars[x]) for x in self.jsvars if x in ("ID1", "ID2", "ID4", "ID5")) +        self.download(download_url, get=data) diff --git a/pyload/plugin/hoster/RPNetBiz.py b/pyload/plugin/hoster/RPNetBiz.py new file mode 100644 index 000000000..5e393ecb9 --- /dev/null +++ b/pyload/plugin/hoster/RPNetBiz.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.MultiHoster import MultiHoster +from pyload.utils import json_loads + + +class RPNetBiz(MultiHoster): +    __name__    = "RPNetBiz" +    __type__    = "hoster" +    __version__ = "0.14" + +    __pattern__ = r'https?://.+rpnet\.biz' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """RPNet.biz multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Dman", "dmanugm@gmail.com")] + + +    def setup(self): +        self.chunkLimit = -1 + + +    def handlePremium(self, pyfile): +        user, data = self.account.selectAccount() + +        # Get the download link +        res = self.load("https://premium.rpnet.biz/client_api.php", +                        get={"username": user, +                             "password": data['password'], +                             "action"  : "generate", +                             "links"   : pyfile.url}) + +        self.logDebug("JSON data: %s" % res) +        link_status = json_loads(res)['links'][0]  # get the first link... since we only queried one + +        # Check if we only have an id as a HDD link +        if 'id' in link_status: +            self.logDebug("Need to wait at least 30 seconds before requery") +            self.setWait(30)  # wait for 30 seconds +            self.wait() +            # Lets query the server again asking for the status on the link, +            # we need to keep doing this until we reach 100 +            max_tries = 30 +            my_try = 0 +            while (my_try <= max_tries): +                self.logDebug("Try: %d ; Max Tries: %d" % (my_try, max_tries)) +                res = self.load("https://premium.rpnet.biz/client_api.php", +                                get={"username": user, +                                     "password": data['password'], +                                     "action": "downloadInformation", +                                     "id": link_status['id']}) +                self.logDebug("JSON data hdd query: %s" % res) +                download_status = json_loads(res)['download'] + +                if download_status['status'] == '100': +                    link_status['generated'] = download_status['rpnet_link'] +                    self.logDebug("Successfully downloaded to rpnet HDD: %s" % link_status['generated']) +                    break +                else: +                    self.logDebug("At %s%% for the file download" % download_status['status']) + +                self.setWait(30) +                self.wait() +                my_try += 1 + +            if my_try > max_tries:  # We went over the limit! +                self.fail(_("Waited for about 15 minutes for download to finish but failed")) + +        if 'generated' in link_status: +            self.link = link_status['generated'] +            return +        elif 'error' in link_status: +            self.fail(link_status['error']) +        else: +            self.fail(_("Something went wrong, not supposed to enter here")) diff --git a/pyload/plugin/hoster/RapideoPl.py b/pyload/plugin/hoster/RapideoPl.py new file mode 100644 index 000000000..86dfad5f0 --- /dev/null +++ b/pyload/plugin/hoster/RapideoPl.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- + +from pyload.utils import json_loads +from pyload.plugin.internal.MultiHoster import MultiHoster + + +class RapideoPl(MultiHoster): +    __name__    = "RapideoPl" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'^unmatchable$' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Rapideo.pl multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("goddie", "dev@rapideo.pl")] + + +    API_URL = "http://enc.rapideo.pl" + +    API_QUERY = {'site'    : "newrd", +                 'output'  : "json", +                 'username': "", +                 'password': "", +                 'url'     : ""} + +    ERROR_CODES = {0 : "[%s] Incorrect login credentials", +                   1 : "[%s] Not enough transfer to download - top-up your account", +                   2 : "[%s] Incorrect / dead link", +                   3 : "[%s] Error connecting to hosting, try again later", +                   9 : "[%s] Premium account has expired", +                   15: "[%s] Hosting no longer supported", +                   80: "[%s] Too many incorrect login attempts, account blocked for 24h"} + + +    def prepare(self): +        super(RapideoPl, self).prepare() + +        data = self.account.getAccountData(self.user) + +        self.usr = data['usr'] +        self.pwd = data['pwd'] + + +    def runFileQuery(self, url, mode=None): +        query = self.API_QUERY.copy() + +        query["username"] = self.usr +        query["password"] = self.pwd +        query["url"]      = url + +        if mode == "fileinfo": +            query['check'] = 2 +            query['loc']   = 1 + +        self.logDebug(query) + +        return self.load(self.API_URL, post=query) + + +    def handleFree(self, pyfile): +        try: +            data = self.runFileQuery(pyfile.url, 'fileinfo') + +        except Exception: +            self.logDebug("RunFileQuery error") +            self.tempOffline() + +        try: +            parsed = json_loads(data) + +        except Exception: +            self.logDebug("Loads error") +            self.tempOffline() + +        self.logDebug(parsed) + +        if "errno" in parsed.keys(): +            if parsed["errno"] in self.ERROR_CODES: +                # error code in known +                self.fail(self.ERROR_CODES[parsed["errno"]] % self.__name__) +            else: +                # error code isn't yet added to plugin +                self.fail( +                    parsed["errstring"] +                    or _("Unknown error (code: %s)") % parsed["errno"] +                ) + +        if "sdownload" in parsed: +            if parsed["sdownload"] == "1": +                self.fail( +                    _("Download from %s is possible only using Rapideo.pl website \ +                    directly") % parsed["hosting"]) + +        pyfile.name = parsed["filename"] +        pyfile.size = parsed["filesize"] + +        try: +            self.link = self.runFileQuery(pyfile.url, 'filedownload') + +        except Exception: +            self.logDebug("runFileQuery error #2") +            self.tempOffline() diff --git a/pyload/plugin/hoster/RapidfileshareNet.py b/pyload/plugin/hoster/RapidfileshareNet.py new file mode 100644 index 000000000..1b1895b39 --- /dev/null +++ b/pyload/plugin/hoster/RapidfileshareNet.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class RapidfileshareNet(XFSHoster): +    __name__    = "RapidfileshareNet" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?rapidfileshare\.net/\w{12}' + +    __description__ = """Rapidfileshare.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("guidobelix", "guidobelix@hotmail.it")] + + +    NAME_PATTERN = r'<input type="hidden" name="fname" value="(?P<N>.+?)">' +    SIZE_PATTERN = r'>http://www.rapidfileshare.net/\w+?</font> \((?P<S>[\d.,]+) (?P<U>[\w^_]+)\)</font>' + +    OFFLINE_PATTERN      = r'>No such file with this filename' +    TEMP_OFFLINE_PATTERN = r'The page may have been renamed, removed or be temporarily unavailable.<' diff --git a/pyload/plugin/hoster/RapidgatorNet.py b/pyload/plugin/hoster/RapidgatorNet.py new file mode 100644 index 000000000..ecddd00b3 --- /dev/null +++ b/pyload/plugin/hoster/RapidgatorNet.py @@ -0,0 +1,162 @@ +# -*- coding: utf-8 -*- + +import re + +from pycurl import HTTPHEADER + +from pyload.utils import json_loads +from pyload.network.HTTPRequest import BadHeader +from pyload.plugin.captcha.AdsCaptcha import AdsCaptcha +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.captcha.SolveMedia import SolveMedia +from pyload.plugin.internal.SimpleHoster import SimpleHoster, secondsToMidnight + + +class RapidgatorNet(SimpleHoster): +    __name__    = "RapidgatorNet" +    __type__    = "hoster" +    __version__ = "0.33" + +    __pattern__ = r'http://(?:www\.)?(rapidgator\.net|rg\.to)/file/\w+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Rapidgator.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("chrox", ""), +                       ("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    API_URL = "http://rapidgator.net/api/file" + +    COOKIES = [("rapidgator.net", "lang", "en")] + +    NAME_PATTERN    = r'<title>Download file (?P<N>.*)</title>' +    SIZE_PATTERN    = r'File size:\s*<strong>(?P<S>[\d.,]+) (?P<U>[\w^_]+)</strong>' +    OFFLINE_PATTERN = r'>(File not found|Error 404)' + +    JSVARS_PATTERN = r'\s+var\s*(startTimerUrl|getDownloadUrl|captchaUrl|fid|secs)\s*=\s*\'?(.*?)\'?;' + +    PREMIUM_ONLY_PATTERN = r'You can download files up to|This file can be downloaded by premium only<' +    ERROR_PATTERN        = r'You have reached your (?:daily|hourly) downloads limit' +    WAIT_PATTERN         = r'(Delay between downloads must be not less than|Try again in).+' + +    LINK_FREE_PATTERN = r'return \'(http://\w+.rapidgator.net/.*)\';' + +    RECAPTCHA_PATTERN  = r'"http://api\.recaptcha\.net/challenge\?k=(.*?)"' +    ADSCAPTCHA_PATTERN = r'(http://api\.adscaptcha\.com/Get\.aspx[^"\']+)' +    SOLVEMEDIA_PATTERN = r'http://api\.solvemedia\.com/papi/challenge\.script\?k=(.*?)"' + + +    def setup(self): +        if self.account: +            self.sid = self.account.getAccountInfo(self.user).get('sid', None) +        else: +            self.sid = None + +        if self.sid: +            self.premium = True + +        self.resumeDownload = self.multiDL = self.premium +        self.chunkLimit     = 1 + + +    def api_response(self, cmd): +        try: +            json = self.load('%s/%s' % (self.API_URL, cmd), +                             get={'sid': self.sid, +                                  'url': self.pyfile.url}, decode=True) +            self.logDebug("API:%s" % cmd, json, "SID: %s" % self.sid) +            json = json_loads(json) +            status = json['response_status'] +            msg = json['response_details'] + +        except BadHeader, e: +            self.logError("API: %s" % cmd, e, "SID: %s" % self.sid) +            status = e.code +            msg = e + +        if status == 200: +            return json['response'] + +        elif status == 423: +            self.account.empty(self.user) +            self.retry() + +        else: +            self.account.relogin(self.user) +            self.retry(wait_time=60) + + +    def handlePremium(self, pyfile): +        self.api_data = self.api_response('info') +        self.api_data['md5'] = self.api_data['hash'] + +        pyfile.name = self.api_data['filename'] +        pyfile.size = self.api_data['size'] + +        self.link = self.api_response('download')['url'] + + +    def handleFree(self, pyfile): +        jsvars = dict(re.findall(self.JSVARS_PATTERN, self.html)) +        self.logDebug(jsvars) + +        self.req.http.lastURL = pyfile.url +        self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With: XMLHttpRequest"]) + +        url = "http://rapidgator.net%s?fid=%s" % ( +            jsvars.get('startTimerUrl', '/download/AjaxStartTimer'), jsvars['fid']) +        jsvars.update(self.getJsonResponse(url)) + +        self.wait(jsvars.get('secs', 45), False) + +        url = "http://rapidgator.net%s?sid=%s" % ( +            jsvars.get('getDownloadUrl', '/download/AjaxGetDownload'), jsvars['sid']) +        jsvars.update(self.getJsonResponse(url)) + +        self.req.http.lastURL = pyfile.url +        self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With:"]) + +        url = "http://rapidgator.net%s" % jsvars.get('captchaUrl', '/download/captcha') +        self.html = self.load(url) + +        for _i in xrange(5): +            m = re.search(self.LINK_FREE_PATTERN, self.html) +            if m: +                self.link = m.group(1) +                break +            else: +                captcha = self.handleCaptcha() + +                if not captcha: +                    self.error(_("Captcha pattern not found")) + +                response, challenge  = captcha.challenge() + +                self.html = self.load(url, post={'DownloadCaptchaForm[captcha]': "", +                                                 'adcopy_challenge'            : challenge, +                                                 'adcopy_response'             : response}) + +                if "The verification code is incorrect" in self.html: +                    self.invalidCaptcha() +                else: +                    self.correctCaptcha() +        else: +            self.error(_("Download link")) + + +    def handleCaptcha(self): +        for klass in (AdsCaptcha, ReCaptcha, SolveMedia): +            inst = klass(self) +            if inst.detect_key(): +                return inst + + +    def getJsonResponse(self, url): +        res = self.load(url, decode=True) +        if not res.startswith('{'): +            self.retry() +        self.logDebug(url, res) +        return json_loads(res) diff --git a/pyload/plugin/hoster/RapiduNet.py b/pyload/plugin/hoster/RapiduNet.py new file mode 100644 index 000000000..ef2a3bbe2 --- /dev/null +++ b/pyload/plugin/hoster/RapiduNet.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pycurl import HTTPHEADER + +from pyload.utils import json_loads +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class RapiduNet(SimpleHoster): +    __name__    = "RapiduNet" +    __type__    = "hoster" +    __version__ = "0.08" + +    __pattern__ = r'https?://(?:www\.)?rapidu\.net/(?P<ID>\d{10})' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Rapidu.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("prOq", "")] + + +    COOKIES = [("rapidu.net", "rapidu_lang", "en")] + +    INFO_PATTERN    = r'<h1 title="(?P<N>.*)">.*</h1>\s*<small>(?P<S>\d+(\.\d+)?)\s(?P<U>\w+)</small>' +    OFFLINE_PATTERN = r'<h1>404' + +    ERROR_PATTERN = r'<div class="error">' + +    RECAPTCHA_KEY = r'6Ld12ewSAAAAAHoE6WVP_pSfCdJcBQScVweQh8Io' + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = self.premium + + +    def handleFree(self, pyfile): +        self.req.http.lastURL = pyfile.url +        self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With: XMLHttpRequest"]) + +        jsvars = self.getJsonResponse("https://rapidu.net/ajax.php", +                                      get={'a': "getLoadTimeToDownload"}, +                                      post={'_go': ""}, +                                      decode=True) + +        if str(jsvars['timeToDownload']) is "stop": +            t = (24 * 60 * 60) - (int(time.time()) % (24 * 60 * 60)) + time.altzone + +            self.logInfo("You've reach your daily download transfer") + +            self.retry(10, 10 if t < 1 else None, _("Try tomorrow again"))  #@NOTE: check t in case of not synchronised clock + +        else: +            self.wait(int(jsvars['timeToDownload']) - int(time.time())) + +        recaptcha = ReCaptcha(self) +        response, challenge = recaptcha.challenge(self.RECAPTCHA_KEY) + +        jsvars = self.getJsonResponse("https://rapidu.net/ajax.php", +                                      get={'a': "getCheckCaptcha"}, +                                      post={'_go'     : "", +                                            'captcha1': challenge, +                                            'captcha2': response, +                                            'fileId'  : self.info['pattern']['ID']}, +                                      decode=True) + +        if jsvars['message'] == 'success': +            self.link = jsvars['url'] + + +    def getJsonResponse(self, *args, **kwargs): +        res = self.load(*args, **kwargs) +        if not res.startswith('{'): +            self.retry() + +        self.logDebug(res) + +        return json_loads(res) diff --git a/pyload/plugin/hoster/RarefileNet.py b/pyload/plugin/hoster/RarefileNet.py new file mode 100644 index 000000000..1010c92ca --- /dev/null +++ b/pyload/plugin/hoster/RarefileNet.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class RarefileNet(XFSHoster): +    __name__    = "RarefileNet" +    __type__    = "hoster" +    __version__ = "0.09" + +    __pattern__ = r'http://(?:www\.)?rarefile\.net/\w{12}' + +    __description__ = """Rarefile.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    LINK_PATTERN = r'<a href="(.+?)">\1</a>' diff --git a/pyload/plugin/hoster/RealdebridCom.py b/pyload/plugin/hoster/RealdebridCom.py new file mode 100644 index 000000000..02e242d72 --- /dev/null +++ b/pyload/plugin/hoster/RealdebridCom.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from random import randrange +from urllib import unquote + +from pyload.utils import json_loads +from pyload.plugin.internal.MultiHoster import MultiHoster +from pyload.utils import parseFileSize + + +class RealdebridCom(MultiHoster): +    __name__    = "RealdebridCom" +    __type__    = "hoster" +    __version__ = "0.67" + +    __pattern__ = r'https?://((?:www\.|s\d+\.)?real-debrid\.com/dl/|[\w^_]\.rdb\.so/d/)[\w^_]+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Real-Debrid.com multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Devirex Hazzard", "naibaf_11@yahoo.de")] + + +    def setup(self): +        self.chunkLimit = 3 + + +    def handlePremium(self, pyfile): +        data = json_loads(self.load("https://real-debrid.com/ajax/unrestrict.php", +                                    get={'lang'    : "en", +                                         'link'    : pyfile.url, +                                         'password': self.getPassword(), +                                         'time'    : int(time.time() * 1000)})) + +        self.logDebug("Returned Data: %s" % data) + +        if data['error'] != 0: +            if data['message'] == "Your file is unavailable on the hoster.": +                self.offline() +            else: +                self.logWarning(data['message']) +                self.tempOffline() +        else: +            if pyfile.name and pyfile.name.endswith('.tmp') and data['file_name']: +                pyfile.name = data['file_name'] +            pyfile.size = parseFileSize(data['file_size']) +            self.link = data['generated_links'][0][-1] + +        if self.getConfig('ssl'): +            self.link = self.link.replace("http://", "https://") +        else: +            self.link = self.link.replace("https://", "http://") + + diff --git a/pyload/plugin/hoster/RedtubeCom.py b/pyload/plugin/hoster/RedtubeCom.py new file mode 100644 index 000000000..f6bc3f825 --- /dev/null +++ b/pyload/plugin/hoster/RedtubeCom.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Hoster import Hoster +from pyload.utils import html_unescape + + +class RedtubeCom(Hoster): +    __name__    = "RedtubeCom" +    __type__    = "hoster" +    __version__ = "0.20" + +    __pattern__ = r'http://(?:www\.)?redtube\.com/\d+' + +    __description__ = """Redtube.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de")] + + +    def process(self, pyfile): +        self.download_html() +        if not self.file_exists(): +            self.offline() + +        pyfile.name = self.get_file_name() +        self.download(self.get_file_url()) + + +    def download_html(self): +        url = self.pyfile.url +        self.html = self.load(url) + + +    def get_file_url(self): +        """ returns the absolute downloadable filepath +        """ +        if not self.html: +            self.download_html() + +        file_url = html_unescape(re.search(r'hashlink=(http.*?)"', self.html).group(1)) + +        return file_url + + +    def get_file_name(self): +        if not self.html: +            self.download_html() + +        return re.search('<title>(.*?)- RedTube - Free Porn Videos</title>', self.html).group(1).strip() + ".flv" + + +    def file_exists(self): +        """ returns True or False +        """ +        if not self.html: +            self.download_html() + +        if re.search(r'This video has been removed.', self.html): +            return False +        else: +            return True diff --git a/pyload/plugin/hoster/RehostTo.py b/pyload/plugin/hoster/RehostTo.py new file mode 100644 index 000000000..36bdb54b5 --- /dev/null +++ b/pyload/plugin/hoster/RehostTo.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +from urllib import unquote + +from pyload.plugin.internal.MultiHoster import MultiHoster + + +class RehostTo(MultiHoster): +    __name__    = "RehostTo" +    __type__    = "hoster" +    __version__ = "0.21" + +    __pattern__ = r'https?://.*rehost\.to\..+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Rehost.com multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org")] + + +    def handlePremium(self, pyfile): +        self.download("http://rehost.to/process_download.php", +                      get={'user': "cookie", +                           'pass': self.account.getAccountInfo(self.user)['session'], +                           'dl'  : pyfile.url}, +                      disposition=True) diff --git a/pyload/plugin/hoster/RemixshareCom.py b/pyload/plugin/hoster/RemixshareCom.py new file mode 100644 index 000000000..ffaef3f38 --- /dev/null +++ b/pyload/plugin/hoster/RemixshareCom.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://remixshare.com/download/z8uli +# +# Note: +# The remixshare.com website is very very slow, so +# if your download not starts because of pycurl timeouts: +# Adjust timeouts in /usr/share/pyload/pyload/network/HTTPRequest.py + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class RemixshareCom(SimpleHoster): +    __name__    = "RemixshareCom" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'https?://remixshare\.com/(download|dl)/\w+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Remixshare.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de"  ), +                       ("Walter Purcaro", "vuolter@gmail.com"     ), +                       ("sraedler"      , "simon.raedler@yahoo.de")] + + +    INFO_PATTERN    = r'title=\'.+?\'>(?P<N>.+?)</span><span class=\'light2\'> \((?P<S>\d+) (?P<U>[\w^_]+)\)<' +    HASHSUM_PATTERN = r'>(?P<T>MD5): (?P<H>\w+)' +    OFFLINE_PATTERN = r'<h1>Ooops!' + +    LINK_PATTERN  = r'var uri = "(.+?)"' +    TOKEN_PATTERN = r'var acc = (\d+)' + +    WAIT_PATTERN = r'var XYZ = "(\d+)"' + + +    def setup(self): +        self.multiDL = True +        self.chunkLimit = 1 + + +    def handleFree(self, pyfile): +        b = re.search(self.LINK_PATTERN, self.html) +        if not b: +            self.error(_("File url")) + +        c = re.search(self.TOKEN_PATTERN, self.html) +        if not c: +            self.error(_("File token")) + +        self.link = b.group(1) + "/zzz/" + c.group(1) + diff --git a/pyload/plugin/hoster/RgHostNet.py b/pyload/plugin/hoster/RgHostNet.py new file mode 100644 index 000000000..0e7d3de46 --- /dev/null +++ b/pyload/plugin/hoster/RgHostNet.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class RgHostNet(SimpleHoster): +    __name__    = "RgHostNet" +    __type__    = "hoster" +    __version__ = "0.04" + +    __pattern__ = r'http://(?:www\.)?rghost\.(net|ru)/[\d-]+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """RgHost.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("z00nx", "z00nx0@gmail.com")] + + +    INFO_PATTERN    = r'data-share42-text="(?P<N>.+?) \((?P<S>[\d.,]+) (?P<U>[\w^_]+)' +    HASHSUM_PATTERN = r'<dt>(?P<T>\w+)</dt>\s*<dd>(?P<H>\w+)' +    OFFLINE_PATTERN = r'>(File is deleted|page not found)' + +    LINK_FREE_PATTERN = r'<a href="(.+?)" class="btn large' + diff --git a/pyload/plugin/hoster/SafesharingEu.py b/pyload/plugin/hoster/SafesharingEu.py new file mode 100644 index 000000000..af046af62 --- /dev/null +++ b/pyload/plugin/hoster/SafesharingEu.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class SafesharingEu(XFSHoster): +    __name__    = "SafesharingEu" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'https?://(?:www\.)?safesharing\.eu/\w{12}' + +    __description__ = """Safesharing.eu hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de")] + + +    ERROR_PATTERN = r'(?:<div class="alert alert-danger">)(.+?)(?:</div>)' diff --git a/pyload/plugin/hoster/SecureUploadEu.py b/pyload/plugin/hoster/SecureUploadEu.py new file mode 100644 index 000000000..4db413c90 --- /dev/null +++ b/pyload/plugin/hoster/SecureUploadEu.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class SecureUploadEu(XFSHoster): +    __name__    = "SecureUploadEu" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'https?://(?:www\.)?secureupload\.eu/\w{12}' + +    __description__ = """SecureUpload.eu hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("z00nx", "z00nx0@gmail.com")] + + +    INFO_PATTERN = r'<h3>Downloading (?P<N>[^<]+) \((?P<S>[^<]+)\)</h3>' diff --git a/pyload/plugin/hoster/SendspaceCom.py b/pyload/plugin/hoster/SendspaceCom.py new file mode 100644 index 000000000..1921317c5 --- /dev/null +++ b/pyload/plugin/hoster/SendspaceCom.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class SendspaceCom(SimpleHoster): +    __name__    = "SendspaceCom" +    __type__    = "hoster" +    __version__ = "0.17" + +    __pattern__ = r'https?://(?:www\.)?sendspace\.com/file/\w+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Sendspace.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN    = r'<h2 class="bgray">\s*<(?:b|strong)>(?P<N>[^<]+)</' +    SIZE_PATTERN    = r'<div class="file_description reverse margin_center">\s*<b>File Size:</b>\s*(?P<S>[\d.,]+)(?P<U>[\w^_]+)\s*</div>' +    OFFLINE_PATTERN = r'<div class="msg error" style="cursor: default">Sorry, the file you requested is not available.</div>' + +    LINK_FREE_PATTERN = r'<a id="download_button" href="(.+?)"' + +    CAPTCHA_PATTERN      = r'<td><img src="(/captchas/captcha\.php?captcha=(.+?))"></td>' +    USER_CAPTCHA_PATTERN = r'<td><img src="/captchas/captcha\.php?user=(.+?))"></td>' + + +    def handleFree(self, pyfile): +        params = {} +        for _i in xrange(3): +            m = re.search(self.LINK_FREE_PATTERN, self.html) +            if m: +                if 'captcha_hash' in params: +                    self.correctCaptcha() +                self.link = m.group(1) +                break + +            m = re.search(self.CAPTCHA_PATTERN, self.html) +            if m: +                if 'captcha_hash' in params: +                    self.invalidCaptcha() +                captcha_url1 = "http://www.sendspace.com/" + m.group(1) +                m = re.search(self.USER_CAPTCHA_PATTERN, self.html) +                captcha_url2 = "http://www.sendspace.com/" + m.group(1) +                params = {'captcha_hash': m.group(2), +                          'captcha_submit': 'Verify', +                          'captcha_answer': self.decryptCaptcha(captcha_url1) + " " + self.decryptCaptcha(captcha_url2)} +            else: +                params = {'download': "Regular Download"} + +            self.logDebug(params) +            self.html = self.load(pyfile.url, post=params) +        else: +            self.fail(_("Download link not found")) + diff --git a/pyload/plugin/hoster/Share4WebCom.py b/pyload/plugin/hoster/Share4WebCom.py new file mode 100644 index 000000000..f2b4e49e9 --- /dev/null +++ b/pyload/plugin/hoster/Share4WebCom.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.hoster.UnibytesCom import UnibytesCom + + +class Share4WebCom(UnibytesCom): +    __name__    = "Share4WebCom" +    __type__    = "hoster" +    __version__ = "0.11" + +    __pattern__ = r'https?://(?:www\.)?share4web\.com/get/\w+' + +    __description__ = """Share4web.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    HOSTER_DOMAIN = "share4web.com" diff --git a/pyload/plugin/hoster/Share76Com.py b/pyload/plugin/hoster/Share76Com.py new file mode 100644 index 000000000..e826b4e2d --- /dev/null +++ b/pyload/plugin/hoster/Share76Com.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class Share76Com(DeadHoster): +    __name__    = "Share76Com" +    __type__    = "hoster" +    __version__ = "0.04" + +    __pattern__ = r'http://(?:www\.)?share76\.com/\w{12}' +    __config__  = [] + +    __description__ = """Share76.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [] diff --git a/pyload/plugin/hoster/ShareFilesCo.py b/pyload/plugin/hoster/ShareFilesCo.py new file mode 100644 index 000000000..946b6a423 --- /dev/null +++ b/pyload/plugin/hoster/ShareFilesCo.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class ShareFilesCo(DeadHoster): +    __name__    = "ShareFilesCo" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?sharefiles\.co/\w{12}' +    __config__  = [] + +    __description__ = """Sharefiles.co hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] diff --git a/pyload/plugin/hoster/SharebeesCom.py b/pyload/plugin/hoster/SharebeesCom.py new file mode 100644 index 000000000..405fab050 --- /dev/null +++ b/pyload/plugin/hoster/SharebeesCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class SharebeesCom(DeadHoster): +    __name__    = "SharebeesCom" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?sharebees\.com/\w{12}' +    __config__  = [] + +    __description__ = """ShareBees hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/hoster/ShareonlineBiz.py b/pyload/plugin/hoster/ShareonlineBiz.py new file mode 100644 index 000000000..840421a67 --- /dev/null +++ b/pyload/plugin/hoster/ShareonlineBiz.py @@ -0,0 +1,182 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from urllib import unquote +from urlparse import urlparse + +from pyload.network.RequestFactory import getURL +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class ShareonlineBiz(SimpleHoster): +    __name__    = "ShareonlineBiz" +    __type__    = "hoster" +    __version__ = "0.49" + +    __pattern__ = r'https?://(?:www\.)?(share-online\.biz|egoshare\.com)/(download\.php\?id=|dl/)(?P<ID>\w+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Shareonline.biz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org"), +                       ("mkaay", "mkaay@mkaay.de"), +                       ("zoidberg", "zoidberg@mujmail.cz"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    URL_REPLACEMENTS = [(__pattern__ + ".*", "http://www.share-online.biz/dl/\g<ID>")] + +    CHECK_TRAFFIC = True + +    RECAPTCHA_KEY = "6LdatrsSAAAAAHZrB70txiV5p-8Iv8BtVxlTtjKX" + +    ERROR_PATTERN = r'<p class="b">Information:</p>\s*<div>\s*<strong>(.*?)</strong>' + + +    @classmethod +    def getInfo(cls, url="", html=""): +        info = {'name': urlparse(unquote(url)).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 3 if url else 1, 'url': url} + +        if url: +            info['pattern'] = re.match(cls.__pattern__, url).groupdict() + +            field = getURL("http://api.share-online.biz/linkcheck.php", +                           get={'md5': "1"}, +                           post={'links': info['pattern']['ID']}, +                           decode=True).split(";") + +            if field[1] == "OK": +                info['fileid']   = field[0] +                info['status']   = 2 +                info['name']     = field[2] +                info['size']     = field[3]  #: in bytes +                info['md5']      = field[4].strip().lower().replace("\n\n", "")  #: md5 + +            elif field[1] in ("DELETED", "NOT FOUND"): +                info['status'] = 1 + +        return info + + +    def setup(self): +        self.resumeDownload = self.premium +        self.multiDL        = False + + +    def handleCaptcha(self): +        recaptcha = ReCaptcha(self) + +        for _i in xrange(5): +            response, challenge = recaptcha.challenge(self.RECAPTCHA_KEY) + +            m = re.search(r'var wait=(\d+);', self.html) +            self.setWait(int(m.group(1)) if m else 30) + +            res = self.load("%s/free/captcha/%d" % (self.pyfile.url, int(time.time() * 1000)), +                            post={'dl_free'                  : "1", +                                  'recaptcha_challenge_field': challenge, +                                  'recaptcha_response_field' : response}) +            if not res == '0': +                self.correctCaptcha() +                return res +            else: +                self.invalidCaptcha() +        else: +            self.invalidCaptcha() +            self.fail(_("No valid captcha solution received")) + + +    def handleFree(self, pyfile): +        self.wait(3) + +        self.html = self.load("%s/free/" % pyfile.url, +                              post={'dl_free': "1", 'choice': "free"}, +                              decode=True) + +        self.checkErrors() + +        res = self.handleCaptcha() +        self.link = res.decode('base64') + +        if not self.link.startswith("http://"): +            self.error(_("Wrong download url")) + +        self.wait() + + +    def checkFile(self, rules={}): +        check = self.checkDownload({'cookie': re.compile(r'<div id="dl_failure"'), +                                    'fail'  : re.compile(r"<title>Share-Online")}) + +        if check == "cookie": +            self.invalidCaptcha() +            self.retry(5, 60, _("Cookie failure")) + +        elif check == "fail": +            self.invalidCaptcha() +            self.retry(5, 5 * 60, _("Download failed")) + +        return super(ShareonlineBiz, self).checkFile(rules) + + +    def handlePremium(self, pyfile):  #: should be working better loading (account) api internally +        html = self.load("http://api.share-online.biz/account.php", +                         get={'username': self.user, +                              'password': self.account.getAccountData(self.user)['password'], +                              'act'     : "download", +                              'lid'     : self.info['fileid']}) + +        self.api_data = dlinfo = {} + +        for line in html.splitlines(): +            key, value = line.split(": ") +            dlinfo[key.lower()] = value + +        self.logDebug(dlinfo) + +        if not dlinfo['status'] == "online": +            self.offline() +        else: +            pyfile.name = dlinfo['name'] +            pyfile.size = int(dlinfo['size']) + +            self.link = dlinfo['url'] + +            if self.link == "server_under_maintenance": +                self.tempOffline() +            else: +                self.multiDL = True + + +    def checkErrors(self): +        m = re.search(r"/failure/(.*?)/1", self.req.lastEffectiveURL) +        if m is None: +            self.info.pop('error', None) +            return + +        errmsg = m.group(1).lower() + +        try: +            self.logError(errmsg, re.search(self.ERROR_PATTERN, self.html).group(1)) +        except Exception: +            self.logError("Unknown error occurred", errmsg) + +        if errmsg is "invalid": +            self.fail(_("File not available")) + +        elif errmsg in ("freelimit", "size", "proxy"): +            self.fail(_("Premium account needed")) + +        elif errmsg in ("expired", "server"): +            self.retry(wait_time=600, reason=errmsg) + +        elif 'slot' in errmsg: +            self.wantReconnect = True +            self.retry(24, 3600, errmsg) + +        else: +            self.wantReconnect = True +            self.retry(wait_time=60, reason=errmsg) diff --git a/pyload/plugin/hoster/ShareplaceCom.py b/pyload/plugin/hoster/ShareplaceCom.py new file mode 100644 index 000000000..35f74d460 --- /dev/null +++ b/pyload/plugin/hoster/ShareplaceCom.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- + +import re + +from urllib import unquote + +from pyload.plugin.Hoster import Hoster + + +class ShareplaceCom(Hoster): +    __name__    = "ShareplaceCom" +    __type__    = "hoster" +    __version__ = "0.12" + +    __pattern__ = r'http://(?:www\.)?shareplace\.(com|org)/\?\w+' + +    __description__ = """Shareplace.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("ACCakut", "")] + + +    def process(self, pyfile): +        self.pyfile = pyfile +        self.prepare() +        self.download(self.get_file_url()) + + +    def prepare(self): +        if not self.file_exists(): +            self.offline() + +        self.pyfile.name = self.get_file_name() + +        wait_time = self.get_waiting_time() +        self.setWait(wait_time) +        self.wait() + + +    def get_waiting_time(self): +        if not self.html: +            self.download_html() + +        #var zzipitime = 15; +        m = re.search(r'var zzipitime = (\d+);', self.html) +        if m: +            sec = int(m.group(1)) +        else: +            sec = 0 + +        return sec + + +    def download_html(self): +        url = re.sub("shareplace.com\/\?", "shareplace.com//index1.php/?a=", self.pyfile.url) +        self.html = self.load(url, decode=True) + + +    def get_file_url(self): +        """ returns the absolute downloadable filepath +        """ +        url = re.search(r"var beer = '(.*?)';", self.html) +        if url: +            url = url.group(1) +            url = unquote( +                url.replace("http://http:/", "").replace("vvvvvvvvv", "").replace("lllllllll", "").replace( +                    "teletubbies", "")) +            self.logDebug("URL: %s" % url) +            return url +        else: +            self.error(_("Absolute filepath not found")) + + +    def get_file_name(self): +        if not self.html: +            self.download_html() + +        return re.search("<title>\s*(.*?)\s*</title>", self.html).group(1) + + +    def file_exists(self): +        """ returns True or False +        """ +        if not self.html: +            self.download_html() + +        if re.search(r"HTTP Status 404", self.html): +            return False +        else: +            return True diff --git a/pyload/plugin/hoster/SharingmatrixCom.py b/pyload/plugin/hoster/SharingmatrixCom.py new file mode 100644 index 000000000..81c371c98 --- /dev/null +++ b/pyload/plugin/hoster/SharingmatrixCom.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class SharingmatrixCom(DeadHoster): +    __name__    = "SharingmatrixCom" +    __type__    = "hoster" +    __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?sharingmatrix\.com/file/\w+' +    __config__  = [] + +    __description__ = """Sharingmatrix.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("paulking", "")] diff --git a/pyload/plugin/hoster/ShragleCom.py b/pyload/plugin/hoster/ShragleCom.py new file mode 100644 index 000000000..7f06e2424 --- /dev/null +++ b/pyload/plugin/hoster/ShragleCom.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class ShragleCom(DeadHoster): +    __name__    = "ShragleCom" +    __type__    = "hoster" +    __version__ = "0.22" + +    __pattern__ = r'http://(?:www\.)?(cloudnator|shragle)\.com/files/(?P<ID>.+?)/' +    __config__  = [] + +    __description__ = """Cloudnator.com (Shragle.com) hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org"), +                       ("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/hoster/SimplyPremiumCom.py b/pyload/plugin/hoster/SimplyPremiumCom.py new file mode 100644 index 000000000..83640bcd5 --- /dev/null +++ b/pyload/plugin/hoster/SimplyPremiumCom.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.MultiHoster import MultiHoster +from pyload.plugin.internal.SimpleHoster import secondsToMidnight + + +class SimplyPremiumCom(MultiHoster): +    __name__    = "SimplyPremiumCom" +    __type__    = "hoster" +    __version__ = "0.08" + +    __pattern__ = r'https?://.+simply-premium\.com' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Simply-Premium.com multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("EvolutionClip", "evolutionclip@live.de")] + + +    def setup(self): +        self.chunkLimit = 16 + + +    def checkErrors(self): +        if '<valid>0</valid>' in self.html or ( +                "You are not allowed to download from this host" in self.html and self.premium): +            self.account.relogin(self.user) +            self.retry() + +        elif "NOTFOUND" in self.html: +            self.offline() + +        elif "downloadlimit" in self.html: +            self.logWarning(_("Reached maximum connctions")) +            self.retry(5, 60, _("Reached maximum connctions")) + +        elif "trafficlimit" in self.html: +            self.logWarning(_("Reached daily limit for this host")) +            self.retry(wait_time=secondsToMidnight(gmt=2), reason="Daily limit for this host reached") + +        elif "hostererror" in self.html: +            self.logWarning(_("Hoster temporarily unavailable, waiting 1 minute and retry")) +            self.retry(5, 60, _("Hoster is temporarily unavailable")) + + +    def handlePremium(self, pyfile): +        for i in xrange(5): +            self.html = self.load("http://www.simply-premium.com/premium.php", get={'info': "", 'link': self.pyfile.url}) + +            if self.html: +                self.logDebug("JSON data: " + self.html) +                break +        else: +            self.logInfo(_("Unable to get API data, waiting 1 minute and retry")) +            self.retry(5, 60, _("Unable to get API data")) + +        self.checkErrors() + +        try: +            self.pyfile.name = re.search(r'<name>([^<]+)</name>', self.html).group(1) + +        except AttributeError: +            self.pyfile.name = "" + +        try: +            self.pyfile.size = re.search(r'<size>(\d+)</size>', self.html).group(1) + +        except AttributeError: +            self.pyfile.size = 0 + +        try: +            self.link = re.search(r'<download>([^<]+)</download>', self.html).group(1) + +        except AttributeError: +            self.link = 'http://www.simply-premium.com/premium.php?link=' + self.pyfile.url diff --git a/pyload/plugin/hoster/SimplydebridCom.py b/pyload/plugin/hoster/SimplydebridCom.py new file mode 100644 index 000000000..0605bb4f3 --- /dev/null +++ b/pyload/plugin/hoster/SimplydebridCom.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.MultiHoster import MultiHoster, replace_patterns + + +class SimplydebridCom(MultiHoster): +    __name__    = "SimplydebridCom" +    __type__    = "hoster" +    __version__ = "0.17" + +    __pattern__ = r'http://\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/sd\.php' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Simply-debrid.com multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Kagenoshin", "kagenoshin@gmx.ch")] + + +    def handlePremium(self, pyfile): +        #fix the links for simply-debrid.com! +        self.link = replace_patterns(pyfile.url, [("clz.to", "cloudzer.net/file") +                                                  ("http://share-online", "http://www.share-online") +                                                  ("ul.to", "uploaded.net/file") +                                                  ("uploaded.com", "uploaded.net") +                                                  ("filerio.com", "filerio.in") +                                                  ("lumfile.com", "lumfile.se")]) + +        if 'fileparadox' in self.link: +            self.link = self.link.replace("http://", "https://") + +        self.html = self.load("http://simply-debrid.com/api.php", get={'dl': self.link}) +        if 'tiger Link' in self.html or 'Invalid Link' in self.html or ('API' in self.html and 'ERROR' in self.html): +            self.error(_("Unable to unrestrict link")) + +        self.link = self.html + +        self.wait(5) + + +    def checkFile(self, rules={}): +        if self.checkDownload({"error": "No address associated with hostname"}): +            self.retry(24, 3 * 60, _("Bad file downloaded")) + +        return super(SimplydebridCom, self).checkFile(rules) diff --git a/pyload/plugin/hoster/SmoozedCom.py b/pyload/plugin/hoster/SmoozedCom.py new file mode 100644 index 000000000..b321fee03 --- /dev/null +++ b/pyload/plugin/hoster/SmoozedCom.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- + +from pyload.utils import json_loads +from pyload.plugin.internal.MultiHoster import MultiHoster + + +class SmoozedCom(MultiHoster): +    __name__    = "SmoozedCom" +    __type__    = "hoster" +    __version__ = "0.04" + +    __pattern__ = r'^unmatchable$'  #: Since we want to allow the user to specify the list of hoster to use we let MultiHoster.activate +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Smoozed.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("", "")] + + +    def handlePremium(self, pyfile): +        # In some cases hostsers do not supply us with a filename at download, so we +        # are going to set a fall back filename (e.g. for freakshare or xfileshare) +        pyfile.name = pyfile.name.split('/').pop()  # Remove everthing before last slash + +        # Correction for automatic assigned filename: Removing html at end if needed +        suffix_to_remove = ["html", "htm", "php", "php3", "asp", "shtm", "shtml", "cfml", "cfm"] +        temp             = pyfile.name.split('.') + +        if temp.pop() in suffix_to_remove: +            pyfile.name = ".".join(temp) + +        # Check the link +        get_data = {'session_key': self.account.getAccountInfo(self.user)['session'], +                    'url'        : pyfile.url} + +        data = json_loads(self.load("http://www2.smoozed.com/api/check", get=get_data)) + +        if data["state"] != "ok": +            self.fail(data["message"]) + +        if data["data"].get("state", "ok") != "ok": +            if data["data"] == "Offline": +                self.offline() +            else: +                self.fail(data["data"]["message"]) + +        pyfile.name = data["data"]["name"] +        pyfile.size = int(data["data"]["size"]) + +        # Start the download +        header = self.load("http://www2.smoozed.com/api/download", get=get_data, just_header=True) + +        if not "location" in header: +            self.fail(_("Unable to initialize download")) +        else: +            self.link = header["location"][-1] if isinstance(header["location"], list) else header["location"] + + +    def checkFile(self, rules={}): +        if self.checkDownload({'error': '{"state":"error"}', +                               'retry': '{"state":"retry"}'}): +            self.fail(_("Error response received")) + +        return super(SmoozedCom, self).checkFile(rules) diff --git a/pyload/plugin/hoster/SockshareCom.py b/pyload/plugin/hoster/SockshareCom.py new file mode 100644 index 000000000..3881278e1 --- /dev/null +++ b/pyload/plugin/hoster/SockshareCom.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class SockshareCom(DeadHoster): +    __name__    = "SockshareCom" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?sockshare\.com/(mobile/)?(file|embed)/(?P<ID>\w+)' +    __config__  = [] + +    __description__ = """Sockshare.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] diff --git a/pyload/plugin/hoster/SoundcloudCom.py b/pyload/plugin/hoster/SoundcloudCom.py new file mode 100644 index 000000000..524fb6e13 --- /dev/null +++ b/pyload/plugin/hoster/SoundcloudCom.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster +from pyload.utils import json_loads + + +class SoundcloudCom(SimpleHoster): +    __name__    = "SoundcloudCom" +    __type__    = "hoster" +    __version__ = "0.11" + +    __pattern__ = r'https?://(?:www\.)?soundcloud\.com/[\w-]+/[\w-]+' +    __config__  = [("use_premium", "bool"        , "Use premium account if available", True    ), +                   ("quality"    , "Lower;Higher", "Quality"                         , "Higher")] + +    __description__ = """SoundCloud.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    NAME_PATTERN    = r'title" content="(?P<N>.+?)"' +    OFFLINE_PATTERN = r'<title>"SoundCloud - Hear the worldâs sounds"</title>' + + +    def handleFree(self, pyfile): +        try: +            song_id = re.search(r'sounds:(\d+)"', self.html).group(1) + +        except Exception: +            self.error(_("Could not find song id")) + +        try: +            client_id = re.search(r'"clientID":"(.+?)"', self.html).group(1) + +        except Exception: +            client_id = "b45b1aa10f1ac2941910a7f0d10f8e28" + +        # url to retrieve the actual song url +        streams = json_loads(self.load("https://api.soundcloud.com/tracks/%s/streams" % song_id, +                             get={'client_id': client_id})) + +        regex = re.compile(r'[^\d]') +        http_streams = sorted([(key, value) for key, value in streams.iteritems() if key.startswith('http_')], +                              key=lambda t: regex.sub(t[0], ''), +                              reverse=True) + +        self.logDebug("Streams found: %s" % (http_streams or "None")) + +        if http_streams: +            stream_name, self.link = http_streams[0 if self.getConfig('quality') == "Higher" else -1] +            pyfile.name += '.' + stream_name.split('_')[1].lower() diff --git a/pyload/plugin/hoster/SpeedLoadOrg.py b/pyload/plugin/hoster/SpeedLoadOrg.py new file mode 100644 index 000000000..5642987d2 --- /dev/null +++ b/pyload/plugin/hoster/SpeedLoadOrg.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class SpeedLoadOrg(DeadHoster): +    __name__    = "SpeedLoadOrg" +    __type__    = "hoster" +    __version__ = "1.02" + +    __pattern__ = r'http://(?:www\.)?speedload\.org/(?P<ID>\w+)' +    __config__  = [] + +    __description__ = """Speedload.org hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] diff --git a/pyload/plugin/hoster/SpeedfileCz.py b/pyload/plugin/hoster/SpeedfileCz.py new file mode 100644 index 000000000..f9df09d08 --- /dev/null +++ b/pyload/plugin/hoster/SpeedfileCz.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class SpeedfileCz(DeadHoster): +    __name__    = "SpeedFileCz" +    __type__    = "hoster" +    __version__ = "0.32" + +    __pattern__ = r'http://(?:www\.)?speedfile\.cz/.+' +    __config__  = [] + +    __description__ = """Speedfile.cz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/hoster/SpeedyshareCom.py b/pyload/plugin/hoster/SpeedyshareCom.py new file mode 100644 index 000000000..541e41b96 --- /dev/null +++ b/pyload/plugin/hoster/SpeedyshareCom.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://speedy.sh/ep2qY/Zapp-Brannigan.jpg + +import re + +from urlparse import urljoin + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class SpeedyshareCom(SimpleHoster): +    __name__    = "SpeedyshareCom" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'https?://(?:www\.)?(speedyshare\.com|speedy\.sh)/\w+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Speedyshare.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de")] + + +    NAME_PATTERN = r'class=downloadfilename>(?P<N>.*)</span></td>' +    SIZE_PATTERN = r'class=sizetagtext>(?P<S>.*) (?P<U>[kKmM]?[iI]?[bB]?)</div>' + +    OFFLINE_PATTERN = r'class=downloadfilenamenotfound>.*</span>' + +    LINK_FREE_PATTERN = r'<a href=\'(.*)\'><img src=/gf/slowdownload\.png alt=\'Slow Download\' border=0' + + +    def setup(self): +        self.multiDL = False +        self.chunkLimit = 1 + + +    def handleFree(self, pyfile): +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.link = m.group(1) diff --git a/pyload/plugin/hoster/StorageTo.py b/pyload/plugin/hoster/StorageTo.py new file mode 100644 index 000000000..835666b45 --- /dev/null +++ b/pyload/plugin/hoster/StorageTo.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class StorageTo(DeadHoster): +    __name__    = "StorageTo" +    __type__    = "hoster" +    __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?storage\.to/get/.+' +    __config__  = [] + +    __description__ = """Storage.to hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de")] diff --git a/pyload/plugin/hoster/StreamCz.py b/pyload/plugin/hoster/StreamCz.py new file mode 100644 index 000000000..fb22cb6e5 --- /dev/null +++ b/pyload/plugin/hoster/StreamCz.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.network.RequestFactory import getURL +from pyload.plugin.Hoster import Hoster + + +def getInfo(urls): +    result = [] + +    for url in urls: + +        html = getURL(url) +        if re.search(StreamCz.OFFLINE_PATTERN, html): +            # File offline +            result.append((url, 0, 1, url)) +        else: +            result.append((url, 0, 2, url)) +    yield result + + +class StreamCz(Hoster): +    __name__    = "StreamCz" +    __type__    = "hoster" +    __version__ = "0.20" + +    __pattern__ = r'https?://(?:www\.)?stream\.cz/[^/]+/\d+' + +    __description__ = """Stream.cz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN = r'<link rel="video_src" href="http://www\.stream\.cz/\w+/(\d+)-(.+?)" />' +    OFFLINE_PATTERN = r'<h1 class="commonTitle">Str.nku nebylo mo.n. nal.zt \(404\)</h1>' + +    CDN_PATTERN = r'<param name="flashvars" value=".+?&id=(?P<ID>\d+)(?:&cdnLQ=(?P<cdnLQ>\d*))?(?:&cdnHQ=(?P<cdnHQ>\d*))?(?:&cdnHD=(?P<cdnHD>\d*))?&' + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True + + +    def process(self, pyfile): +        self.html = self.load(pyfile.url, decode=True) + +        if re.search(self.OFFLINE_PATTERN, self.html): +            self.offline() + +        m = re.search(self.CDN_PATTERN, self.html) +        if m is None: +            self.error(_("CDN_PATTERN not found")) +        cdn = m.groupdict() +        self.logDebug(cdn) +        for cdnkey in ("cdnHD", "cdnHQ", "cdnLQ"): +            if cdnkey in cdn and cdn[cdnkey] > '': +                cdnid = cdn[cdnkey] +                break +        else: +            self.fail(_("Stream URL not found")) + +        m = re.search(self.NAME_PATTERN, self.html) +        if m is None: +            self.error(_("NAME_PATTERN not found")) +        pyfile.name = "%s-%s.%s.mp4" % (m.group(2), m.group(1), cdnkey[-2:]) + +        download_url = "http://cdn-dispatcher.stream.cz/?id=" + cdnid +        self.logInfo(_("STREAM: %s") % cdnkey[-2:], download_url) +        self.download(download_url) diff --git a/pyload/plugin/hoster/StreamcloudEu.py b/pyload/plugin/hoster/StreamcloudEu.py new file mode 100644 index 000000000..01e334491 --- /dev/null +++ b/pyload/plugin/hoster/StreamcloudEu.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class StreamcloudEu(XFSHoster): +    __name__    = "StreamcloudEu" +    __type__    = "hoster" +    __version__ = "0.10" + +    __pattern__ = r'http://(?:www\.)?streamcloud\.eu/\w{12}' + +    __description__ = """Streamcloud.eu hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("seoester", "seoester@googlemail.com")] + + +    WAIT_PATTERN = r'var count = (\d+)' + +    LINK_PATTERN = r'file: "(http://(stor|cdn)\d+\.streamcloud\.eu:?\d*/.*/video\.(mp4|flv))",' + + +    def setup(self): +        self.multiDL        = True +        self.chunkLimit     = 1 +        self.resumeDownload = self.premium diff --git a/pyload/plugin/hoster/TurbobitNet.py b/pyload/plugin/hoster/TurbobitNet.py new file mode 100644 index 000000000..5a96d85e8 --- /dev/null +++ b/pyload/plugin/hoster/TurbobitNet.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- + +import random +import re +import time + +from Crypto.Cipher import ARC4 +from binascii import hexlify, unhexlify +from pycurl import HTTPHEADER +from urllib import quote + +from pyload.network.RequestFactory import getURL +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster, timestamp + + +class TurbobitNet(SimpleHoster): +    __name__    = "TurbobitNet" +    __type__    = "hoster" +    __version__ = "0.19" + +    __pattern__ = r'http://(?:www\.)?turbobit\.net/(?:download/free/)?(?P<ID>\w+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Turbobit.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("prOq", "")] + + +    URL_REPLACEMENTS = [(__pattern__ + ".*", "http://turbobit.net/\g<ID>.html")] + +    COOKIES = [("turbobit.net", "user_lang", "en")] + +    NAME_PATTERN = r'id="file-title">(?P<N>.+?)<' +    SIZE_PATTERN = r'class="file-size">(?P<S>[\d.,]+) (?P<U>[\w^_]+)' +    OFFLINE_PATTERN = r'<h2>File Not Found</h2>|html\(\'File (?:was )?not found' + +    LINK_FREE_PATTERN = LINK_PREMIUM_PATTERN = r'(/download/redirect/[^"\']+)' + +    LIMIT_WAIT_PATTERN = r'<div id=\'timeout\'>(\d+)<' +    CAPTCHA_PATTERN    = r'<img alt="Captcha" src="(.+?)"' + + +    def handleFree(self, pyfile): +        self.html = self.load("http://turbobit.net/download/free/%s" % self.info['pattern']['ID'], +                              decode=True) + +        rtUpdate = self.getRtUpdate() + +        self.solveCaptcha() + +        self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With: XMLHttpRequest"]) + +        self.html = self.load(self.getDownloadUrl(rtUpdate)) + +        self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With:"]) + +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m: +            self.link = m.group(1) + + +    def solveCaptcha(self): +        for _i in xrange(5): +            m = re.search(self.LIMIT_WAIT_PATTERN, self.html) +            if m: +                wait_time = int(m.group(1)) +                self.wait(wait_time, wait_time > 60) +                self.retry() + +            action, inputs = self.parseHtmlForm("action='#'") +            if not inputs: +                self.error(_("Captcha form not found")) +            self.logDebug(inputs) + +            if inputs['captcha_type'] == 'recaptcha': +                recaptcha = ReCaptcha(self) +                inputs['recaptcha_response_field'], inputs['recaptcha_challenge_field'] = recaptcha.challenge() +            else: +                m = re.search(self.CAPTCHA_PATTERN, self.html) +                if m is None: +                    self.error(_("captcha")) +                captcha_url = m.group(1) +                inputs['captcha_response'] = self.decryptCaptcha(captcha_url) + +            self.logDebug(inputs) +            self.html = self.load(self.url, post=inputs) + +            if '<div class="captcha-error">Incorrect, try again!<' in self.html: +                self.invalidCaptcha() +            else: +                self.correctCaptcha() +                break +        else: +            self.fail(_("Invalid captcha")) + + +    def getRtUpdate(self): +        rtUpdate = self.getStorage("rtUpdate") +        if not rtUpdate: +            if self.getStorage("version") != self.__version__ \ +               or int(self.getStorage("timestamp", 0)) + 86400000 < timestamp(): +                # that's right, we are even using jdownloader updates +                rtUpdate = getURL("http://update0.jdownloader.org/pluginstuff/tbupdate.js") +                rtUpdate = self.decrypt(rtUpdate.splitlines()[1]) +                # but we still need to fix the syntax to work with other engines than rhino +                rtUpdate = re.sub(r'for each\(var (\w+) in(\[[^\]]+\])\)\{', +                                  r'zza=\2;for(var zzi=0;zzi<zza.length;zzi++){\1=zza[zzi];', rtUpdate) +                rtUpdate = re.sub(r"for\((\w+)=", r"for(var \1=", rtUpdate) + +                self.setStorage("rtUpdate", rtUpdate) +                self.setStorage("timestamp", timestamp()) +                self.setStorage("version", self.__version__) +            else: +                self.logError(_("Unable to download, wait for update...")) +                self.tempOffline() + +        return rtUpdate + + +    def getDownloadUrl(self, rtUpdate): +        self.req.http.lastURL = self.url + +        m = re.search("(/\w+/timeout\.js\?\w+=)([^\"\'<>]+)", self.html) +        if m: +            url = "http://turbobit.net%s%s" % m.groups() +        else: +            url = "http://turbobit.net/files/timeout.js?ver=%s" % "".join(random.choice('0123456789ABCDEF') for _i in xrange(32)) + +        fun = self.load(url) + +        self.setWait(65, False) + +        for b in [1, 3]: +            self.jscode = "var id = \'%s\';var b = %d;var inn = \'%s\';%sout" % ( +                          self.info['pattern']['ID'], b, quote(fun), rtUpdate) + +            try: +                out = self.js.eval(self.jscode) +                self.logDebug("URL", self.js.engine, out) +                if out.startswith('/download/'): +                    return "http://turbobit.net%s" % out.strip() + +            except Exception, e: +                self.logError(e) +        else: +            if self.retries >= 2: +                # retry with updated js +                self.delStorage("rtUpdate") +            else: +                self.retry() + +        self.wait() + + +    def decrypt(self, data): +        cipher = ARC4.new(hexlify('E\x15\xa1\x9e\xa3M\xa0\xc6\xa0\x84\xb6H\x83\xa8o\xa0')) +        return unhexlify(cipher.encrypt(unhexlify(data))) + + +    def getLocalTimeString(self): +        lt = time.localtime() +        tz = time.altzone if lt.tm_isdst else time.timezone +        return "%s GMT%+03d%02d" % (time.strftime("%a %b %d %Y %H:%M:%S", lt), -tz // 3600, tz % 3600) diff --git a/pyload/plugin/hoster/TurbouploadCom.py b/pyload/plugin/hoster/TurbouploadCom.py new file mode 100644 index 000000000..20ae8bb04 --- /dev/null +++ b/pyload/plugin/hoster/TurbouploadCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class TurbouploadCom(DeadHoster): +    __name__    = "TurbouploadCom" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?turboupload\.com/(\w+)' +    __config__  = [] + +    __description__ = """Turboupload.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/hoster/TusfilesNet.py b/pyload/plugin/hoster/TusfilesNet.py new file mode 100644 index 000000000..a11e86a40 --- /dev/null +++ b/pyload/plugin/hoster/TusfilesNet.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- + +from pyload.network.HTTPRequest import BadHeader +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class TusfilesNet(XFSHoster): +    __name__    = "TusfilesNet" +    __type__    = "hoster" +    __version__ = "0.10" + +    __pattern__ = r'https?://(?:www\.)?tusfiles\.net/\w{12}' + +    __description__ = """Tusfiles.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com"), +                       ("guidobelix", "guidobelix@hotmail.it")] + + +    INFO_PATTERN    = r'\](?P<N>.+) - (?P<S>[\d.,]+) (?P<U>[\w^_]+)\[' +    OFFLINE_PATTERN = r'>File Not Found|<Title>TusFiles - Fast Sharing Files!|The file you are trying to download is no longer available' + + +    def setup(self): +        self.chunkLimit     = -1 +        self.multiDL        = True +        self.resumeDownload = True + + +    def downloadLink(self, link, disposition=True): +        try: +            return super(TusfilesNet, self).downloadLink(link, disposition) + +        except BadHeader, e: +            if e.code is 503: +                self.multiDL = False +                raise Retry("503") diff --git a/pyload/plugin/hoster/TwoSharedCom.py b/pyload/plugin/hoster/TwoSharedCom.py new file mode 100644 index 000000000..854c46979 --- /dev/null +++ b/pyload/plugin/hoster/TwoSharedCom.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class TwoSharedCom(SimpleHoster): +    __name__    = "TwoSharedCom" +    __type__    = "hoster" +    __version__ = "0.13" + +    __pattern__ = r'http://(?:www\.)?2shared\.com/(account/)?(download|get|file|document|photo|video|audio)/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """2Shared.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN    = r'<h1>(?P<N>.*)</h1>' +    SIZE_PATTERN    = r'<span class="dtitle">File size:</span>\s*(?P<S>[\d.,]+) (?P<U>[\w^_]+)' +    OFFLINE_PATTERN = r'The file link that you requested is not valid\.|This file was deleted\.' + +    LINK_FREE_PATTERN = r'window.location =\'(.+?)\';' + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True diff --git a/pyload/plugin/hoster/UlozTo.py b/pyload/plugin/hoster/UlozTo.py new file mode 100644 index 000000000..8eec7062f --- /dev/null +++ b/pyload/plugin/hoster/UlozTo.py @@ -0,0 +1,150 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.utils import json_loads +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +def convertDecimalPrefix(m): +    # decimal prefixes used in filesize and traffic +    return ("%%.%df" % {'k': 3, 'M': 6, 'G': 9}[m.group(2)] % float(m.group(1))).replace('.', '') + + +class UlozTo(SimpleHoster): +    __name__    = "UlozTo" +    __type__    = "hoster" +    __version__ = "1.08" + +    __pattern__ = r'http://(?:www\.)?(uloz\.to|ulozto\.(cz|sk|net)|bagruj\.cz|zachowajto\.pl)/(?:live/)?(?P<ID>\w+/[^/?]*)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Uloz.to hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    INFO_PATTERN    = r'<p>File <strong>(?P<N>[^<]+)</strong> is password protected</p>' +    NAME_PATTERN    = r'<title>(?P<N>[^<]+) \| Uloz\.to</title>' +    SIZE_PATTERN    = r'<span id="fileSize">.*?(?P<S>[\d.,]+\s[kMG]?B)</span>' +    OFFLINE_PATTERN = r'<title>404 - Page not found</title>|<h1 class="h1">File (has been deleted|was banned)</h1>' + +    URL_REPLACEMENTS  = [(r'(?<=http://)([^/]+)', "www.ulozto.net")] +    SIZE_REPLACEMENTS = [(r'([\d.]+)\s([kMG])B', convertDecimalPrefix)] + +    CHECK_TRAFFIC = True + +    ADULT_PATTERN   = r'<form action="(.+?)" method="post" id="frm-askAgeForm">' +    PASSWD_PATTERN  = r'<div class="passwordProtectedFile">' +    VIPLINK_PATTERN = r'<a href=".+?\?disclaimer=1" class="linkVip">' +    TOKEN_PATTERN   = r'<input type="hidden" name="_token_" .*?value="(.+?)"' + + +    def setup(self): +        self.chunkLimit     = 16 if self.premium else 1 +        self.multiDL        = True +        self.resumeDownload = True + + +    def handleFree(self, pyfile): +        action, inputs = self.parseHtmlForm('id="frm-downloadDialog-freeDownloadForm"') +        if not action or not inputs: +            self.error(_("Free download form not found")) + +        self.logDebug("inputs.keys = " + str(inputs.keys())) +        # get and decrypt captcha +        if all(key in inputs for key in ("captcha_value", "captcha_id", "captcha_key")): +            # Old version - last seen 9.12.2013 +            self.logDebug('Using "old" version') + +            captcha_value = self.decryptCaptcha("http://img.uloz.to/captcha/%s.png" % inputs['captcha_id']) +            self.logDebug("CAPTCHA ID: " + inputs['captcha_id'] + ", CAPTCHA VALUE: " + captcha_value) + +            inputs.update({'captcha_id': inputs['captcha_id'], 'captcha_key': inputs['captcha_key'], 'captcha_value': captcha_value}) + +        elif all(key in inputs for key in ("captcha_value", "timestamp", "salt", "hash")): +            # New version - better to get new parameters (like captcha reload) because of image url - since 6.12.2013 +            self.logDebug('Using "new" version') + +            xapca = self.load("http://www.ulozto.net/reloadXapca.php", get={'rnd': str(int(time.time()))}) +            self.logDebug("xapca = " + str(xapca)) + +            data = json_loads(xapca) +            captcha_value = self.decryptCaptcha(str(data['image'])) +            self.logDebug("CAPTCHA HASH: " + data['hash'], "CAPTCHA SALT: " + str(data['salt']), "CAPTCHA VALUE: " + captcha_value) + +            inputs.update({'timestamp': data['timestamp'], 'salt': data['salt'], 'hash': data['hash'], 'captcha_value': captcha_value}) + +        else: +            self.error(_("CAPTCHA form changed")) + +        self.download("http://www.ulozto.net" + action, post=inputs) + + +    def handlePremium(self, pyfile): +        self.download(pyfile.url, get={'do': "directDownload"}) + + +    def checkErrors(self): +        if re.search(self.ADULT_PATTERN, self.html): +            self.logInfo(_("Adult content confirmation needed")) + +            m = re.search(self.TOKEN_PATTERN, self.html) +            if m is None: +                self.error(_("TOKEN_PATTERN not found")) + +            self.html = self.load(pyfile.url, +                                  get={'do': "askAgeForm-submit"}, +                                  post={"agree": "Confirm", "_token_": m.group(1)}) + +        if self.PASSWD_PATTERN in self.html: +            password = self.getPassword() + +            if password: +                self.logInfo(_("Password protected link, trying ") + password) +                self.html = self.load(pyfile.url, +                                      get={'do': "passwordProtectedForm-submit"}, +                                      post={"password": password, "password_send": 'Send'}) + +                if self.PASSWD_PATTERN in self.html: +                    self.fail(_("Incorrect password")) +            else: +                self.fail(_("No password found")) + +        if re.search(self.VIPLINK_PATTERN, self.html): +            self.html = self.load(pyfile.url, get={'disclaimer': "1"}) + +        return super(UlozTo, self).checkErrors() + + +    def checkFile(self, rules={}): +        check = self.checkDownload({ +            "wrong_captcha": re.compile(r'<ul class="error">\s*<li>Error rewriting the text.</li>'), +            "offline"      : re.compile(self.OFFLINE_PATTERN), +            "passwd"       : self.PASSWD_PATTERN, +            "server_error" : 'src="http://img.ulozto.cz/error403/vykricnik.jpg"',  # paralell dl, server overload etc. +            "not_found"    : "<title>UloÅŸ.to</title>" +        }) + +        if check == "wrong_captcha": +            self.invalidCaptcha() +            self.retry(reason=_("Wrong captcha code")) + +        elif check == "offline": +            self.offline() + +        elif check == "passwd": +            self.fail(_("Wrong password")) + +        elif check == "server_error": +            self.logError(_("Server error, try downloading later")) +            self.multiDL = False +            self.wait(1 * 60 * 60, True) +            self.retry() + +        elif check == "not_found": +            self.fail(_("Server error, file not downloadable")) + +        return super(UlozTo, self).checkFile(rules) + diff --git a/pyload/plugin/hoster/UloziskoSk.py b/pyload/plugin/hoster/UloziskoSk.py new file mode 100644 index 000000000..f8ccc46d2 --- /dev/null +++ b/pyload/plugin/hoster/UloziskoSk.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class UloziskoSk(SimpleHoster): +    __name__    = "UloziskoSk" +    __type__    = "hoster" +    __version__ = "0.25" + +    __pattern__ = r'http://(?:www\.)?ulozisko\.sk/.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Ulozisko.sk hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN = r'<div class="down1">(?P<N>[^<]+)</div>' +    SIZE_PATTERN = ur'VeÄŸkosÅ¥ súboru: <strong>(?P<S>[\d.,]+) (?P<U>[\w^_]+)</strong><br />' +    OFFLINE_PATTERN = ur'<span class = "red">ZadanÜ súbor neexistuje z jedného z nasledujúcich dÃŽvodov:</span>' + +    LINK_FREE_PATTERN = r'<form name = "formular" action = "(.+?)" method = "post">' +    ID_PATTERN = r'<input type = "hidden" name = "id" value = "(.+?)" />' +    CAPTCHA_PATTERN = r'<img src="(/obrazky/obrazky\.php\?fid=.+?)" alt="" />' +    IMG_PATTERN = ur'<strong>PRE ZVÃÄÅ ENIE KLIKNITE NA OBRÃZOK</strong><br /><a href = "(.+?)">' + + +    def process(self, pyfile): +        self.html = self.load(pyfile.url, decode=True) +        self.getFileInfo() + +        m = re.search(self.IMG_PATTERN, self.html) +        if m: +            self.link = "http://ulozisko.sk" + m.group(1) +        else: +            self.handleFree(pyfile) + + +    def handleFree(self, pyfile): +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.error(_("LINK_FREE_PATTERN not found")) +        parsed_url = 'http://www.ulozisko.sk' + m.group(1) + +        m = re.search(self.ID_PATTERN, self.html) +        if m is None: +            self.error(_("ID_PATTERN not found")) +        id = m.group(1) + +        self.logDebug("URL:" + parsed_url + ' ID:' + id) + +        m = re.search(self.CAPTCHA_PATTERN, self.html) +        if m is None: +            self.error(_("CAPTCHA_PATTERN not found")) +        captcha_url = 'http://www.ulozisko.sk' + m.group(1) + +        captcha = self.decryptCaptcha(captcha_url, cookies=True) + +        self.logDebug("CAPTCHA_URL:" + captcha_url + ' CAPTCHA:' + captcha) + +        self.download(parsed_url, +                      post={"antispam": captcha, +                            "id"      : id, +                            "name"    : pyfile.name, +                            "but"     : "++++STIAHNI+S%DABOR++++"}) diff --git a/pyload/plugin/hoster/UnibytesCom.py b/pyload/plugin/hoster/UnibytesCom.py new file mode 100644 index 000000000..d00dc774d --- /dev/null +++ b/pyload/plugin/hoster/UnibytesCom.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class UnibytesCom(SimpleHoster): +    __name__    = "UnibytesCom" +    __type__    = "hoster" +    __version__ = "0.12" + +    __pattern__ = r'https?://(?:www\.)?unibytes\.com/[\w .-]{11}B' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """UniBytes.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    HOSTER_DOMAIN = "unibytes.com" + +    INFO_PATTERN = r'<span[^>]*?id="fileName".*?>(?P<N>[^>]+)</span>\s*\((?P<S>\d.*?)\)' + +    WAIT_PATTERN = r'Wait for <span id="slowRest">(\d+)</span> sec' +    LINK_FREE_PATTERN = r'<a href="(.+?)">Download</a>' + + +    def handleFree(self, pyfile): +        domain            = "http://www.%s/" % self.HOSTER_DOMAIN +        action, post_data = self.parseHtmlForm('id="startForm"') + + +        for _i in xrange(8): +            self.logDebug(action, post_data) +            self.html = self.load(urljoin(domain, action), post=post_data, follow_location=False) + +            m = re.search(r'location:\s*(\S+)', self.req.http.header, re.I) +            if m: +                self.link = m.group(1) +                break + +            if '>Somebody else is already downloading using your IP-address<' in self.html: +                self.wait(10 * 60, True) +                self.retry() + +            if post_data['step'] == 'last': +                m = re.search(self.LINK_FREE_PATTERN, self.html) +                if m: +                    self.link = m.group(1) +                    self.correctCaptcha() +                    break +                else: +                    self.invalidCaptcha() + +            last_step = post_data['step'] +            action, post_data = self.parseHtmlForm('id="stepForm"') + +            if last_step == 'timer': +                m = re.search(self.WAIT_PATTERN, self.html) +                self.wait(m.group(1) if m else 60, False) + +            elif last_step in ("captcha", "last"): +                post_data['captcha'] = self.decryptCaptcha(urljoin(domain, "/captcha.jpg")) + +        else: +            self.fail(_("No valid captcha code entered")) + diff --git a/pyload/plugin/hoster/UnrestrictLi.py b/pyload/plugin/hoster/UnrestrictLi.py new file mode 100644 index 000000000..fa77d15e8 --- /dev/null +++ b/pyload/plugin/hoster/UnrestrictLi.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.utils import json_loads +from pyload.plugin.internal.MultiHoster import MultiHoster +from pyload.plugin.internal.SimpleHoster import secondsToMidnight + + +class UnrestrictLi(MultiHoster): +    __name__    = "UnrestrictLi" +    __type__    = "hoster" +    __version__ = "0.22" + +    __pattern__ = r'https?://(?:www\.)?(unrestrict|unr)\.li/dl/[\w^_]+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Unrestrict.li multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    LOGIN_ACCOUNT = False + + +    def setup(self): +        self.chunkLimit     = 16 +        self.resumeDownload = True + + +    def handleFree(self, pyfile): +        for _i in xrange(5): +            self.html = self.load('https://unrestrict.li/unrestrict.php', +                             post={'link': pyfile.url, 'domain': 'long'}) + +            self.logDebug("JSON data: " + self.html) + +            if self.html: +                break +        else: +            self.logInfo(_("Unable to get API data, waiting 1 minute and retry")) +            self.retry(5, 60, "Unable to get API data") + +        if 'Expired session' in self.html \ +           or ("You are not allowed to download from this host" in self.html and self.premium): +            self.account.relogin(self.user) +            self.retry() + +        elif "File offline" in self.html: +            self.offline() + +        elif "You are not allowed to download from this host" in self.html: +            self.fail(_("You are not allowed to download from this host")) + +        elif "You have reached your daily limit for this host" in self.html: +            self.logWarning(_("Reached daily limit for this host")) +            self.retry(5, secondsToMidnight(gmt=2), "Daily limit for this host reached") + +        elif "ERROR_HOSTER_TEMPORARILY_UNAVAILABLE" in self.html: +            self.logInfo(_("Hoster temporarily unavailable, waiting 1 minute and retry")) +            self.retry(5, 60, "Hoster is temporarily unavailable") + +        self.html     = json_loads(self.html) +        self.link     = self.html.keys()[0] +        self.api_data = self.html[self.link] + +        if hasattr(self, 'api_data'): +            self.setNameSize() + + +    def checkFile(self, rules={}): +        super(UnrestrictLi, self).checkFile(rules) + +        if self.getConfig('history'): +            self.load("https://unrestrict.li/history/", get={'delete': "all"}) +            self.logInfo(_("Download history deleted")) + + +    def setNameSize(self): +        if 'name' in self.api_data: +            self.pyfile.name = self.api_data['name'] +        if 'size' in self.api_data: +            self.pyfile.size = self.api_data['size'] diff --git a/pyload/plugin/hoster/UpleaCom.py b/pyload/plugin/hoster/UpleaCom.py new file mode 100644 index 000000000..855d9ed65 --- /dev/null +++ b/pyload/plugin/hoster/UpleaCom.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class UpleaCom(XFSHoster): +    __name__    = "UpleaCom" +    __type__    = "hoster" +    __version__ = "0.06" + +    __pattern__ = r'https?://(?:www\.)?uplea\.com/dl/\w{15}' + +    __description__ = """Uplea.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Redleon", "")] + + +    NAME_PATTERN = r'class="agmd size18">(?P<N>.+?)<' +    SIZE_PATTERN = r'size14">(?P<S>[\d.,]+) (?P<U>[\w^_])</span>' + +    OFFLINE_PATTERN = r'>You followed an invalid or expired link' + +    LINK_PATTERN = r'"(http?://\w+\.uplea\.com/anonym/.*?)"' + +    WAIT_PATTERN = r'timeText:([\d.]+),' +    STEP_PATTERN = r'<a href="(/step/.+)">' + + +    def setup(self): +        self.multiDL = False +        self.chunkLimit = 1 +        self.resumeDownload = True + + +    def handleFree(self, pyfile): +        m = re.search(self.STEP_PATTERN, self.html) +        if m is None: +            self.error(_("STEP_PATTERN not found")) + +        self.html = self.load(urljoin("http://uplea.com/", m.group(1))) + +        m = re.search(self.WAIT_PATTERN, self.html) +        if m: +            self.wait(m.group(1), True) +            self.retry() + +        m = re.search(self.LINK_PATTERN, self.html) +        if m is None: +            self.error(_("LINK_PATTERN not found")) + +        self.link = m.group(1) +        self.wait(15) diff --git a/pyload/plugin/hoster/UploadStationCom.py b/pyload/plugin/hoster/UploadStationCom.py new file mode 100644 index 000000000..decde08ab --- /dev/null +++ b/pyload/plugin/hoster/UploadStationCom.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class UploadStationCom(DeadHoster): +    __name__    = "UploadStationCom" +    __type__    = "hoster" +    __version__ = "0.52" + +    __pattern__ = r'http://(?:www\.)?uploadstation\.com/file/(?P<ID>\w+)' +    __config__  = [] + +    __description__ = """UploadStation.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("fragonib", "fragonib[AT]yahoo[DOT]es"), +                       ("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/hoster/UploadableCh.py b/pyload/plugin/hoster/UploadableCh.py new file mode 100644 index 000000000..d1e356875 --- /dev/null +++ b/pyload/plugin/hoster/UploadableCh.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class UploadableCh(SimpleHoster): +    __name__    = "UploadableCh" +    __type__    = "hoster" +    __version__ = "0.09" + +    __pattern__ = r'http://(?:www\.)?uploadable\.ch/file/(?P<ID>\w+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Uploadable.ch hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    URL_REPLACEMENTS = [(__pattern__ + ".*", r'http://www.uploadable.ch/file/\g<ID>')] + +    INFO_PATTERN = r'div id=\"file_name\" title=.*>(?P<N>.+)<span class=\"filename_normal\">\((?P<S>[\d.]+) (?P<U>\w+)\)</span><' + +    OFFLINE_PATTERN      = r'>(File not available|This file is no longer available)' +    TEMP_OFFLINE_PATTERN = r'<div class="icon_err">' + +    WAIT_PATTERN = r'>Please wait.+?<' + +    RECAPTCHA_KEY = "6LdlJuwSAAAAAPJbPIoUhyqOJd7-yrah5Nhim5S3" + + +    def handleFree(self, pyfile): +        # Click the "free user" button and wait +        a = self.load(pyfile.url, post={'downloadLink': "wait"}, decode=True) +        self.logDebug(a) + +        self.wait(30) + +        # Make the recaptcha appear and show it the pyload interface +        b = self.load(pyfile.url, post={'checkDownload': "check"}, decode=True) +        self.logDebug(b)  #: Expected output: {"success":"showCaptcha"} + +        recaptcha = ReCaptcha(self) + +        response, challenge = recaptcha.challenge(self.RECAPTCHA_KEY) + +        # Submit the captcha solution +        self.load("http://www.uploadable.ch/checkReCaptcha.php", +                  post={'recaptcha_challenge_field'  : challenge, +                        'recaptcha_response_field'   : response, +                        'recaptcha_shortencode_field': self.info['pattern']['ID']}, +                  decode=True) + +        self.wait(3) + +        # Get ready for downloading +        self.load(pyfile.url, post={'downloadLink': "show"}, decode=True) + +        self.wait(3) + +        # Download the file +        self.download(pyfile.url, post={'download': "normal"}, disposition=True) + + +    def checkFile(self, rules={}): +        if self.checkDownload({'wait': re.compile("Please wait for")}): +            self.logInfo("Downloadlimit reached, please wait or reconnect") +            self.wait(60 * 60, True) +            self.retry() + +        return super(UploadableCh, self).checkFile(rules) diff --git a/pyload/plugin/hoster/UploadboxCom.py b/pyload/plugin/hoster/UploadboxCom.py new file mode 100644 index 000000000..33b81b97e --- /dev/null +++ b/pyload/plugin/hoster/UploadboxCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class UploadboxCom(DeadHoster): +    __name__    = "Uploadbox" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?uploadbox\.com/files/.+' +    __config__  = [] + +    __description__ = """UploadBox.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/hoster/UploadedTo.py b/pyload/plugin/hoster/UploadedTo.py new file mode 100644 index 000000000..44c0da516 --- /dev/null +++ b/pyload/plugin/hoster/UploadedTo.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from pyload.network.RequestFactory import getURL +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class UploadedTo(SimpleHoster): +    __name__    = "UploadedTo" +    __type__    = "hoster" +    __version__ = "0.85" + +    __pattern__ = r'https?://(?:www\.)?(uploaded\.(to|net)|ul\.to)(/file/|/?\?id=|.*?&id=|/)(?P<ID>\w+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Uploaded.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    API_KEY = "lhF2IeeprweDfu9ccWlxXVVypA5nA3EL" + +    URL_REPLACEMENTS = [(__pattern__ + ".*", r'http://uploaded.net/file/\g<ID>')] + +    LINK_PREMIUM_PATTERN = r'<div class="tfree".*\s*<form method="post" action="(.+?)"' + +    WAIT_PATTERN   = r'Current waiting period: <span>(\d+)' +    DL_LIMIT_ERROR = r'You have reached the max. number of possible free downloads for this hour' + + +    @classmethod +    def apiInfo(cls, url="", get={}, post={}): +        info = super(UploadedTo, cls).apiInfo(url) + +        for _i in xrange(5): +            html = getURL("http://uploaded.net/api/filemultiple", +                          get={"apikey": cls.API_KEY, 'id_0': re.match(cls.__pattern__, url).group('ID')}, +                          decode=True) + +            if html != "can't find request": +                api = html.split(",", 4) +                if api[0] == "online": +                    info.update({'name': api[4].strip(), 'size': api[2], 'status': 2}) +                else: +                    info['status'] = 1 +                break +            else: +                time.sleep(3) + +        return info + + +    def setup(self): +        self.multiDL    = self.resumeDownload = self.premium +        self.chunkLimit = 1  # critical problems with more chunks + + +    def checkErrors(self): +        if 'var free_enabled = false;' in self.html: +            self.logError(_("Free-download capacities exhausted")) +            self.retry(24, 5 * 60) + +        elif "limit-size" in self.html: +            self.fail(_("File too big for free download")) + +        elif "limit-slot" in self.html:  # Temporary restriction so just wait a bit +            self.wait(30 * 60, True) +            self.retry() + +        elif "limit-parallel" in self.html: +            self.fail(_("Cannot download in parallel")) + +        elif "limit-dl" in self.html or self.DL_LIMIT_ERROR in self.html:  # limit-dl +            self.wait(3 * 60 * 60, True) +            self.retry() + +        elif '"err":"captcha"' in self.html: +            self.invalidCaptcha() + +        else: +            m = re.search(self.WAIT_PATTERN, self.html) +            if m: +                self.wait(m.group(1)) + + +    def handleFree(self, pyfile): +        self.load("http://uploaded.net/language/en", just_header=True) + +        self.html = self.load("http://uploaded.net/js/download.js", decode=True) + +        recaptcha = ReCaptcha(self) +        response, challenge = recaptcha.challenge() + +        self.html = self.load("http://uploaded.net/io/ticket/captcha/%s" % self.info['pattern']['ID'], +                              post={'recaptcha_challenge_field': challenge, +                                    'recaptcha_response_field' : response}) + +        if "type:'download'" in self.html: +            self.correctCaptcha() +            try: +                self.link = re.search("url:'(.+?)'", self.html).group(1) + +            except Exception: +                pass + +        self.checkErrors() + + +    def checkFile(self, rules={}): +        if self.checkDownload({'limit-dl': self.DL_LIMIT_ERROR}): +            self.wait(3 * 60 * 60, True) +            self.retry() + +        return super(UploadedTo, self).checkFile(rules) diff --git a/pyload/plugin/hoster/UploadhereCom.py b/pyload/plugin/hoster/UploadhereCom.py new file mode 100644 index 000000000..259b53dc1 --- /dev/null +++ b/pyload/plugin/hoster/UploadhereCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class UploadhereCom(DeadHoster): +    __name__    = "UploadhereCom" +    __type__    = "hoster" +    __version__ = "0.12" + +    __pattern__ = r'http://(?:www\.)?uploadhere\.com/\w{10}' +    __config__  = [] + +    __description__ = """Uploadhere.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/hoster/UploadheroCom.py b/pyload/plugin/hoster/UploadheroCom.py new file mode 100644 index 000000000..2def499d9 --- /dev/null +++ b/pyload/plugin/hoster/UploadheroCom.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://uploadhero.co/dl/wQBRAVSM + +import re + +from urlparse import urljoin + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class UploadheroCom(SimpleHoster): +    __name__    = "UploadheroCom" +    __type__    = "hoster" +    __version__ = "0.18" + +    __pattern__ = r'http://(?:www\.)?uploadhero\.com?/dl/\w+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """UploadHero.co plugin""" +    __license__     = "GPLv3" +    __authors__     = [("mcmyst", "mcmyst@hotmail.fr"), +                       ("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN    = r'<div class="nom_de_fichier">(?P<N>.+?)<' +    SIZE_PATTERN    = r'>Filesize: </span><strong>(?P<S>[\d.,]+) (?P<U>[\w^_]+)' +    OFFLINE_PATTERN = r'<p class="titre_dl_2">' + +    COOKIES = [("uploadhero.co", "lang", "en")] + +    IP_BLOCKED_PATTERN = r'href="(/lightbox_block_download\.php\?min=.+?)"' +    IP_WAIT_PATTERN    = r'<span id="minutes">(\d+)</span>.*\s*<span id="seconds">(\d+)</span>' + +    CAPTCHA_PATTERN = r'"(/captchadl\.php\?\w+)"' + +    LINK_FREE_PATTERN    = r'var magicomfg = \'<a href="(.+?)"|"(http://storage\d+\.uploadhero\.co.+?)"' +    LINK_PREMIUM_PATTERN = r'<a href="(.+?)" id="downloadnow"' + + +    def handleFree(self, pyfile): +        m = re.search(self.CAPTCHA_PATTERN, self.html) +        if m is None: +            self.error(_("Captcha not found")) + +        captcha = self.decryptCaptcha(urljoin("http://uploadhero.co", m.group(1))) + +        self.html = self.load(pyfile.url, +                              get={"code": captcha}) + +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m: +            self.link = m.group(1) or m.group(2) +            self.wait(50) + + +    def checkErrors(self): +        m = re.search(self.IP_BLOCKED_PATTERN, self.html) +        if m: +            self.html = self.load(urljoin("http://uploadhero.co", m.group(1))) + +            m = re.search(self.IP_WAIT_PATTERN, self.html) +            wait_time = (int(m.group(1)) * 60 + int(m.group(2))) if m else 5 * 60 +            self.wait(wait_time, True) +            self.retry() + +        return super(UploadheroCom, self).checkErrors() diff --git a/pyload/plugin/hoster/UploadingCom.py b/pyload/plugin/hoster/UploadingCom.py new file mode 100644 index 000000000..da096eb1f --- /dev/null +++ b/pyload/plugin/hoster/UploadingCom.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- + +import re + +from pycurl import HTTPHEADER + +from pyload.utils import json_loads +from pyload.plugin.internal.SimpleHoster import SimpleHoster, timestamp + + +class UploadingCom(SimpleHoster): +    __name__    = "UploadingCom" +    __type__    = "hoster" +    __version__ = "0.40" + +    __pattern__ = r'http://(?:www\.)?uploading\.com/files/(?:get/)?(?P<ID>\w+)' + +    __description__ = """Uploading.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("mkaay", "mkaay@mkaay.de"), +                       ("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN = r'id="file_title">(?P<N>.+)</' +    SIZE_PATTERN = r'size tip_container">(?P<S>[\d.,]+) (?P<U>[\w^_]+)<' +    OFFLINE_PATTERN = r'(Page|file) not found' + +    COOKIES = [("uploading.com", "lang", "1"), +               (".uploading.com", "language", "1"), +               (".uploading.com", "setlang", "en"), +               (".uploading.com", "_lang", "en")] + + +    def process(self, pyfile): +        if not "/get/" in pyfile.url: +            pyfile.url = pyfile.url.replace("/files", "/files/get") + +        self.html = self.load(pyfile.url, decode=True) +        self.getFileInfo() + +        if self.premium: +            self.handlePremium(pyfile) +        else: +            self.handleFree(pyfile) + + +    def handlePremium(self, pyfile): +        postData = {'action': 'get_link', +                    'code'  : self.info['pattern']['ID'], +                    'pass'  : 'undefined'} + +        self.html = self.load('http://uploading.com/files/get/?JsHttpRequest=%d-xml' % timestamp(), post=postData) +        url = re.search(r'"link"\s*:\s*"(.*?)"', self.html) +        if url: +            self.link = url.group(1).replace("\\/", "/") + +        raise Exception("Plugin defect") + + +    def handleFree(self, pyfile): +        m = re.search('<h2>((Daily )?Download Limit)</h2>', self.html) +        if m: +            pyfile.error = m.group(1) +            self.logWarning(pyfile.error) +            self.retry(6, (6 * 60 if m.group(2) else 15) * 60, pyfile.error) + +        ajax_url = "http://uploading.com/files/get/?ajax" +        self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With: XMLHttpRequest"]) +        self.req.http.lastURL = pyfile.url + +        res = json_loads(self.load(ajax_url, post={'action': 'second_page', 'code': self.info['pattern']['ID']})) + +        if 'answer' in res and 'wait_time' in res['answer']: +            wait_time = int(res['answer']['wait_time']) +            self.logInfo(_("Waiting %d seconds") % wait_time) +            self.wait(wait_time) +        else: +            self.error(_("No AJAX/WAIT")) + +        res = json_loads(self.load(ajax_url, post={'action': 'get_link', 'code': self.info['pattern']['ID'], 'pass': 'false'})) + +        if 'answer' in res and 'link' in res['answer']: +            url = res['answer']['link'] +        else: +            self.error(_("No AJAX/URL")) + +        self.html = self.load(url) +        m = re.search(r'<form id="file_form" action="(.*?)"', self.html) +        if m: +            url = m.group(1) +        else: +            self.error(_("No URL")) + +        self.link = url diff --git a/pyload/plugin/hoster/UploadkingCom.py b/pyload/plugin/hoster/UploadkingCom.py new file mode 100644 index 000000000..33ecfd574 --- /dev/null +++ b/pyload/plugin/hoster/UploadkingCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class UploadkingCom(DeadHoster): +    __name__    = "UploadkingCom" +    __type__    = "hoster" +    __version__ = "0.14" + +    __pattern__ = r'http://(?:www\.)?uploadking\.com/\w{10}' +    __config__  = [] + +    __description__ = """UploadKing.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/pyload/plugin/hoster/UpstoreNet.py b/pyload/plugin/hoster/UpstoreNet.py new file mode 100644 index 000000000..80e9f4ebe --- /dev/null +++ b/pyload/plugin/hoster/UpstoreNet.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class UpstoreNet(SimpleHoster): +    __name__    = "UpstoreNet" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'https?://(?:www\.)?upstore\.net/' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Upstore.Net File Download Hoster""" +    __license__     = "GPLv3" +    __authors__     = [("igel", "igelkun@myopera.com")] + + +    INFO_PATTERN = r'<div class="comment">.*?</div>\s*\n<h2 style="margin:0">(?P<N>.*?)</h2>\s*\n<div class="comment">\s*\n\s*(?P<S>[\d.,]+) (?P<U>[\w^_]+)' +    OFFLINE_PATTERN = r'<span class="error">File not found</span>' + +    WAIT_PATTERN = r'var sec = (\d+)' +    CHASH_PATTERN = r'<input type="hidden" name="hash" value="(.+?)">' +    LINK_FREE_PATTERN = r'<a href="(https?://.*?)" target="_blank"><b>' + + +    def handleFree(self, pyfile): +        # STAGE 1: get link to continue +        m = re.search(self.CHASH_PATTERN, self.html) +        if m is None: +            self.error(_("CHASH_PATTERN not found")) +        chash = m.group(1) +        self.logDebug("Read hash " + chash) +        # continue to stage2 +        post_data = {'hash': chash, 'free': 'Slow download'} +        self.html = self.load(pyfile.url, post=post_data, decode=True) + +        # STAGE 2: solv captcha and wait +        # first get the infos we need: recaptcha key and wait time +        recaptcha = ReCaptcha(self) + +        # try the captcha 5 times +        for i in xrange(5): +            m = re.search(self.WAIT_PATTERN, self.html) +            if m is None: +                self.error(_("Wait pattern not found")) +            wait_time = int(m.group(1)) + +            # then, do the waiting +            self.wait(wait_time) + +            # then, handle the captcha +            response, challenge = recaptcha.challenge() +            post_data.update({'recaptcha_challenge_field': challenge, +                              'recaptcha_response_field' : response}) + +            self.html = self.load(pyfile.url, post=post_data, decode=True) + +            # STAGE 3: get direct link +            m = re.search(self.LINK_FREE_PATTERN, self.html, re.S) +            if m: +                break + +        if m is None: +            self.error(_("Download link not found")) + +        self.link = m.group(1) diff --git a/pyload/plugin/hoster/UptoboxCom.py b/pyload/plugin/hoster/UptoboxCom.py new file mode 100644 index 000000000..de23d4ad0 --- /dev/null +++ b/pyload/plugin/hoster/UptoboxCom.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class UptoboxCom(XFSHoster): +    __name__    = "UptoboxCom" +    __type__    = "hoster" +    __version__ = "0.18" + +    __pattern__ = r'https?://(?:www\.)?(uptobox|uptostream)\.com/\w{12}' + +    __description__ = """Uptobox.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    INFO_PATTERN         = r'"para_title">(?P<N>.+) \((?P<S>[\d.,]+) (?P<U>[\w^_]+)\)' +    OFFLINE_PATTERN      = r'>(File not found|Access Denied|404 Not Found)' +    TEMP_OFFLINE_PATTERN = r'>Service Unavailable' + +    LINK_PATTERN = r'"(https?://\w+\.uptobox\.com/d/.*?)"' + +    ERROR_PATTERN = r'>(You have to wait.+till next download.)<'  #@TODO: Check XFSHoster ERROR_PATTERN + + +    def setup(self): +        self.multiDL = True +        self.chunkLimit = 1 +        self.resumeDownload = True diff --git a/pyload/plugin/hoster/VeehdCom.py b/pyload/plugin/hoster/VeehdCom.py new file mode 100644 index 000000000..f4b0be050 --- /dev/null +++ b/pyload/plugin/hoster/VeehdCom.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Hoster import Hoster + + +class VeehdCom(Hoster): +    __name__    = "VeehdCom" +    __type__    = "hoster" +    __version__ = "0.23" + +    __pattern__ = r'http://veehd\.com/video/\d+_\S+' +    __config__  = [("filename_spaces", "bool", "Allow spaces in filename", False), +                   ("replacement_char", "str", "Filename replacement character", "_")] + +    __description__ = """Veehd.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("cat", "cat@pyload")] + + +    def setup(self): +        self.multiDL = True +        self.req.canContinue = True + + +    def process(self, pyfile): +        self.download_html() +        if not self.file_exists(): +            self.offline() + +        pyfile.name = self.get_file_name() +        self.download(self.get_file_url()) + + +    def download_html(self): +        url = self.pyfile.url +        self.logDebug("Requesting page: %s" % url) +        self.html = self.load(url) + + +    def file_exists(self): +        if not self.html: +            self.download_html() + +        if '<title>Veehd</title>' in self.html: +            return False +        return True + + +    def get_file_name(self): +        if not self.html: +            self.download_html() + +        m = re.search(r'<title.*?>([^<]+) on Veehd</title>', self.html) +        if m is None: +            self.error(_("Video title not found")) + +        name = m.group(1) + +        # replace unwanted characters in filename +        if self.getConfig('filename_spaces'): +            pattern = '[^\w ]+' +        else: +            pattern = '[^\w.]+' + +        return re.sub(pattern, self.getConfig('replacement_char'), name) + '.avi' + + +    def get_file_url(self): +        """ returns the absolute downloadable filepath +        """ +        if not self.html: +            self.download_html() + +        m = re.search(r'<embed type="video/divx" src="(http://([^/]*\.)?veehd\.com/dl/.+?)"', +                          self.html) +        if m is None: +            self.error(_("Embedded video url not found")) + +        return m.group(1) diff --git a/pyload/plugin/hoster/VeohCom.py b/pyload/plugin/hoster/VeohCom.py new file mode 100644 index 000000000..78080ee1d --- /dev/null +++ b/pyload/plugin/hoster/VeohCom.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class VeohCom(SimpleHoster): +    __name__    = "VeohCom" +    __type__    = "hoster" +    __version__ = "0.22" + +    __pattern__ = r'http://(?:www\.)?veoh\.com/(tv/)?(watch|videos)/(?P<ID>v\w+)' +    __config__  = [("use_premium", "bool"         , "Use premium account if available", True  ), +                   ("quality"    , "Low;High;Auto", "Quality"                         , "Auto")] + +    __description__ = """Veoh.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    NAME_PATTERN    = r'<meta name="title" content="(?P<N>.*?)"' +    OFFLINE_PATTERN = r'>Sorry, we couldn\'t find the video you were looking for' + +    URL_REPLACEMENTS = [(__pattern__ + ".*", r'http://www.veoh.com/watch/\g<ID>')] + +    COOKIES = [("veoh.com", "lassieLocale", "en")] + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True +        self.chunkLimit     = -1 + + +    def handleFree(self, pyfile): +        quality = self.getConfig('quality') +        if quality == "Auto": +            quality = ("High", "Low") + +        for q in quality: +            pattern = r'"fullPreviewHash%sPath":"(.+?)"' % q +            m = re.search(pattern, self.html) +            if m: +                pyfile.name += ".mp4" +                self.link = m.group(1).replace("\\", "") +                return +            else: +                self.logInfo(_("No %s quality video found") % q.upper()) +        else: +            self.fail(_("No video found!")) diff --git a/pyload/plugin/hoster/VidPlayNet.py b/pyload/plugin/hoster/VidPlayNet.py new file mode 100644 index 000000000..094da09bd --- /dev/null +++ b/pyload/plugin/hoster/VidPlayNet.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# BigBuckBunny_320x180.mp4 - 61.7 Mb - http://vidplay.net/38lkev0h3jv0 + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class VidPlayNet(XFSHoster): +    __name__    = "VidPlayNet" +    __type__    = "hoster" +    __version__ = "0.04" + +    __pattern__ = r'https?://(?:www\.)?vidplay\.net/\w{12}' + +    __description__ = """VidPlay.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")] + + +    NAME_PATTERN = r'<b>Password:</b></div>\s*<h[1-6]>(?P<N>[^<]+)</h[1-6]>' diff --git a/pyload/plugin/hoster/VimeoCom.py b/pyload/plugin/hoster/VimeoCom.py new file mode 100644 index 000000000..47ce0de4e --- /dev/null +++ b/pyload/plugin/hoster/VimeoCom.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class VimeoCom(SimpleHoster): +    __name__    = "VimeoCom" +    __type__    = "hoster" +    __version__ = "0.04" + +    __pattern__ = r'https?://(?:www\.)?(player\.)?vimeo\.com/(video/)?(?P<ID>\d+)' +    __config__  = [("use_premium", "bool"                       , "Use premium account if available" , True     ), +                   ("quality"    , "Lowest;Mobile;SD;HD;Highest", "Quality"                          , "Highest"), +                   ("original"   , "bool"                       , "Try to download the original file", True     )] + +    __description__ = """Vimeo.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    NAME_PATTERN         = r'<title>(?P<N>.+) on Vimeo<' +    OFFLINE_PATTERN      = r'class="exception_header"' +    TEMP_OFFLINE_PATTERN = r'Please try again in a few minutes.<' + +    URL_REPLACEMENTS = [(__pattern__ + ".*", r'https://www.vimeo.com/\g<ID>')] + +    COOKIES = [("vimeo.com", "language", "en")] + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True +        self.chunkLimit     = -1 + + +    def handleFree(self, pyfile): +        password = self.getPassword() + +        if self.js and 'class="btn iconify_down_b"' in self.html: +            html    = self.js.eval(self.load(pyfile.url, get={'action': "download", 'password': password}, decode=True)) +            pattern = r'href="(?P<URL>http://vimeo\.com.+?)".*?\>(?P<QL>.+?) ' +        else: +            html    = self.load("https://player.vimeo.com/video/" + self.info['pattern']['ID'], get={'password': password}) +            pattern = r'"(?P<QL>\w+)":{"profile".*?"(?P<URL>http://pdl\.vimeocdn\.com.+?)"' + +        link = dict((l.group('QL').lower(), l.group('URL')) for l in re.finditer(pattern, html)) + +        if self.getConfig('original'): +            if "original" in link: +                self.link = link[q] +                return +            else: +                self.logInfo(_("Original file not downloadable")) + +        quality = self.getConfig('quality') +        if quality == "Highest": +            qlevel = ("hd", "sd", "mobile") +        elif quality == "Lowest": +            qlevel = ("mobile", "sd", "hd") +        else: +            qlevel = quality.lower() + +        for q in qlevel: +            if q in link: +                self.link = link[q] +                return +            else: +                self.logInfo(_("No %s quality video found") % q.upper()) +        else: +            self.fail(_("No video found!")) diff --git a/pyload/plugin/hoster/Vipleech4UCom.py b/pyload/plugin/hoster/Vipleech4UCom.py new file mode 100644 index 000000000..0550f5c77 --- /dev/null +++ b/pyload/plugin/hoster/Vipleech4UCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class Vipleech4UCom(DeadHoster): +    __name__    = "Vipleech4UCom" +    __type__    = "hoster" +    __version__ = "0.20" + +    __pattern__ = r'http://(?:www\.)?vipleech4u\.com/manager\.php' +    __config__  = [] + +    __description__ = """Vipleech4u.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Kagenoshin", "kagenoshin@gmx.ch")] diff --git a/pyload/plugin/hoster/WarserverCz.py b/pyload/plugin/hoster/WarserverCz.py new file mode 100644 index 000000000..bedda34a7 --- /dev/null +++ b/pyload/plugin/hoster/WarserverCz.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class WarserverCz(DeadHoster): +    __name__    = "WarserverCz" +    __type__    = "hoster" +    __version__ = "0.13" + +    __pattern__ = r'http://(?:www\.)?warserver\.cz/stahnout/\d+' +    __config__  = [] + +    __description__ = """Warserver.cz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] diff --git a/pyload/plugin/hoster/WebshareCz.py b/pyload/plugin/hoster/WebshareCz.py new file mode 100644 index 000000000..baeecfc94 --- /dev/null +++ b/pyload/plugin/hoster/WebshareCz.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.network.RequestFactory import getURL +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class WebshareCz(SimpleHoster): +    __name__    = "WebshareCz" +    __type__    = "hoster" +    __version__ = "0.16" + +    __pattern__ = r'https?://(?:www\.)?webshare\.cz/(?:#/)?file/(?P<ID>\w+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """WebShare.cz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it"), +                       ("rush", "radek.senfeld@gmail.com")] + + +    @classmethod +    def getInfo(cls, url="", html=""): +        info = super(WebshareCz, cls).getInfo(url, html) + +        if url: +            info['pattern'] = re.match(cls.__pattern__, url).groupdict() + +            api_data = getURL("https://webshare.cz/api/file_info/", +                              post={'ident': info['pattern']['ID']}, +                              decode=True) + +            if 'File not found' in api_data: +                info['status'] = 1 +            else: +                info["status"] = 2 +                info['name']   = re.search('<name>(.+)</name>', api_data).group(1) or info['name'] +                info['size']   = re.search('<size>(.+)</size>', api_data).group(1) or info['size'] + +        return info + + +    def handleFree(self, pyfile): +        wst = self.account.infos['wst'] if self.account and 'wst' in self.account.infos else "" + +        api_data = getURL('https://webshare.cz/api/file_link/', +                          post={'ident': self.info['pattern']['ID'], 'wst': wst}, +                          decode=True) + +        self.logDebug("API data: " + api_data) + +        m = re.search('<link>(.+)</link>', api_data) +        if m is None: +            self.error(_("Unable to detect direct link")) + +        self.link = m.group(1) + + +    def handlePremium(self, pyfile): +        return self.handleFree(pyfile) diff --git a/pyload/plugin/hoster/WrzucTo.py b/pyload/plugin/hoster/WrzucTo.py new file mode 100644 index 000000000..bdbcb6a5b --- /dev/null +++ b/pyload/plugin/hoster/WrzucTo.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- + +import re + +from pycurl import HTTPHEADER + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class WrzucTo(SimpleHoster): +    __name__    = "WrzucTo" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?wrzuc\.to/(\w+(\.wt|\.html)|(\w+/?linki/\w+))' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Wrzuc.to hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN = r'id="file_info">\s*<strong>(?P<N>.*?)</strong>' +    SIZE_PATTERN = r'class="info">\s*<tr>\s*<td>(?P<S>.*?)</td>' + +    COOKIES = [("wrzuc.to", "language", "en")] + + +    def setup(self): +        self.multiDL = True + + +    def handleFree(self, pyfile): +        data = dict(re.findall(r'(md5|file): "(.*?)"', self.html)) +        if len(data) != 2: +            self.error(_("No file ID")) + +        self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With: XMLHttpRequest"]) +        self.req.http.lastURL = pyfile.url +        self.load("http://www.wrzuc.to/ajax/server/prepair", post={"md5": data['md5']}) + +        self.req.http.lastURL = pyfile.url +        self.html = self.load("http://www.wrzuc.to/ajax/server/download_link", post={"file": data['file']}) + +        data.update(re.findall(r'"(download_link|server_id)":"(.*?)"', self.html)) +        if len(data) != 4: +            self.error(_("No download URL")) + +        self.link = "http://%s.wrzuc.to/pobierz/%s" % (data['server_id'], data['download_link']) + diff --git a/pyload/plugin/hoster/WuploadCom.py b/pyload/plugin/hoster/WuploadCom.py new file mode 100644 index 000000000..0b4411a5b --- /dev/null +++ b/pyload/plugin/hoster/WuploadCom.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class WuploadCom(DeadHoster): +    __name__    = "WuploadCom" +    __type__    = "hoster" +    __version__ = "0.23" + +    __pattern__ = r'http://(?:www\.)?wupload\..+?/file/((\w+/)?\d+)(/.*)?' +    __config__  = [] + +    __description__ = """Wupload.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("Paul King", "")] diff --git a/pyload/plugin/hoster/X7To.py b/pyload/plugin/hoster/X7To.py new file mode 100644 index 000000000..f8700ba67 --- /dev/null +++ b/pyload/plugin/hoster/X7To.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class X7To(DeadHoster): +    __name__    = "X7To" +    __type__    = "hoster" +    __version__ = "0.41" + +    __pattern__ = r'http://(?:www\.)?x7\.to/' +    __config__  = [] + +    __description__ = """X7.to hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("ernieb", "ernieb")] diff --git a/pyload/plugin/hoster/XFileSharingPro.py b/pyload/plugin/hoster/XFileSharingPro.py new file mode 100644 index 000000000..e90826536 --- /dev/null +++ b/pyload/plugin/hoster/XFileSharingPro.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.internal.XFSHoster import XFSHoster + + +class XFileSharingPro(XFSHoster): +    __name__    = "XFileSharingPro" +    __type__    = "hoster" +    __version__ = "0.45" + +    __pattern__ = r'^unmatchable$' + +    __description__ = """XFileSharingPro dummy hoster plugin for hook""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    URL_REPLACEMENTS = [("/embed-", "/")] + + +    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("DOMAIN").lower() +        self.HOSTER_NAME   = "".join(part.capitalize() for part in re.split(r'(\.|\d+)', self.HOSTER_DOMAIN) if part != '.') + +        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 diff --git a/pyload/plugin/hoster/XHamsterCom.py b/pyload/plugin/hoster/XHamsterCom.py new file mode 100644 index 000000000..934ff3a58 --- /dev/null +++ b/pyload/plugin/hoster/XHamsterCom.py @@ -0,0 +1,129 @@ +# -*- coding: utf-8 -*- + +import re + +from urllib import unquote + +from pyload.utils import json_loads +from pyload.plugin.Hoster import Hoster + + +def clean_json(json_expr): +    json_expr = re.sub('[\n\r]', '', json_expr) +    json_expr = re.sub(' +', '', json_expr) +    json_expr = re.sub('\'', '"', json_expr) + +    return json_expr + + +class XHamsterCom(Hoster): +    __name__    = "XHamsterCom" +    __type__    = "hoster" +    __version__ = "0.12" + +    __pattern__ = r'http://(?:www\.)?xhamster\.com/movies/.+' +    __config__  = [("type", ".mp4;.flv", "Preferred type", ".mp4")] + +    __description__ = """XHamster.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [] + + +    def process(self, pyfile): +        self.pyfile = pyfile + +        if not self.file_exists(): +            self.offline() + +        if self.getConfig('type'): +            self.desired_fmt = self.getConfig('type') + +        pyfile.name = self.get_file_name() + self.desired_fmt +        self.download(self.get_file_url()) + + +    def download_html(self): +        url = self.pyfile.url +        self.html = self.load(url) + + +    def get_file_url(self): +        """ returns the absolute downloadable filepath +        """ +        if not self.html: +            self.download_html() + +        flashvar_pattern = re.compile('flashvars = ({.*?});', re.S) +        json_flashvar = flashvar_pattern.search(self.html) + +        if not json_flashvar: +            self.error(_("flashvar not found")) + +        j = clean_json(json_flashvar.group(1)) +        flashvars = json_loads(j) + +        if flashvars['srv']: +            srv_url = flashvars['srv'] + '/' +        else: +            self.error(_("srv_url not found")) + +        if flashvars['url_mode']: +            url_mode = flashvars['url_mode'] + + +        else: +            self.error(_("url_mode not found")) + +        if self.desired_fmt == ".mp4": +            file_url = re.search(r"<a href=\"" + srv_url + "(.+?)\"", self.html) +            if file_url is None: +                self.error(_("file_url not found")) +            file_url = file_url.group(1) +            long_url = srv_url + file_url +            self.logDebug("long_url = " + long_url) +        else: +            if flashvars['file']: +                file_url = unquote(flashvars['file']) +            else: +                self.error(_("file_url not found")) + +            if url_mode == '3': +                long_url = file_url +                self.logDebug("long_url = " + long_url) +            else: +                long_url = srv_url + "key=" + file_url +                self.logDebug("long_url = " + long_url) + +        return long_url + + +    def get_file_name(self): +        if not self.html: +            self.download_html() + +        pattern = r'<title>(.*?) - xHamster\.com</title>' +        name = re.search(pattern, self.html) +        if name is None: +            pattern = r'<h1 >(.*)</h1>' +            name = re.search(pattern, self.html) +            if name is None: +                pattern = r'http://[www.]+xhamster\.com/movies/.*/(.*?)\.html?' +                name = re.match(file_name_pattern, self.pyfile.url) +                if name is None: +                    pattern = r'<div id="element_str_id" style="display:none;">(.*)</div>' +                    name = re.search(pattern, self.html) +                    if name is None: +                        return "Unknown" + +        return name.group(1) + + +    def file_exists(self): +        """ returns True or False +        """ +        if not self.html: +            self.download_html() +        if re.search(r"(.*Video not found.*)", self.html): +            return False +        else: +            return True diff --git a/pyload/plugin/hoster/XVideosCom.py b/pyload/plugin/hoster/XVideosCom.py new file mode 100644 index 000000000..c5e2921cb --- /dev/null +++ b/pyload/plugin/hoster/XVideosCom.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +import re + +from urllib import unquote + +from pyload.plugin.Hoster import Hoster + + +class XVideosCom(Hoster): +    __name__    = "XVideos.com" +    __type__    = "hoster" +    __version__ = "0.10" + +    __pattern__ = r'http://(?:www\.)?xvideos\.com/video(\d+)' + +    __description__ = """XVideos.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [] + + +    def process(self, pyfile): +        site = self.load(pyfile.url) +        pyfile.name = "%s (%s).flv" % ( +            re.search(r"<h2>([^<]+)<span", site).group(1), +            re.match(self.__pattern__, pyfile.url).group(1), +        ) +        self.download(unquote(re.search(r"flv_url=([^&]+)&", site).group(1))) diff --git a/pyload/plugin/hoster/XdadevelopersCom.py b/pyload/plugin/hoster/XdadevelopersCom.py new file mode 100644 index 000000000..8ef79657f --- /dev/null +++ b/pyload/plugin/hoster/XdadevelopersCom.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -* +# +# Test links: +#   http://forum.xda-developers.com/devdb/project/dl/?id=10885 + +import re + +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class XdadevelopersCom(SimpleHoster): +    __name__    = "XdadevelopersCom" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'https?://(?:www\.)?forum\.xda-developers\.com/devdb/project/dl/\?id=\d+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Xda-developers.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de")] + + +    NAME_PATTERN    = r'<label>Filename:</label>\s*<div>\s*(?P<N>.*?)\n' +    SIZE_PATTERN    = r'<label>Size:</label>\s*<div>\s*(?P<S>[\d.,]+)(?P<U>[\w^_]+)' +    OFFLINE_PATTERN = r'</i> Device Filter</h3>' + + +    def setup(self): +        self.multiDL        = True +        self.resumeDownload = True +        self.chunkLimit     = 1 + + +    def handleFree(self, pyfile): +        self.link = pyfile.url + "&task=get"  #@TODO: Revert to `get={'task': "get"}` in 0.4.10 diff --git a/pyload/plugin/hoster/Xdcc.py b/pyload/plugin/hoster/Xdcc.py new file mode 100644 index 000000000..10d50369f --- /dev/null +++ b/pyload/plugin/hoster/Xdcc.py @@ -0,0 +1,210 @@ +# -*- coding: utf-8 -*- + +import re +import socket +import struct +import sys +import time + +from os import makedirs +from os.path import exists, join +from select import select + +from pyload.plugin.Hoster import Hoster +from pyload.utils import fs_join + + +class Xdcc(Hoster): +    __name__    = "Xdcc" +    __type__    = "hoster" +    __version__ = "0.32" + +    __config__ = [("nick", "str", "Nickname", "pyload"), +                ("ident", "str", "Ident", "pyloadident"), +                ("realname", "str", "Realname", "pyloadreal")] + +    __description__ = """Download from IRC XDCC bot""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.com")] + + +    def setup(self): +        self.debug = 0  # 0,1,2 +        self.timeout = 30 +        self.multiDL = False + + +    def process(self, pyfile): +        # change request type +        self.req = pyfile.m.core.requestFactory.getRequest(self.__name__, type="XDCC") + +        self.pyfile = pyfile +        for _i in xrange(0, 3): +            try: +                nmn = self.doDownload(pyfile.url) +                self.logDebug("Download of %s finished." % nmn) +                return +            except socket.error, e: +                if hasattr(e, "errno"): +                    errno = e.errno +                else: +                    errno = e.args[0] + +                if errno == 10054: +                    self.logDebug("Server blocked our ip, retry in 5 min") +                    self.setWait(300) +                    self.wait() +                    continue + +                self.fail(_("Failed due to socket errors. Code: %d") % errno) + +        self.fail(_("Server blocked our ip, retry again later manually")) + + +    def doDownload(self, url): +        self.pyfile.setStatus("waiting")  # real link + +        m = re.match(r'xdcc://(.*?)/#?(.*?)/(.*?)/#?(\d+)/?', url) +        server = m.group(1) +        chan = m.group(2) +        bot = m.group(3) +        pack = m.group(4) +        nick = self.getConfig('nick') +        ident = self.getConfig('ident') +        real = self.getConfig('realname') + +        temp = server.split(':') +        ln = len(temp) +        if ln == 2: +            host, port = temp +        elif ln == 1: +            host, port = temp[0], 6667 +        else: +            self.fail(_("Invalid hostname for IRC Server: %s") % server) + +        ####################### +        # CONNECT TO IRC AND IDLE FOR REAL LINK +        dl_time = time.time() + +        sock = socket.socket() +        sock.connect((host, int(port))) +        if nick == "pyload": +            nick = "pyload-%d" % (time.time() % 1000)  # last 3 digits +        sock.send("NICK %s\r\n" % nick) +        sock.send("USER %s %s bla :%s\r\n" % (ident, host, real)) + +        self.setWait(3) +        self.wait() + +        sock.send("JOIN #%s\r\n" % chan) +        sock.send("PRIVMSG %s :xdcc send #%s\r\n" % (bot, pack)) + +        # IRC recv loop +        readbuffer = "" +        done = False +        retry = None +        m = None +        while True: + +            # done is set if we got our real link +            if done: +                break + +            if retry: +                if time.time() > retry: +                    retry = None +                    dl_time = time.time() +                    sock.send("PRIVMSG %s :xdcc send #%s\r\n" % (bot, pack)) + +            else: +                if (dl_time + self.timeout) < time.time():  # todo: add in config +                    sock.send("QUIT :byebye\r\n") +                    sock.close() +                    self.fail(_("XDCC Bot did not answer")) + +            fdset = select([sock], [], [], 0) +            if sock not in fdset[0]: +                continue + +            readbuffer += sock.recv(1024) +            temp = readbuffer.split("\n") +            readbuffer = temp.pop() + +            for line in temp: +                if self.debug is 2: +                    print "*> " + unicode(line, errors='ignore') +                line = line.rstrip() +                first = line.split() + +                if first[0] == "PING": +                    sock.send("PONG %s\r\n" % first[1]) + +                if first[0] == "ERROR": +                    self.fail(_("IRC-Error: %s") % line) + +                msg = line.split(None, 3) +                if len(msg) != 4: +                    continue + +                msg = { +                    "origin": msg[0][1:], +                    "action": msg[1], +                    "target": msg[2], +                    "text": msg[3][1:] +                } + +                if nick == msg['target'][0:len(nick)] and "PRIVMSG" == msg['action']: +                    if msg['text'] == "\x01VERSION\x01": +                        self.logDebug("Sending CTCP VERSION") +                        sock.send("NOTICE %s :%s\r\n" % (msg['origin'], "pyLoad! IRC Interface")) +                    elif msg['text'] == "\x01TIME\x01": +                        self.logDebug("Sending CTCP TIME") +                        sock.send("NOTICE %s :%d\r\n" % (msg['origin'], time.time())) +                    elif msg['text'] == "\x01LAG\x01": +                        pass  # don't know how to answer + +                if not (bot == msg['origin'][0:len(bot)] +                        and nick == msg['target'][0:len(nick)] +                        and msg['action'] in ("PRIVMSG", "NOTICE")): +                    continue + +                if self.debug is 1: +                    print "%s: %s" % (msg['origin'], msg['text']) + +                if "You already requested that pack" in msg['text']: +                    retry = time.time() + 300 + +                if "you must be on a known channel to request a pack" in msg['text']: +                    self.fail(_("Wrong channel")) + +                m = re.match('\x01DCC SEND (.*?) (\d+) (\d+)(?: (\d+))?\x01', msg['text']) +                if m: +                    done = True + +        # get connection data +        ip = socket.inet_ntoa(struct.pack('L', socket.ntohl(int(m.group(2))))) +        port = int(m.group(3)) +        packname = m.group(1) + +        if len(m.groups()) > 3: +            self.req.filesize = int(m.group(4)) + +        self.pyfile.name = packname + +        download_folder = self.config['general']['download_folder'] +        filename = fs_join(download_folder, packname) + +        self.logInfo(_("Downloading %s from %s:%d") % (packname, ip, port)) + +        self.pyfile.setStatus("downloading") +        newname = self.req.download(ip, port, filename, sock, self.pyfile.setProgress) +        if newname and newname != filename: +            self.logInfo(_("%(name)s saved as %(newname)s") % {"name": self.pyfile.name, "newname": newname}) +            filename = newname + +        # kill IRC socket +        # sock.send("QUIT :byebye\r\n") +        sock.close() + +        self.lastDownload = filename +        return self.lastDownload diff --git a/pyload/plugin/hoster/YibaishiwuCom.py b/pyload/plugin/hoster/YibaishiwuCom.py new file mode 100644 index 000000000..01bd56763 --- /dev/null +++ b/pyload/plugin/hoster/YibaishiwuCom.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.utils import json_loads +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class YibaishiwuCom(SimpleHoster): +    __name__    = "YibaishiwuCom" +    __type__    = "hoster" +    __version__ = "0.14" + +    __pattern__ = r'http://(?:www\.)?(?:u\.)?115\.com/file/(?P<ID>\w+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """115.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    NAME_PATTERN = r'file_name: \'(?P<N>.+?)\'' +    SIZE_PATTERN = r'file_size: \'(?P<S>.+?)\'' +    OFFLINE_PATTERN = ur'<h3><i style="color:red;">ååïŒæåç äžååšïŒäžåŠšææçå§ïŒ</i></h3>' + +    LINK_FREE_PATTERN = r'(/\?ct=(pickcode|download)[^"\']+)' + + +    def handleFree(self, pyfile): +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.error(_("LINK_FREE_PATTERN not found")) + +        url = m.group(1) + +        self.logDebug(('FREEUSER' if m.group(2) == 'download' else 'GUEST') + ' URL', url) + +        res = json_loads(self.load("http://115.com" + url, decode=False)) +        if "urls" in res: +            mirrors = res['urls'] + +        elif "data" in res: +            mirrors = res['data'] + +        else: +            mirrors = None + +        for mr in mirrors: +            try: +                self.link = mr['url'].replace("\\", "") +                self.logDebug("Trying URL: " + self.link) +                break +            except Exception: +                continue +        else: +            self.fail(_("No working link found")) diff --git a/pyload/plugin/hoster/YoupornCom.py b/pyload/plugin/hoster/YoupornCom.py new file mode 100644 index 000000000..9aec3531a --- /dev/null +++ b/pyload/plugin/hoster/YoupornCom.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Hoster import Hoster + + +class YoupornCom(Hoster): +    __name__    = "YoupornCom" +    __type__    = "hoster" +    __version__ = "0.20" + +    __pattern__ = r'http://(?:www\.)?youporn\.com/watch/.+' + +    __description__ = """Youporn.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("willnix", "willnix@pyload.org")] + + +    def process(self, pyfile): +        self.pyfile = pyfile + +        if not self.file_exists(): +            self.offline() + +        pyfile.name = self.get_file_name() +        self.download(self.get_file_url()) + + +    def download_html(self): +        url = self.pyfile.url +        self.html = self.load(url, post={"user_choice": "Enter"}, cookies=False) + + +    def get_file_url(self): +        """ returns the absolute downloadable filepath +        """ +        if not self.html: +            self.download_html() + +        return re.search(r'(http://download\.youporn\.com/download/\d+\?save=1)">', self.html).group(1) + + +    def get_file_name(self): +        if not self.html: +            self.download_html() + +        file_name_pattern = r'<title>(.+) - ' +        return re.search(file_name_pattern, self.html).group(1).replace("&", "&").replace("/", "") + '.flv' + + +    def file_exists(self): +        """ returns True or False +        """ +        if not self.html: +            self.download_html() +        if re.search(r"(.*invalid video_id.*)", self.html): +            return False +        else: +            return True diff --git a/pyload/plugin/hoster/YourfilesTo.py b/pyload/plugin/hoster/YourfilesTo.py new file mode 100644 index 000000000..cd7ea0a4b --- /dev/null +++ b/pyload/plugin/hoster/YourfilesTo.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- + +import re + +from urllib import unquote + +from pyload.plugin.Hoster import Hoster + + +class YourfilesTo(Hoster): +    __name__    = "YourfilesTo" +    __type__    = "hoster" +    __version__ = "0.22" + +    __pattern__ = r'http://(?:www\.)?yourfiles\.(to|biz)/\?d=\w+' + +    __description__ = """Youfiles.to hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("skydancer", "skydancer@hasnomail.de")] + + +    def process(self, pyfile): +        self.pyfile = pyfile +        self.prepare() +        self.download(self.get_file_url()) + + +    def prepare(self): +        if not self.file_exists(): +            self.offline() + +        self.pyfile.name = self.get_file_name() + +        wait_time = self.get_waiting_time() +        self.setWait(wait_time) +        self.wait() + + +    def get_waiting_time(self): +        if not self.html: +            self.download_html() + +        #var zzipitime = 15; +        m = re.search(r'var zzipitime = (\d+);', self.html) +        if m: +            sec = int(m.group(1)) +        else: +            sec = 0 + +        return sec + + +    def download_html(self): +        url = self.pyfile.url +        self.html = self.load(url) + + +    def get_file_url(self): +        """ returns the absolute downloadable filepath +        """ +        url = re.search(r"var bla = '(.*?)';", self.html) +        if url: +            url = url.group(1) +            url = unquote(url.replace("http://http:/http://", "http://").replace("dumdidum", "")) +            return url +        else: +            self.error(_("Absolute filepath not found")) + + +    def get_file_name(self): +        if not self.html: +            self.download_html() + +        return re.search("<title>(.*)</title>", self.html).group(1) + + +    def file_exists(self): +        """ returns True or False +        """ +        if not self.html: +            self.download_html() + +        if re.search(r"HTTP Status 404", self.html): +            return False +        else: +            return True diff --git a/pyload/plugin/hoster/YoutubeCom.py b/pyload/plugin/hoster/YoutubeCom.py new file mode 100644 index 000000000..260b1ba3d --- /dev/null +++ b/pyload/plugin/hoster/YoutubeCom.py @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- + +import os +import re +import subprocess + +from urllib import unquote + +from pyload.plugin.Hoster import Hoster +from pyload.plugin.internal.SimpleHoster import replace_patterns +from pyload.utils import html_unescape + + +def which(program): +    """Works exactly like the unix command which +    Courtesy of http://stackoverflow.com/a/377028/675646""" + +    isExe = lambda x: os.path.isfile(x) and os.access(x, os.X_OK) + +    fpath, fname = os.path.split(program) + +    if fpath: +        if isExe(program): +            return program +    else: +        for path in os.environ['PATH'].split(os.pathsep): +            path = path.strip('"') +            exe_file = os.path.join(path, program) +            if isExe(exe_file): +                return exe_file + + +class YoutubeCom(Hoster): +    __name__    = "YoutubeCom" +    __type__    = "hoster" +    __version__ = "0.41" + +    __pattern__ = r'https?://(?:[^/]*\.)?(youtube\.com|youtu\.be)/watch\?(?:.*&)?v=.+' +    __config__  = [("quality", "sd;hd;fullhd;240p;360p;480p;720p;1080p;3072p", "Quality Setting"             , "hd" ), +                   ("fmt"    , "int"                                         , "FMT/ITAG Number (0 for auto)", 0    ), +                   (".mp4"   , "bool"                                        , "Allow .mp4"                  , True ), +                   (".flv"   , "bool"                                        , "Allow .flv"                  , True ), +                   (".webm"  , "bool"                                        , "Allow .webm"                 , False), +                   (".3gp"   , "bool"                                        , "Allow .3gp"                  , False), +                   ("3d"     , "bool"                                        , "Prefer 3D"                   , False)] + +    __description__ = """Youtube.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org"), +                       ("zoidberg", "zoidberg@mujmail.cz")] + + +    URL_REPLACEMENTS = [(r'youtu\.be/', 'youtube.com/')] + +    # Invalid characters that must be removed from the file name +    invalidChars = u'\u2605:?><"|\\' + +    # name, width, height, quality ranking, 3D +    formats = {5  : (".flv" , 400 , 240 , 1 , False), +               6  : (".flv" , 640 , 400 , 4 , False), +               17 : (".3gp" , 176 , 144 , 0 , False), +               18 : (".mp4" , 480 , 360 , 2 , False), +               22 : (".mp4" , 1280, 720 , 8 , False), +               43 : (".webm", 640 , 360 , 3 , False), +               34 : (".flv" , 640 , 360 , 4 , False), +               35 : (".flv" , 854 , 480 , 6 , False), +               36 : (".3gp" , 400 , 240 , 1 , False), +               37 : (".mp4" , 1920, 1080, 9 , False), +               38 : (".mp4" , 4096, 3072, 10, False), +               44 : (".webm", 854 , 480 , 5 , False), +               45 : (".webm", 1280, 720 , 7 , False), +               46 : (".webm", 1920, 1080, 9 , False), +               82 : (".mp4" , 640 , 360 , 3 , True ), +               83 : (".mp4" , 400 , 240 , 1 , True ), +               84 : (".mp4" , 1280, 720 , 8 , True ), +               85 : (".mp4" , 1920, 1080, 9 , True ), +               100: (".webm", 640 , 360 , 3 , True ), +               101: (".webm", 640 , 360 , 4 , True ), +               102: (".webm", 1280, 720 , 8 , True )} + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True + + +    def process(self, pyfile): +        pyfile.url = replace_patterns(pyfile.url, self.URL_REPLACEMENTS) +        html       = self.load(pyfile.url, decode=True) + +        if re.search(r'<div id="player-unavailable" class="\s*player-width player-height\s*">', html): +            self.offline() + +        if "We have been receiving a large volume of requests from your network." in html: +            self.tempOffline() + +        #get config +        use3d = self.getConfig('3d') + +        if use3d: +            quality = {"sd": 82, "hd": 84, "fullhd": 85, "240p": 83, "360p": 82, +                       "480p": 82, "720p": 84, "1080p": 85, "3072p": 85} +        else: +            quality = {"sd": 18, "hd": 22, "fullhd": 37, "240p": 5, "360p": 18, +                       "480p": 35, "720p": 22, "1080p": 37, "3072p": 38} + +        desired_fmt = self.getConfig('fmt') + +        if not desired_fmt: +            desired_fmt = quality.get(self.getConfig('quality'), 18) + +        elif desired_fmt not in self.formats: +            self.logWarning(_("FMT %d unknown, using default") % desired_fmt) +            desired_fmt = 0 + +        #parse available streams +        streams = re.search(r'"url_encoded_fmt_stream_map":"(.+?)",', html).group(1) +        streams = [x.split('\u0026') for x in streams.split(',')] +        streams = [dict((y.split('=', 1)) for y in x) for x in streams] +        streams = [(int(x['itag']), unquote(x['url'])) for x in streams] + +        # self.logDebug("Found links: %s" % streams) + +        self.logDebug("AVAILABLE STREAMS: %s" % [x[0] for x in streams]) + +        #build dictionary of supported itags (3D/2D) +        allowed = lambda x: self.getConfig(self.formats[x][0]) +        streams = [x for x in streams if x[0] in self.formats and allowed(x[0])] + +        if not streams: +            self.fail(_("No available stream meets your preferences")) + +        fmt_dict = dict([x for x in streams if self.formats[x[0]][4] == use3d] or streams) + +        self.logDebug("DESIRED STREAM: ITAG:%d (%s) %sfound, %sallowed" % +                      (desired_fmt, "%s %dx%d Q:%d 3D:%s" % self.formats[desired_fmt], +                       "" if desired_fmt in fmt_dict else "NOT ", "" if allowed(desired_fmt) else "NOT ")) + +        #return fmt nearest to quality index +        if desired_fmt in fmt_dict and allowed(desired_fmt): +            fmt = desired_fmt +        else: +            sel  = lambda x: self.formats[x][3]  # select quality index +            comp = lambda x, y: abs(sel(x) - sel(y)) + +            self.logDebug("Choosing nearest fmt: %s" % [(x, allowed(x), comp(x, desired_fmt)) for x in fmt_dict.keys()]) + +            fmt = reduce(lambda x, y: x if comp(x, desired_fmt) <= comp(y, desired_fmt) and +                         sel(x) > sel(y) else y, fmt_dict.keys()) + +        self.logDebug("Chosen fmt: %s" % fmt) + +        url = fmt_dict[fmt] + +        self.logDebug("URL: %s" % url) + +        #set file name +        file_suffix = self.formats[fmt][0] if fmt in self.formats else ".flv" +        file_name_pattern = '<meta name="title" content="(.+?)">' +        name = re.search(file_name_pattern, html).group(1).replace("/", "") + +        # Cleaning invalid characters from the file name +        name = name.encode('ascii', 'replace') +        for c in self.invalidChars: +            name = name.replace(c, '_') + +        pyfile.name = html_unescape(name) + +        time = re.search(r"t=((\d+)m)?(\d+)s", pyfile.url) +        ffmpeg = which("ffmpeg") +        if ffmpeg and time: +            m, s = time.groups()[1:] +            if m is None: +                m = "0" + +            pyfile.name += " (starting at %s:%s)" % (m, s) + +        pyfile.name += file_suffix +        filename     = self.download(url) + +        if ffmpeg and time: +            inputfile = filename + "_" +            os.rename(filename, inputfile) + +            subprocess.call([ +                ffmpeg, +                "-ss", "00:%s:%s" % (m, s), +                "-i", inputfile, +                "-vcodec", "copy", +                "-acodec", "copy", +                filename]) + +            os.remove(inputfile) diff --git a/pyload/plugin/hoster/ZDF.py b/pyload/plugin/hoster/ZDF.py new file mode 100644 index 000000000..da6d6448e --- /dev/null +++ b/pyload/plugin/hoster/ZDF.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +import re + +from xml.etree.ElementTree import fromstring + +from pyload.plugin.Hoster import Hoster + + +# Based on zdfm by Roland Beermann (http://github.com/enkore/zdfm/) +class ZDF(Hoster): +    __name__    = "ZDF Mediathek" +    __type__    = "hoster" +    __version__ = "0.80" + +    __pattern__ = r'http://(?:www\.)?zdf\.de/ZDFmediathek/\D*(\d+)\D*' + +    __description__ = """ZDF.de hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [] + +    XML_API = "http://www.zdf.de/ZDFmediathek/xmlservice/web/beitragsDetails?id=%i" + + +    @staticmethod +    def video_key(video): +        return ( +            int(video.findtext("videoBitrate", "0")), +            any(f.text == "progressive" for f in video.iter("facet")), +        ) + + +    @staticmethod +    def video_valid(video): +        return video.findtext("url").startswith("http") and video.findtext("url").endswith(".mp4") and \ +               video.findtext("facets/facet").startswith("progressive") + + +    @staticmethod +    def get_id(url): +        return int(re.search(r"\D*(\d{4,})\D*", url).group(1)) + + +    def process(self, pyfile): +        xml = fromstring(self.load(self.XML_API % self.get_id(pyfile.url))) + +        status = xml.findtext("./status/statuscode") +        if status != "ok": +            self.fail(_("Error retrieving manifest")) + +        video = xml.find("video") +        title = video.findtext("information/title") + +        pyfile.name = title + +        target_url = sorted((v for v in video.iter("formitaet") if self.video_valid(v)), +                            key=self.video_key)[-1].findtext("url") + +        self.download(target_url) diff --git a/pyload/plugin/hoster/ZShareNet.py b/pyload/plugin/hoster/ZShareNet.py new file mode 100644 index 000000000..fb02370f5 --- /dev/null +++ b/pyload/plugin/hoster/ZShareNet.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.DeadHoster import DeadHoster + + +class ZShareNet(DeadHoster): +    __name__    = "ZShareNet" +    __type__    = "hoster" +    __version__ = "0.21" + +    __pattern__ = r'https?://(?:ww[2w]\.)?zshares?\.net/.+' +    __config__  = [] + +    __description__ = """ZShare.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("espes", ""), +                       ("Cptn Sandwich", "")] diff --git a/pyload/plugin/hoster/ZeveraCom.py b/pyload/plugin/hoster/ZeveraCom.py new file mode 100644 index 000000000..96bbf3455 --- /dev/null +++ b/pyload/plugin/hoster/ZeveraCom.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from pyload.plugin.internal.MultiHoster import MultiHoster + + +class ZeveraCom(MultiHoster): +    __name__    = "ZeveraCom" +    __type__    = "hoster" +    __version__ = "0.29" + +    __pattern__ = r'https?://(?:www\.)zevera\.com/(getFiles\.ashx|Members/download\.ashx)\?.*ourl=.+' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Zevera.com multi-hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    def handlePremium(self, pyfile): +        self.link = "https://%s/getFiles.ashx?ourl=%s" % (self.account.HOSTER_DOMAIN, pyfile.url) + + +    def checkFile(self, rules={}): +        if self.checkDownload({"error": 'action="ErrorDownload.aspx'}): +            self.fail(_("Error response received")) + +        return super(ZeveraCom, self).checkFile(rules) diff --git a/pyload/plugin/hoster/ZippyshareCom.py b/pyload/plugin/hoster/ZippyshareCom.py new file mode 100644 index 000000000..e4a05d634 --- /dev/null +++ b/pyload/plugin/hoster/ZippyshareCom.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- + +import re +import urllib + +from BeautifulSoup import BeautifulSoup + +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.internal.SimpleHoster import SimpleHoster + + +class ZippyshareCom(SimpleHoster): +    __name__    = "ZippyshareCom" +    __type__    = "hoster" +    __version__ = "0.78" + +    __pattern__ = r'http://www\d{0,2}\.zippyshare\.com/v(/|iew\.jsp.*key=)(?P<KEY>[\w^_]+)' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Zippyshare.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com"), +                       ("sebdelsol", "seb.morin@gmail.com")] + + +    COOKIES = [("zippyshare.com", "ziplocale", "en")] + +    NAME_PATTERN    = r'(<title>Zippyshare.com - |"/)(?P<N>[^/]+)(</title>|";)' +    SIZE_PATTERN    = r'>Size:.+?">(?P<S>[\d.,]+) (?P<U>[\w^_]+)' +    OFFLINE_PATTERN = r'does not exist (anymore )?on this server<' + +    LINK_PREMIUM_PATTERN = r"document.location = '(.+?)'" + + +    def setup(self): +        self.chunkLimit     = -1 +        self.multiDL        = True +        self.resumeDownload = True + + +    def handleFree(self, pyfile): +        recaptcha   = ReCaptcha(self) +        captcha_key = recaptcha.detect_key() + +        if captcha_key: +            try: +                self.link = re.search(self.LINK_PREMIUM_PATTERN, self.html) +                recaptcha.challenge() + +            except Exception, e: +                self.error(e) + +        else: +            self.link = self.get_link() + +        if self.link and pyfile.name == 'file.html': +            pyfile.name = urllib.unquote(self.link.split('/')[-1]) + + +    def get_link(self): +        # get all the scripts inside the html body +        soup = BeautifulSoup(self.html) +        scripts = (s.getText().strip() for s in soup.body.findAll('script', type='text/javascript')) + +        # meant to be populated with the initialization of all the DOM elements found in the scripts +        initScripts = set() + +        def replElementById(element): +            id   = element.group(1) # id might be either 'x' (a real id) or x (a variable) +            attr = element.group(4)  # attr might be None + +            varName = re.sub(r'-', '', 'GVAR[%s+"_%s"]' %(id, attr)) + +            realid = id.strip('"\'') +            if id != realid: #id is not a variable, so look for realid.attr in the html +                initValues = filter(None, [elt.get(attr, None) for elt in soup.findAll(id=realid)]) +                initValue  = '"%s"' % initValues[-1] if initValues else 'null' +                initScripts.add('%s = %s;' % (varName, initValue)) + +            return varName + +        # handle all getElementById +        reVar = r'document.getElementById\(([\'"\w-]+)\)(\.)?(getAttribute\([\'"])?(\w+)?([\'"]\))?' +        scripts = [re.sub(reVar, replElementById, script) for script in scripts if script] + +        # add try/catch in JS to handle deliberate errors +        scripts = ['\n'.join(('try{', script, '} catch(err){}')) for script in scripts] + +        # get the file's url by evaluating all the scripts +        scripts = ['var GVAR = {}'] + list(initScripts)  + scripts + ['GVAR["dlbutton_href"]'] +        return self.js.eval('\n'.join(scripts)) diff --git a/pyload/plugin/hoster/__init__.py b/pyload/plugin/hoster/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/pyload/plugin/hoster/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/pyload/plugin/internal/BasePlugin.py b/pyload/plugin/internal/BasePlugin.py new file mode 100644 index 000000000..7c83ddef0 --- /dev/null +++ b/pyload/plugin/internal/BasePlugin.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- + +import re + +from urllib import unquote +from urlparse import urljoin, urlparse + +from pyload.network.HTTPRequest import BadHeader +from pyload.plugin.internal.SimpleHoster import getFileURL +from pyload.plugin.Hoster import Hoster + + +class BasePlugin(Hoster): +    __name__    = "BasePlugin" +    __type__    = "hoster" +    __version__ = "0.41" + +    __pattern__ = r'^unmatchable$' + +    __description__ = """Base plugin when any other didnt fit""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    @classmethod +    def getInfo(cls, url="", html=""):  #@TODO: Move to hoster class in 0.4.10 +        url   = unquote(url) +        url_p = urlparse(url) +        return {'name'  : (url_p.path.split('/')[-1] +                           or url_p.query.split('=', 1)[::-1][0].split('&', 1)[0] +                           or url_p.netloc.split('.', 1)[0]), +                'size'  : 0, +                'status': 3 if url else 8, +                'url'   : url} + + +    def setup(self): +        self.chunkLimit     = -1 +        self.multiDL        = True +        self.resumeDownload = True + + +    #: Work-around to `filename*=UTF-8` bug; remove in 0.4.10 +    def download(self, url, get={}, post={}, ref=True, cookies=True, disposition=False): +        try: +            if disposition: +                content = urllib2.urlopen(url).info()['Content-Disposition'].split(';') +                self.pyfile.name = content[1].split('filename=')[1][1:-1] or self.pyfile.name +        finally: +            return super(BasePlugin, self).download(url, get, post, ref, cookies, False) + + +    def process(self, pyfile): +        """main function""" + +        pyfile.name = self.getInfo(pyfile.url)['name'] + +        if not pyfile.url.startswith("http"): +            self.fail(_("No plugin matched")) + +        for _i in xrange(5): +            try: +                link = getFileURL(self, unquote(pyfile.url)) + +                if link: +                    self.download(link, ref=False, disposition=True) +                else: +                    self.fail(_("File not found")) + +            except BadHeader, e: +                if e.code is 404: +                    self.offline() + +                elif e.code in (401, 403): +                    self.logDebug("Auth required", "Received HTTP status code: %d" % e.code) + +                    account = self.core.accountManager.getAccountPlugin('Http') +                    servers = [x['login'] for x in account.getAllAccounts()] +                    server  = urlparse(pyfile.url).netloc + +                    if server in servers: +                        self.logDebug("Logging on to %s" % server) +                        self.req.addAuth(account.getAccountData(server)['password']) +                    else: +                        pwd = self.getPassword() +                        if ':' in pwd: +                            self.req.addAuth(pwd) +                        else: +                            self.fail(_("Authorization required")) +                else: +                    self.fail(e) +            else: +                break +        else: +            self.fail(_("No file downloaded"))  #@TODO: Move to hoster class in 0.4.10 + +        errmsg = self.checkDownload({'Empty file'   : re.compile(r'\A\s*\Z'), +                                     'Html error'   : re.compile(r'\A(?:\s*<.+>)?((?:[\w\s]*(?:[Ee]rror|ERROR)\s*\:?)?\s*\d{3})(?:\Z|\s+)'), +                                     'Html file'    : re.compile(r'\A\s*<!DOCTYPE html'), +                                     'Request error': re.compile(r'([Aa]n error occured while processing your request)')}) +        if not errmsg: +            return + +        try: +            errmsg += " | " + self.lastCheck.group(1).strip() +        except Exception: +            pass + +        self.logWarning("Check result: " + errmsg, "Waiting 1 minute and retry") +        self.retry(3, 60, errmsg) diff --git a/pyload/plugin/internal/DeadCrypter.py b/pyload/plugin/internal/DeadCrypter.py new file mode 100644 index 000000000..ddeb0431d --- /dev/null +++ b/pyload/plugin/internal/DeadCrypter.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Crypter import Crypter as _Crypter + + +class DeadCrypter(_Crypter): +    __name__    = "DeadCrypter" +    __type__    = "crypter" +    __version__ = "0.04" + +    __pattern__ = r'^unmatchable$' + +    __description__ = """Crypter is no longer available""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    @classmethod +    def apiInfo(cls, url="", get={}, post={}): +        api = super(DeadCrypter, self).apiInfo(url, get, post) +        api['status'] = 1 +        return api + + +    def setup(self): +        self.pyfile.error = "Crypter is no longer available" +        self.offline()  #@TODO: self.offline("Crypter is no longer available") diff --git a/pyload/plugin/internal/DeadHoster.py b/pyload/plugin/internal/DeadHoster.py new file mode 100644 index 000000000..1596943ae --- /dev/null +++ b/pyload/plugin/internal/DeadHoster.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.Hoster import Hoster as _Hoster + + +class DeadHoster(_Hoster): +    __name__    = "DeadHoster" +    __type__    = "hoster" +    __version__ = "0.14" + +    __pattern__ = r'^unmatchable$' + +    __description__ = """Hoster is no longer available""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    @classmethod +    def apiInfo(cls, url="", get={}, post={}): +        api = super(DeadHoster, self).apiInfo(url, get, post) +        api['status'] = 1 +        return api + + +    def setup(self): +        self.pyfile.error = "Hoster is no longer available" +        self.offline()  #@TODO: self.offline("Hoster is no longer available") diff --git a/pyload/plugin/internal/MultiHook.py b/pyload/plugin/internal/MultiHook.py new file mode 100644 index 000000000..4ca158c24 --- /dev/null +++ b/pyload/plugin/internal/MultiHook.py @@ -0,0 +1,284 @@ +# -*- coding: utf-8 -*- + +import re +import time +import traceback + +from pyload.plugin.Hook import Hook +from pyload.utils import decode, remove_chars + + +class MultiHook(Hook): +    __name__    = "MultiHook" +    __type__    = "hook" +    __version__ = "0.44" + +    __config__  = [("pluginmode"    , "all;listed;unlisted", "Use for plugins"              , "all"), +                   ("pluginlist"    , "str"                , "Plugin list (comma separated)", ""   ), +                   ("reload"        , "bool"               , "Reload plugin list"           , True ), +                   ("reloadinterval", "int"                , "Reload interval in hours"     , 12   )] + +    __description__ = """Hook plugin for multi hoster/crypter""" +    __license__     = "GPLv3" +    __authors__     = [("pyLoad Team"   , "admin@pyload.org" ), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    MIN_RELOAD_INTERVAL = 1 * 60 * 60  #: 1 hour + +    DOMAIN_REPLACEMENTS = [(r'180upload\.com'  , "hundredeightyupload.com"), +                           (r'bayfiles\.net'   , "bayfiles.com"           ), +                           (r'cloudnator\.com' , "shragle.com"            ), +                           (r'dfiles\.eu'      , "depositfiles.com"       ), +                           (r'easy-share\.com' , "crocko.com"             ), +                           (r'freakshare\.net' , "freakshare.com"         ), +                           (r'hellshare\.com'  , "hellshare.cz"           ), +                           (r'ifile\.it'       , "filecloud.io"           ), +                           (r'nowdownload\.\w+', "nowdownload.sx"         ), +                           (r'nowvideo\.\w+'   , "nowvideo.sx"            ), +                           (r'putlocker\.com'  , "firedrive.com"          ), +                           (r'share-?rapid\.cz', "multishare.cz"          ), +                           (r'ul\.to'          , "uploaded.to"            ), +                           (r'uploaded\.net'   , "uploaded.to"            ), +                           (r'uploadhero\.co'  , "uploadhero.com"         ), +                           (r'zshares\.net'    , "zshare.net"             ), +                           (r'^1'              , "one"                    ), +                           (r'^2'              , "two"                    ), +                           (r'^3'              , "three"                  ), +                           (r'^4'              , "four"                   ), +                           (r'^5'              , "five"                   ), +                           (r'^6'              , "six"                    ), +                           (r'^7'              , "seven"                  ), +                           (r'^8'              , "eight"                  ), +                           (r'^9'              , "nine"                   ), +                           (r'^0'              , "zero"                   )] + + +    def setup(self): +        self.plugins       = [] +        self.supported     = [] +        self.new_supported = [] + +        self.account      = None +        self.pluginclass  = None +        self.pluginmodule = None +        self.pluginname   = None +        self.plugintype   = None + +        self.initPlugin() + + +    def initPlugin(self): +        self.pluginname         = self.__class__.__name__.rsplit("Hook", 1)[0] +        plugin, self.plugintype = self.core.pluginManager.findPlugin(self.pluginname) + +        if plugin: +            self.pluginmodule = self.core.pluginManager.loadModule(self.plugintype, self.pluginname) +            self.pluginclass  = getattr(self.pluginmodule, self.pluginname) +        else: +            self.logWarning("Hook plugin will be deactivated due missing plugin reference") +            self.setConfig('activated', False) + + +    def loadAccount(self): +        self.account = self.core.accountManager.getAccountPlugin(self.pluginname) + +        if self.account and not self.account.canUse(): +            self.account = None + +        if not self.account and hasattr(self.pluginclass, "LOGIN_ACCOUNT") and self.pluginclass.LOGIN_ACCOUNT: +            self.logWarning("Hook plugin will be deactivated due missing account reference") +            self.setConfig('activated', False) + + +    def activate(self): +        self.initPeriodical(threaded=True) + + +    def getURL(self, *args, **kwargs):  #@TODO: Remove in 0.4.10 +        """ see HTTPRequest for argument list """ +        h = pyreq.getHTTPRequest(timeout=120) +        try: +            if not 'decode' in kwargs: +                kwargs['decode'] = True +            rep = h.load(*args, **kwargs) +        finally: +            h.close() + +        return rep + + +    def getConfig(self, option, default=''):  #@TODO: Remove in 0.4.10 +        """getConfig with default value - sublass may not implements all config options""" +        try: +            return self.getConf(option) + +        except KeyError: +            return default + + +    def pluginsCached(self): +        if self.plugins: +            return self.plugins + +        for _i in xrange(2): +            try: +                pluginset = self._pluginSet(self.getHosters() if self.plugintype == "hoster" else self.getCrypters()) +                break + +            except Exception, e: +                self.logDebug(e, "Waiting 1 minute and retry") +                time.sleep(60) +        else: +            self.logWarning(_("Fallback to default reload interval due plugin parse error")) +            self.interval = self.MIN_RELOAD_INTERVAL +            return list() + +        try: +            configmode = self.getConfig('pluginmode', 'all') +            if configmode in ("listed", "unlisted"): +                pluginlist = self.getConfig('pluginlist', '').replace('|', ',').replace(';', ',').split(',') +                configset  = self._pluginSet(pluginlist) + +                if configmode == "listed": +                    pluginset &= configset +                else: +                    pluginset -= configset + +        except Exception, e: +            self.logError(e) + +        self.plugins = list(pluginset) + +        return self.plugins + + +    def _pluginSet(self, plugins): +        regexp  = re.compile(r'^[\w\-.^_]{3,63}\.[a-zA-Z]{2,}$', re.U) +        plugins = [decode(p.strip()).lower() for p in plugins if regexp.match(p.strip())] + +        for r in self.DOMAIN_REPLACEMENTS: +            rf, rt  = r +            repr    = re.compile(rf, re.I|re.U) +            plugins = [re.sub(rf, rt, p) if repr.match(p) else p for p in plugins] + +        return set(plugins) + + +    def getHosters(self): +        """Load list of supported hoster + +        :return: List of domain names +        """ +        raise NotImplementedError + + +    def getCrypters(self): +        """Load list of supported crypters + +        :return: List of domain names +        """ +        raise NotImplementedError + + +    def periodical(self): +        """reload plugin list periodically""" +        self.loadAccount() + +        if self.getConfig('reload', True): +            self.interval = max(self.getConfig('reloadinterval', 12) * 60 * 60, self.MIN_RELOAD_INTERVAL) +        else: +            self.core.scheduler.removeJob(self.cb) +            self.cb = None + +        self.logInfo(_("Reloading supported %s list") % self.plugintype) + +        old_supported = self.supported + +        self.supported     = [] +        self.new_supported = [] +        self.plugins       = [] + +        self.overridePlugins() + +        old_supported = [plugin for plugin in old_supported if plugin not in self.supported] + +        if old_supported: +            self.logDebug("Unload: %s" % ", ".join(old_supported)) +            for plugin in old_supported: +                self.unloadPlugin(plugin) + + +    def overridePlugins(self): +        excludedList = [] + +        if self.plugintype == "hoster": +            pluginMap    = dict((name.lower(), name) for name in self.core.pluginManager.hosterPlugins.iterkeys()) +            accountList  = [account.type.lower() for account in self.core.api.getAccounts(False) if account.valid and account.premium] +        else: +            pluginMap    = {} +            accountList  = [name[::-1].replace("Folder"[::-1], "", 1).lower()[::-1] for name in self.core.pluginManager.crypterPlugins.iterkeys()] + +        for plugin in self.pluginsCached(): +            name = remove_chars(plugin, "-.") + +            if name in accountList: +                excludedList.append(plugin) +            else: +                if name in pluginMap: +                    self.supported.append(pluginMap[name]) +                else: +                    self.new_supported.append(plugin) + +        if not self.supported and not self.new_supported: +            self.logError(_("No %s loaded") % self.plugintype) +            return + +        # inject plugin plugin +        self.logDebug("Overwritten %ss: %s" % (self.plugintype, ", ".join(sorted(self.supported)))) + +        for plugin in self.supported: +            hdict = self.core.pluginManager.plugins[self.plugintype][plugin] +            hdict['new_module'] = self.pluginmodule +            hdict['new_name']   = self.pluginname + +        if excludedList: +            self.logInfo(_("%ss not overwritten: %s") % (self.plugintype.capitalize(), ", ".join(sorted(excludedList)))) + +        if self.new_supported: +            plugins = sorted(self.new_supported) + +            self.logDebug("New %ss: %s" % (self.plugintype, ", ".join(plugins))) + +            # create new regexp +            regexp = r'.*(?P<DOMAIN>%s).*' % "|".join(x.replace('.', '\.') for x in plugins) +            if hasattr(self.pluginclass, "__pattern__") and isinstance(self.pluginclass.__pattern__, basestring) and '://' in self.pluginclass.__pattern__: +                regexp = r'%s|%s' % (self.pluginclass.__pattern__, regexp) + +            self.logDebug("Regexp: %s" % regexp) + +            hdict = self.core.pluginManager.plugins[self.plugintype][self.pluginname] +            hdict['pattern'] = regexp +            hdict['re']      = re.compile(regexp) + + +    def unloadPlugin(self, plugin): +        hdict = self.core.pluginManager.plugins[self.plugintype][plugin] +        if "module" in hdict: +            hdict.pop('module', None) + +        if "new_module" in hdict: +            hdict.pop('new_module', None) +            hdict.pop('new_name', None) + + +    def deactivate(self): +        """Remove override for all plugins. Scheduler job is removed by hookmanager""" +        for plugin in self.supported: +            self.unloadPlugin(plugin) + +        # reset pattern +        hdict = self.core.pluginManager.plugins[self.plugintype][self.pluginname] + +        hdict['pattern'] = getattr(self.pluginclass, "__pattern__", r'^unmatchable$') +        hdict['re']      = re.compile(hdict['pattern']) diff --git a/pyload/plugin/internal/MultiHoster.py b/pyload/plugin/internal/MultiHoster.py new file mode 100644 index 000000000..5f70335dd --- /dev/null +++ b/pyload/plugin/internal/MultiHoster.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugin.Plugin import Fail, Retry +from pyload.plugin.internal.SimpleHoster import SimpleHoster, replace_patterns, set_cookies + + +class MultiHoster(SimpleHoster): +    __name__    = "MultiHoster" +    __type__    = "hoster" +    __version__ = "0.39" + +    __pattern__ = r'^unmatchable$' +    __config__  = [("use_premium" , "bool", "Use premium account if available"    , True), +                   ("revertfailed", "bool", "Revert to standard download if fails", True)] + +    __description__ = """Multi hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    LOGIN_ACCOUNT = True + + +    def setup(self): +        self.chunkLimit     = 1 +        self.multiDL        = bool(self.account) +        self.resumeDownload = self.premium + + +    def prepare(self): +        self.info     = {} +        self.html     = "" +        self.link     = ""     #@TODO: Move to hoster class in 0.4.10 +        self.directDL = False  #@TODO: Move to hoster class in 0.4.10 + +        if not self.getConfig('use_premium', True): +            self.retryFree() + +        if self.LOGIN_ACCOUNT and not self.account: +            self.fail(_("Required account not found")) + +        self.req.setOption("timeout", 120) + +        if isinstance(self.COOKIES, list): +            set_cookies(self.req.cj, self.COOKIES) + +        if self.DIRECT_LINK is None: +            self.directDL = self.__pattern__ != r'^unmatchable$' and re.match(self.__pattern__, self.pyfile.url) +        else: +            self.directDL = self.DIRECT_LINK + +        self.pyfile.url = replace_patterns(self.pyfile.url, self.URL_REPLACEMENTS) + + +    def process(self, pyfile): +        try: +            self.prepare() + +            if self.directDL: +                self.checkInfo() +                self.logDebug("Looking for direct download link...") +                self.handleDirect(pyfile) + +            if not self.link and not self.lastDownload: +                self.preload() + +                self.checkErrors() +                self.checkStatus(getinfo=False) + +                if self.premium and (not self.CHECK_TRAFFIC or self.checkTrafficLeft()): +                    self.logDebug("Handled as premium download") +                    self.handlePremium(pyfile) + +                elif not self.LOGIN_ACCOUNT or (not self.CHECK_TRAFFIC or self.checkTrafficLeft()): +                    self.logDebug("Handled as free download") +                    self.handleFree(pyfile) + +            self.downloadLink(self.link, True) +            self.checkFile() + +        except Fail, e:  #@TODO: Move to PluginThread in 0.4.10 +            if self.premium: +                self.logWarning(_("Premium download failed")) +                self.retryFree() + +            elif self.getConfig('revertfailed', True) \ +                 and "new_module" in self.core.pluginManager.hosterPlugins[self.__class__.__name__]: +                hdict = self.core.pluginManager.hosterPlugins[self.__class__.__name__] + +                tmp_module = hdict['new_module'] +                tmp_name   = hdict['new_name'] +                hdict.pop('new_module', None) +                hdict.pop('new_name', None) + +                pyfile.initPlugin() + +                hdict['new_module'] = tmp_module +                hdict['new_name']   = tmp_name + +                raise Retry(_("Revert to original hoster plugin")) + +            else: +                raise Fail(e) + + +    def handlePremium(self, pyfile): +        return self.handleFree(pyfile) + + +    def handleFree(self, pyfile): +        if self.premium: +            raise NotImplementedError +        else: +            self.fail(_("Required premium account not found")) diff --git a/pyload/plugin/internal/SimpleCrypter.py b/pyload/plugin/internal/SimpleCrypter.py new file mode 100644 index 000000000..d0380c0d9 --- /dev/null +++ b/pyload/plugin/internal/SimpleCrypter.py @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin, urlparse + +from pyload.plugin.Crypter import Crypter +from pyload.plugin.internal.SimpleHoster import SimpleHoster, replace_patterns, set_cookies +from pyload.utils import fixup + + +class SimpleCrypter(Crypter, SimpleHoster): +    __name__    = "SimpleCrypter" +    __type__    = "crypter" +    __version__ = "0.43" + +    __pattern__ = r'^unmatchable$' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True),  #: Overrides core.config['general']['folder_per_package'] +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Simple decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    """ +    Following patterns should be defined by each crypter: + +      LINK_PATTERN: Download link or regex to catch links in group(1) +        example: LINK_PATTERN = r'<div class="link"><a href="(.+?)"' + +      NAME_PATTERN: (optional) folder name or page title +        example: NAME_PATTERN = r'<title>Files of: (?P<N>[^<]+) folder</title>' + +      OFFLINE_PATTERN: (optional) Checks if the page is unreachable +        example: OFFLINE_PATTERN = r'File (deleted|not found)' + +      TEMP_OFFLINE_PATTERN: (optional) Checks if the page is temporarily unreachable +        example: TEMP_OFFLINE_PATTERN = r'Server maintainance' + + +    You can override the getLinks method if you need a more sophisticated way to extract the links. + + +    If the links are splitted on multiple pages you can define the PAGES_PATTERN regex: + +      PAGES_PATTERN: (optional) group(1) should be the number of overall pages containing the links +        example: PAGES_PATTERN = r'Pages: (\d+)' + +    and its loadPage method: + + +      def loadPage(self, page_n): +          return the html of the page number page_n +    """ + +    LINK_PATTERN = None + +    NAME_REPLACEMENTS = [("&#?\w+;", fixup)] +    URL_REPLACEMENTS  = [] + +    TEXT_ENCODING = False  #: Set to True or encoding name if encoding in http header is not correct +    COOKIES       = True  #: or False or list of tuples [(domain, name, value)] + +    LOGIN_ACCOUNT = False +    LOGIN_PREMIUM = False + + +    def prepare(self): +        self.pyfile.error = ""  #@TODO: Remove in 0.4.10 + +        self.info  = {} +        self.html  = "" +        self.links = []  #@TODO: Move to hoster class in 0.4.10 + +        if self.LOGIN_PREMIUM and not self.premium: +            self.fail(_("Required premium account not found")) + +        if self.LOGIN_ACCOUNT and not self.account: +            self.fail(_("Required account not found")) + +        self.req.setOption("timeout", 120) + +        if isinstance(self.COOKIES, list): +            set_cookies(self.req.cj, self.COOKIES) + +        self.pyfile.url = replace_patterns(self.pyfile.url, self.URL_REPLACEMENTS) + + +    def decrypt(self, pyfile): +        self.prepare() + +        self.preload() +        self.checkInfo() + +        self.links = self.getLinks() + +        if hasattr(self, 'PAGES_PATTERN') and hasattr(self, 'loadPage'): +            self.handlePages(pyfile) + +        self.logDebug("Package has %d links" % len(self.links)) + +        if self.links: +            self.packages = [(self.info['name'], self.links, self.info['folder'])] + +        elif not self.urls and not self.packages:  #@TODO: Remove in 0.4.10 +            self.fail(_("No link grabbed")) + + +    def checkNameSize(self, getinfo=True): +        if not self.info or getinfo: +            self.logDebug("File info (BEFORE): %s" % self.info) +            self.info.update(self.getInfo(self.pyfile.url, self.html)) +            self.logDebug("File info (AFTER): %s"  % self.info) + +        try: +            url  = self.info['url'].strip() +            name = self.info['name'].strip() +            if name and name != url: +                self.pyfile.name = name + +        except Exception: +            pass + +        try: +            folder = self.info['folder'] = self.pyfile.name + +        except Exception: +            pass + +        self.logDebug("File name: %s"   % self.pyfile.name, +                      "File folder: %s" % self.pyfile.name) + + +    def getLinks(self): +        """ +        Returns the links extracted from self.html +        You should override this only if it's impossible to extract links using only the LINK_PATTERN. +        """ +        url_p   = urlparse(self.pyfile.url) +        baseurl = "%s://%s" % (url_p.scheme, url_p.netloc) + +        return [urljoin(baseurl, link) if not urlparse(link).scheme else link \ +                for link in re.findall(self.LINK_PATTERN, self.html)] + + +    def handlePages(self, pyfile): +        try: +            pages = int(re.search(self.PAGES_PATTERN, self.html).group(1)) +        except Exception: +            pages = 1 + +        for p in xrange(2, pages + 1): +            self.html = self.loadPage(p) +            self.links += self.getLinks() diff --git a/pyload/plugin/internal/SimpleDereferer.py b/pyload/plugin/internal/SimpleDereferer.py new file mode 100644 index 000000000..a224c1e40 --- /dev/null +++ b/pyload/plugin/internal/SimpleDereferer.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- + +import re + +from urllib import unquote + +from pyload.plugin.Crypter import Crypter +from pyload.plugin.internal.SimpleHoster import getFileURL, set_cookies + + +class SimpleDereferer(Crypter): +    __name__    = "SimpleDereferer" +    __type__    = "crypter" +    __version__ = "0.11" + +    __pattern__ = r'^unmatchable$' +    __config__  = [("use_subfolder"     , "bool", "Save package to subfolder"          , True), +                   ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Simple dereferer plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    """ +    Following patterns should be defined by each crypter: + +      LINK_PATTERN: Regex to catch the redirect url in group(1) +        example: LINK_PATTERN = r'<div class="link"><a href="(.+?)"' + +      OFFLINE_PATTERN: (optional) Checks if the page is unreachable +        example: OFFLINE_PATTERN = r'File (deleted|not found)' + +      TEMP_OFFLINE_PATTERN: (optional) Checks if the page is temporarily unreachable +        example: TEMP_OFFLINE_PATTERN = r'Server maintainance' + + +    You can override the getLinks method if you need a more sophisticated way to extract the redirect url. +    """ + +    LINK_PATTERN = None + +    TEXT_ENCODING = False +    COOKIES       = True + + +    def decrypt(self, pyfile): +        link = getFileURL(self, pyfile.url) + +        if not link: +            try: +                link = unquote(re.match(self.__pattern__, pyfile.url).group('LINK')) + +            except Exception: +                self.prepare() +                self.preload() +                self.checkStatus() + +                link = self.getLink() + +        if link.strip(): +            self.urls = [link.strip()]  #@TODO: Remove `.strip()` in 0.4.10 + +        elif not self.urls and not self.packages:  #@TODO: Remove in 0.4.10 +            self.fail(_("No link grabbed")) + + +    def prepare(self): +        self.info = {} +        self.html = "" + +        self.req.setOption("timeout", 120) + +        if isinstance(self.COOKIES, list): +            set_cookies(self.req.cj, self.COOKIES) + + +    def preload(self): +        self.html = self.load(self.pyfile.url, cookies=bool(self.COOKIES), decode=not self.TEXT_ENCODING) + +        if isinstance(self.TEXT_ENCODING, basestring): +            self.html = unicode(self.html, self.TEXT_ENCODING) + + +    def checkStatus(self): +        if hasattr(self, "OFFLINE_PATTERN") and re.search(self.OFFLINE_PATTERN, self.html): +            self.offline() + +        elif hasattr(self, "TEMP_OFFLINE_PATTERN") and re.search(self.TEMP_OFFLINE_PATTERN, self.html): +            self.tempOffline() + + +    def getLink(self): +        try: +            return re.search(self.LINK_PATTERN, self.html).group(1) + +        except Exception: +            pass diff --git a/pyload/plugin/internal/SimpleHoster.py b/pyload/plugin/internal/SimpleHoster.py new file mode 100644 index 000000000..56170a4fd --- /dev/null +++ b/pyload/plugin/internal/SimpleHoster.py @@ -0,0 +1,736 @@ +# -*- coding: utf-8 -*- + +import datetime +import mimetypes +import os +import re +import time +import urllib +import urlparse + +from pyload.datatype.File import statusMap as _statusMap +from pyload.network.CookieJar import CookieJar +from pyload.network.HTTPRequest import BadHeader +from pyload.network.RequestFactory import getURL +from pyload.plugin.Hoster import Hoster +from pyload.plugin.Plugin import Fail, Retry +from pyload.utils import fixup, fs_encode, parseFileSize + + +#@TODO: Adapt and move to PyFile in 0.4.10 +statusMap = dict((v, k) for k, v in _statusMap.iteritems()) + + +#@TODO: Remove in 0.4.10 and redirect to self.error instead +def _error(self, reason, type): +    if not reason and not type: +        type = "unknown" + +    msg  = _("%s error") % type.strip().capitalize() if type else _("Error") +    msg += (": %s" % reason.strip()) if reason else "" +    msg += _(" | Plugin may be out of date") + +    raise Fail(msg) + + +#@TODO: Remove in 0.4.10 +def _wait(self, seconds, reconnect): +    if seconds: +        self.setWait(int(seconds) + 1) + +    if reconnect is not None: +        self.wantReconnect = reconnect + +    super(SimpleHoster, self).wait() + + +def replace_patterns(string, ruleslist): +    for r in ruleslist: +        rf, rt = r +        string = re.sub(rf, rt, string) +    return string + + +def set_cookies(cj, cookies): +    for cookie in cookies: +        if isinstance(cookie, tuple) and len(cookie) == 3: +            domain, name, value = cookie +            cj.setCookie(domain, name, value) + + +def parseHtmlTagAttrValue(attr_name, tag): +    m = re.search(r"%s\s*=\s*([\"']?)((?<=\")[^\"]+|(?<=')[^']+|[^>\s\"'][^>\s]*)\1" % attr_name, tag, re.I) +    return m.group(2) if m else None + + +def parseHtmlForm(attr_str, html, input_names={}): +    for form in re.finditer(r"(?P<TAG><form[^>]*%s[^>]*>)(?P<CONTENT>.*?)</?(form|body|html)[^>]*>" % attr_str, +                            html, re.S | re.I): +        inputs = {} +        action = parseHtmlTagAttrValue("action", form.group('TAG')) + +        for inputtag in re.finditer(r'(<(input|textarea)[^>]*>)([^<]*(?=</\2)|)', form.group('CONTENT'), re.S | re.I): +            name = parseHtmlTagAttrValue("name", inputtag.group(1)) +            if name: +                value = parseHtmlTagAttrValue("value", inputtag.group(1)) +                if not value: +                    inputs[name] = inputtag.group(3) or '' +                else: +                    inputs[name] = value + +        if input_names: +            # check input attributes +            for key, val in input_names.iteritems(): +                if key in inputs: +                    if isinstance(val, basestring) and inputs[key] == val: +                        continue +                    elif isinstance(val, tuple) and inputs[key] in val: +                        continue +                    elif hasattr(val, "search") and re.match(val, inputs[key]): +                        continue +                    break  #: attibute value does not match +                else: +                    break  #: attibute name does not match +            else: +                return action, inputs  #: passed attribute check +        else: +            # no attribute check +            return action, inputs + +    return {}, None  #: no matching form found + + +#: Deprecated +def parseFileInfo(plugin, url="", html=""): +    if hasattr(plugin, "getInfo"): +        info = plugin.getInfo(url, html) +        res  = info['name'], info['size'], info['status'], info['url'] +    else: +        url   = urllib.unquote(url) +        url_p = urlparse.urlparse(url) +        res   = ((url_p.path.split('/')[-1] +                  or url_p.query.split('=', 1)[::-1][0].split('&', 1)[0] +                  or url_p.netloc.split('.', 1)[0]), +                 0, +                 3 if url else 8, +                 url) + +    return res + + +#@TODO: Remove in 0.4.10 +#@NOTE: Every plugin must have own parseInfos classmethod to work with 0.4.10 +# def create_getInfo(plugin): + +    # def generator(list): +    # for x in list: +    # yield x + +    # if hasattr(plugin, "parseInfos"): +    # fn = lambda urls: generator((info['name'], info['size'], info['status'], info['url']) for info in plugin.parseInfos(urls)) +    # else: +    # fn = lambda urls: generator(parseFileInfo(url) for url in urls) + +    # return fn + + +def timestamp(): +    return int(time.time() * 1000) + + +#@TODO: Move to hoster class in 0.4.10 +def getFileURL(self, url, follow_location=None): +    link     = "" +    redirect = 1 + +    if type(follow_location) is int: +        redirect = max(follow_location, 1) +    else: +        redirect = 10 + +    for i in xrange(redirect): +        try: +            self.logDebug("Redirect #%d to: %s" % (i, url)) +            header = self.load(url, just_header=True, decode=True) + +        except Exception:  #: Bad bad bad... +            req = pyreq.getHTTPRequest() +            res = req.load(url, just_header=True, decode=True) + +            req.close() + +            header = {"code": req.code} +            for line in res.splitlines(): +                line = line.strip() +                if not line or ":" not in line: +                    continue + +                key, none, value = line.partition(":") +                key              = key.lower().strip() +                value            = value.strip() + +                if key in header: +                    if type(header[key]) == list: +                        header[key].append(value) +                    else: +                        header[key] = [header[key], value] +                else: +                    header[key] = value + +        if 'content-disposition' in header: +            link = url + +        elif 'location' in header and header['location'].strip(): +            location = header['location'] + +            if not urlparse.urlparse(location).scheme: +                url_p    = urlparse.urlparse(url) +                baseurl  = "%s://%s" % (url_p.scheme, url_p.netloc) +                location = urlparse.urljoin(baseurl, location) + +            if 'code' in header and header['code'] == 302: +                link = location + +            if follow_location: +                url = location +                continue + +        else: +            extension = os.path.splitext(urlparse.urlparse(url).path.split('/')[-1])[-1] + +            if 'content-type' in header and header['content-type'].strip(): +                mimetype = header['content-type'].split(';')[0].strip() + +            elif extension: +                mimetype = mimetypes.guess_type(extension, False)[0] or "application/octet-stream" + +            else: +                mimetype = "" + +            if mimetype and (link or 'html' not in mimetype): +                link = url +            else: +                link = "" + +        break + +    else: +        try: +            self.logError(_("Too many redirects")) +        except Exception: +            pass + +    return link + + +def secondsToMidnight(gmt=0): +    now = datetime.datetime.utcnow() + datetime.timedelta(hours=gmt) + +    if now.hour is 0 and now.minute < 10: +        midnight = now +    else: +        midnight = now + datetime.timedelta(days=1) + +    td = midnight.replace(hour=0, minute=10, second=0, microsecond=0) - now + +    if hasattr(td, 'total_seconds'): +        res = td.total_seconds() +    else:  #: work-around for python 2.5 and 2.6 missing datetime.timedelta.total_seconds +        res = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10 ** 6) / 10 ** 6 + +    return int(res) + + +class SimpleHoster(Hoster): +    __name__    = "SimpleHoster" +    __type__    = "hoster" +    __version__ = "1.37" + +    __pattern__ = r'^unmatchable$' +    __config__  = [("use_premium", "bool", "Use premium account if available", True)] + +    __description__ = """Simple hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + +    """ +    Info patterns should be defined by each hoster: + +      INFO_PATTERN: (optional) Name and Size of the file +        example: INFO_PATTERN = r'(?P<N>file_name) (?P<S>file_size) (?P<U>size_unit)' +      or +        NAME_PATTERN: (optional) Name that will be set for the file +          example: NAME_PATTERN = r'(?P<N>file_name)' +        SIZE_PATTERN: (optional) Size that will be checked for the file +          example: SIZE_PATTERN = r'(?P<S>file_size) (?P<U>size_unit)' + +      HASHSUM_PATTERN: (optional) Hash code and type of the file +        example: HASHSUM_PATTERN = r'(?P<H>hash_code) (?P<T>MD5)' + +      OFFLINE_PATTERN: (optional) Check if the page is unreachable +        example: OFFLINE_PATTERN = r'File (deleted|not found)' + +      TEMP_OFFLINE_PATTERN: (optional) Check if the page is temporarily unreachable +        example: TEMP_OFFLINE_PATTERN = r'Server (maintenance|maintainance)' + + +    Error handling patterns are all optional: + +      WAIT_PATTERN: (optional) Detect waiting time +        example: WAIT_PATTERN = r'' + +      PREMIUM_ONLY_PATTERN: (optional) Check if the file can be downloaded only with a premium account +        example: PREMIUM_ONLY_PATTERN = r'Premium account required' + +      ERROR_PATTERN: (optional) Detect any error preventing download +        example: ERROR_PATTERN = r'' + + +    Instead overriding handleFree and handlePremium methods you can define the following patterns for direct download: + +      LINK_FREE_PATTERN: (optional) group(1) should be the direct link for free download +        example: LINK_FREE_PATTERN = r'<div class="link"><a href="(.+?)"' + +      LINK_PREMIUM_PATTERN: (optional) group(1) should be the direct link for premium download +        example: LINK_PREMIUM_PATTERN = r'<div class="link"><a href="(.+?)"' +    """ + +    NAME_REPLACEMENTS = [("&#?\w+;", fixup)] +    SIZE_REPLACEMENTS = [] +    URL_REPLACEMENTS  = [] + +    TEXT_ENCODING = False  #: Set to True or encoding name if encoding value in http header is not correct +    COOKIES       = True   #: or False or list of tuples [(domain, name, value)] +    CHECK_TRAFFIC = False  #: Set to True to force checking traffic left for premium account +    DIRECT_LINK   = None   #: Set to True to looking for direct link (as defined in handleDirect method), set to None to do it if self.account is True else False +    MULTI_HOSTER  = False  #: Set to True to leech other hoster link (as defined in handleMulti method) +    LOGIN_ACCOUNT = False  #: Set to True to require account login +    DISPOSITION   = True   #: Set to True to use any content-disposition value in http header as file name + +    directLink = getFileURL  # @TODO: Remove in 0.4.10 + + +    @classmethod +    def parseInfos(cls, urls):  #@TODO: Built-in in 0.4.10 core (remove from plugins) +        for url in urls: +            url = replace_patterns(url, cls.URL_REPLACEMENTS) +            yield cls.getInfo(url) + +    @classmethod +    def apiInfo(cls, url="", get={}, post={}): +        url   = urllib.unquote(url) +        url_p = urlparse.urlparse(url) +        return {'name': (url_p.path.split('/')[-1] +                         or url_p.query.split('=', 1)[::-1][0].split('&', 1)[0] +                         or url_p.netloc.split('.', 1)[0]), +                'size': 0, +                'status': 3 if url else 8, +                'url': url} + +    @classmethod +    def getInfo(cls, url="", html=""): +        info   = cls.apiInfo(url) +        online = False if info['status'] != 2 else True + +        try: +            info['pattern'] = re.match(cls.__pattern__, url).groupdict()  #: pattern groups will be saved here + +        except Exception: +            info['pattern'] = {} + +        if not html and not online: +            if not url: +                info['error']  = "missing url" +                info['status'] = 1 + +            elif info['status'] is 3 and not getFileURL(None, url): +                try: +                    html = getURL(url, cookies=cls.COOKIES, decode=not cls.TEXT_ENCODING) + +                    if isinstance(cls.TEXT_ENCODING, basestring): +                        html = unicode(html, cls.TEXT_ENCODING) + +                except BadHeader, e: +                    info['error'] = "%d: %s" % (e.code, e.content) + +                    if e.code is 404: +                        info['status'] = 1 + +                    elif e.code is 503: +                        info['status'] = 6 + +        if html: +            if hasattr(cls, "OFFLINE_PATTERN") and re.search(cls.OFFLINE_PATTERN, html): +                info['status'] = 1 + +            elif hasattr(cls, "TEMP_OFFLINE_PATTERN") and re.search(cls.TEMP_OFFLINE_PATTERN, html): +                info['status'] = 6 + +            else: +                for pattern in ("INFO_PATTERN", "NAME_PATTERN", "SIZE_PATTERN", "HASHSUM_PATTERN"): +                    try: +                        attr  = getattr(cls, pattern) +                        pdict = re.search(attr, html).groupdict() + +                        if all(True for k in pdict if k not in info['pattern']): +                            info['pattern'].update(pdict) + +                    except AttributeError: +                        continue + +                    else: +                        online = True + +        if online: +            info['status'] = 2 + +            if 'N' in info['pattern']: +                info['name'] = replace_patterns(urllib.unquote(info['pattern']['N'].strip()), +                                                cls.NAME_REPLACEMENTS) + +            if 'S' in info['pattern']: +                size = replace_patterns(info['pattern']['S'] + info['pattern']['U'] if 'U' in info['pattern'] else info['pattern']['S'], +                                        cls.SIZE_REPLACEMENTS) +                info['size'] = parseFileSize(size) + +            elif isinstance(info['size'], basestring): +                unit = info['units'] if 'units' in info else None +                info['size'] = parseFileSize(info['size'], unit) + +            if 'H' in info['pattern']: +                hashtype = info['pattern']['T'] if 'T' in info['pattern'] else "hash" +                info[hashtype] = info['pattern']['H'] + +        if not info['pattern']: +            info.pop('pattern', None) + +        return info + +    def setup(self): +        self.resumeDownload = self.multiDL = self.premium + +    def prepare(self): +        self.pyfile.error = ""  # @TODO: Remove in 0.4.10 + +        self.info      = {} +        self.html      = "" +        self.link      = ""  # @TODO: Move to hoster class in 0.4.10 +        self.directDL  = False  # @TODO: Move to hoster class in 0.4.10 +        self.multihost = False  # @TODO: Move to hoster class in 0.4.10 + +        if not self.getConfig('use_premium', True): +            self.retryFree() + +        if self.LOGIN_ACCOUNT and not self.account: +            self.fail(_("Required account not found")) + +        self.req.setOption("timeout", 120) + +        if isinstance(self.COOKIES, list): +            set_cookies(self.req.cj, self.COOKIES) + +        if (self.MULTI_HOSTER +            and (self.__pattern__ != self.core.pluginManager.hosterPlugins[self.__class__.__name__]['pattern'] +                 or re.match(self.__pattern__, self.pyfile.url) is None)): +            self.multihost = True +            return + +        if self.DIRECT_LINK is None: +            self.directDL = bool(self.account) +        else: +            self.directDL = self.DIRECT_LINK + +        self.pyfile.url = replace_patterns(self.pyfile.url, self.URL_REPLACEMENTS) + +    def preload(self): +        self.html = self.load(self.pyfile.url, cookies=bool(self.COOKIES), decode=not self.TEXT_ENCODING) + +        if isinstance(self.TEXT_ENCODING, basestring): +            self.html = unicode(self.html, self.TEXT_ENCODING) + +    def process(self, pyfile): +        try: +            self.prepare() +            self.checkInfo() + +            if self.directDL: +                self.logDebug("Looking for direct download link...") +                self.handleDirect(pyfile) + +            if self.multihost and not self.link and not self.lastDownload: +                self.logDebug("Looking for leeched download link...") +                self.handleMulti(pyfile) + +                if not self.link and not self.lastDownload: +                    self.MULTI_HOSTER = False +                    self.retry(1, reason="Multi hoster fails") + +            if not self.link and not self.lastDownload: +                self.preload() +                self.checkInfo() + +                if self.premium and (not self.CHECK_TRAFFIC or self.checkTrafficLeft()): +                    self.logDebug("Handled as premium download") +                    self.handlePremium(pyfile) + +                elif not self.LOGIN_ACCOUNT or (not self.CHECK_TRAFFIC or self.checkTrafficLeft()): +                    self.logDebug("Handled as free download") +                    self.handleFree(pyfile) + +            self.downloadLink(self.link, self.DISPOSITION) +            self.checkFile() + +        except Fail, e:  # @TODO: Move to PluginThread in 0.4.10 +            if self.premium: +                self.logWarning(_("Premium download failed")) +                self.retryFree() +            else: +                raise Fail(e) + +    def downloadLink(self, link, disposition=True): +        if link and isinstance(link, basestring): +            self.correctCaptcha() + +            if not urlparse.urlparse(link).scheme: +                url_p   = urlparse.urlparse(self.pyfile.url) +                baseurl = "%s://%s" % (url_p.scheme, url_p.netloc) +                link    = urlparse.urljoin(baseurl, link) + +            self.download(link, ref=False, disposition=disposition) + +    def checkFile(self, rules={}): +        if self.cTask and not self.lastDownload: +            self.invalidCaptcha() +            self.retry(10, reason=_("Wrong captcha")) + +        elif not self.lastDownload or not os.path.exists(fs_encode(self.lastDownload)): +            self.lastDownload = "" +            self.error(self.pyfile.error or _("No file downloaded")) + +        else: +            errmsg = self.checkDownload({'Empty file': re.compile(r'\A\s*\Z'), +                                         'Html error': re.compile(r'\A(?:\s*<.+>)?((?:[\w\s]*(?:[Ee]rror|ERROR)\s*\:?)?\s*\d{3})(?:\Z|\s+)')}) + +            if not errmsg: +                for r, p in [('Html file', re.compile(r'\A\s*<!DOCTYPE html')), +                             ('Request error', re.compile(r'([Aa]n error occured while processing your request)'))]: +                    if r not in rules: +                        rules[r] = p + +                for r, a in [('Error', "ERROR_PATTERN"), +                             ('Premium only', "PREMIUM_ONLY_PATTERN"), +                             ('Wait error', "WAIT_PATTERN")]: +                    if r not in rules and hasattr(self, a): +                        rules[r] = getattr(self, a) + +                errmsg = self.checkDownload(rules) + +            if not errmsg: +                return + +            errmsg = errmsg.strip().capitalize() + +            try: +                errmsg += " | " + self.lastCheck.group(1).strip() +            except Exception: +                pass + +            self.logWarning("Check result: " + errmsg, "Waiting 1 minute and retry") +            self.retry(3, 60, errmsg) + +    def checkErrors(self): +        if not self.html: +            self.logWarning(_("No html code to check")) +            return + +        if hasattr(self, 'PREMIUM_ONLY_PATTERN') and not self.premium and re.search(self.PREMIUM_ONLY_PATTERN, self.html): +            self.fail(_("Link require a premium account to be handled")) + +        elif hasattr(self, 'ERROR_PATTERN'): +            m = re.search(self.ERROR_PATTERN, self.html) +            if m: +                try: +                    errmsg = m.group(1).strip() +                except Exception: +                    errmsg = m.group(0).strip() + +                self.info['error'] = errmsg + +                if "hour" in errmsg: +                    self.wait(1 * 60 * 60, True) + +                elif re.search("da(il)?y|today", errmsg): +                    self.wait(secondsToMidnight(gmt=2), True) + +                elif "minute" in errmsg: +                    self.wait(1 * 60) + +                else: +                    self.error(errmsg) + +        elif hasattr(self, 'WAIT_PATTERN'): +            m = re.search(self.WAIT_PATTERN, self.html) +            if m: +                try: +                    waitmsg = m.group(1).strip() +                except Exception: +                    waitmsg = m.group(0).strip() + +                wait_time = sum(int(v) * {"hr": 3600, "hour": 3600, "min": 60, "sec": 1, "": 1}[u.lower()] for v, u in +                                re.findall(r'(\d+)\s*(hr|hour|min|sec|)', waitmsg, re.I)) +                self.wait(wait_time, wait_time > 300) + +        self.info.pop('error', None) + +    def checkStatus(self, getinfo=True): +        if not self.info or getinfo: +            self.logDebug("Update file info...") +            self.logDebug("Previous file info: %s" % self.info) +            self.info.update(self.getInfo(self.pyfile.url, self.html)) +            self.logDebug("Current file info: %s"  % self.info) + +        try: +            status = self.info['status'] + +            if status is 1: +                self.offline() + +            elif status is 6: +                self.tempOffline() + +            elif status is 8: +                self.fail() + +        finally: +            self.logDebug("File status: %s" % statusMap[status]) + +    def checkNameSize(self, getinfo=True): +        if not self.info or getinfo: +            self.logDebug("Update file info...") +            self.logDebug("Previous file info: %s" % self.info) +            self.info.update(self.getInfo(self.pyfile.url, self.html)) +            self.logDebug("Current file info: %s"  % self.info) + +        try: +            url  = self.info['url'].strip() +            name = self.info['name'].strip() +            if name and name != url: +                self.pyfile.name = name + +        except Exception: +            pass + +        try: +            size = self.info['size'] +            if size > 0: +                self.pyfile.size = size + +        except Exception: +            pass + +        self.logDebug("File name: %s" % self.pyfile.name, +                      "File size: %s byte" % self.pyfile.size if self.pyfile.size > 0 else "File size: Unknown") + +    def checkInfo(self): +        self.checkNameSize() + +        if self.html: +            self.checkErrors() +            self.checkNameSize() + +        self.checkStatus(getinfo=False) + +    #: Deprecated +    def getFileInfo(self): +        self.info = {} +        self.checkInfo() +        return self.info + +    def handleDirect(self, pyfile): +        link = self.directLink(pyfile.url, self.resumeDownload) + +        if link: +            self.logInfo(_("Direct download link detected")) +            self.link = link +        else: +            self.logDebug("Direct download link not found") + +    def handleMulti(self, pyfile):  #: Multi-hoster handler +        pass + +    def handleFree(self, pyfile): +        if not hasattr(self, 'LINK_FREE_PATTERN'): +            self.logError(_("Free download not implemented")) + +        m = re.search(self.LINK_FREE_PATTERN, self.html) +        if m is None: +            self.error(_("Free download link not found")) +        else: +            self.link = m.group(1) + +    def handlePremium(self, pyfile): +        if not hasattr(self, 'LINK_PREMIUM_PATTERN'): +            self.logError(_("Premium download not implemented")) +            self.logDebug("Handled as free download") +            self.handleFree(pyfile) + +        m = re.search(self.LINK_PREMIUM_PATTERN, self.html) +        if m is None: +            self.error(_("Premium download link not found")) +        else: +            self.link = m.group(1) + +    def longWait(self, wait_time=None, max_tries=3): +        if wait_time and isinstance(wait_time, (int, long, float)): +            time_str  = "%dh %dm" % divmod(wait_time / 60, 60) +        else: +            wait_time = 900 +            time_str  = _("(unknown time)") +            max_tries = 100 + +        self.logInfo(_("Download limit reached, reconnect or wait %s") % time_str) + +        self.wait(wait_time, True) +        self.retry(max_tries=max_tries, reason=_("Download limit reached")) + +    def parseHtmlForm(self, attr_str="", input_names={}): +        return parseHtmlForm(attr_str, self.html, input_names) + +    def checkTrafficLeft(self): +        if not self.account: +            return True + +        traffic = self.account.getAccountInfo(self.user, True)['trafficleft'] + +        if traffic is None: +            return False +        elif traffic == -1: +            return True +        else: +            size = self.pyfile.size / 1024 +            self.logInfo(_("Filesize: %i KiB, Traffic left for user %s: %i KiB") % (size, self.user, traffic)) +            return size <= traffic + +    def getConfig(self, option, default=''):  # @TODO: Remove in 0.4.10 +        """getConfig with default value - sublass may not implements all config options""" +        try: +            return self.getConf(option) + +        except KeyError: +            return default + +    def retryFree(self): +        if not self.premium: +            return +        self.premium = False +        self.account = None +        self.req     = self.core.requestFactory.getRequest(self.__name__) +        self.retries = 0 +        raise Retry(_("Fallback to free download")) + +    #@TODO: Remove in 0.4.10 +    def wait(self, seconds=0, reconnect=None): +        return _wait(self, seconds, reconnect) + +    def error(self, reason="", type="parse"): +        return _error(self, reason, type) diff --git a/pyload/plugin/internal/XFSAccount.py b/pyload/plugin/internal/XFSAccount.py new file mode 100644 index 000000000..1f2d2b180 --- /dev/null +++ b/pyload/plugin/internal/XFSAccount.py @@ -0,0 +1,174 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from urlparse import urljoin + +from pyload.plugin.Account import Account +from pyload.plugin.internal.SimpleHoster import parseHtmlForm, set_cookies + + +class XFSAccount(Account): +    __name__    = "XFSAccount" +    __type__    = "account" +    __version__ = "0.36" + +    __description__ = """XFileSharing account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg"      , "zoidberg@mujmail.cz"), +                       ("Walter Purcaro", "vuolter@gmail.com"  )] + + +    HOSTER_DOMAIN = None +    HOSTER_URL    = None +    LOGIN_URL     = None + +    COOKIES = True + +    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 + +    LEECH_TRAFFIC_PATTERN = r'Leech Traffic left:<b>.*?(?P<S>[\d.,]+|[Uu]nlimited)\s*(?:(?P<U>[\w^_]+)\s*)?</b>' +    LEECH_TRAFFIC_UNIT    = "MB"  #: used only if no group <U> was found + +    LOGIN_FAIL_PATTERN = r'Incorrect Login or Password|account was banned|Error<' + + +    def init(self): +        if not self.HOSTER_DOMAIN: +            self.logError(_("Missing HOSTER_DOMAIN")) +            self.COOKIES = False + +        else: +            if not self.HOSTER_URL: +                self.HOSTER_URL = "http://www.%s/" % self.HOSTER_DOMAIN + +            if isinstance(self.COOKIES, list): +                self.COOKIES.insert((self.HOSTER_DOMAIN, "lang", "english")) +                set_cookies(req.cj, self.COOKIES) + + +    def loadAccountInfo(self, user, req): +        validuntil   = None +        trafficleft  = None +        leechtraffic = None +        premium      = None + +        if not self.HOSTER_URL:  #@TODO: Remove in 0.4.10 +            return {'validuntil'  : validuntil, +                    'trafficleft' : trafficleft, +                    'leechtraffic': leechtraffic, +                    'premium'     : premium} + +        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 = time.mktime(time.strptime(expiredate, "%d %B %Y")) + +            except Exception, e: +                self.logError(e) + +            else: +                self.logDebug("Valid until: %s" % validuntil) + +                if validuntil > time.mktime(time.gmtime()): +                    premium     = True +                    trafficleft = -1 +                else: +                    premium    = False +                    validuntil = None  #: registered account type (not premium) +        else: +            self.logDebug("VALID_UNTIL_PATTERN not found") + +        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) +        else: +            self.logDebug("TRAFFIC_LEFT_PATTERN not found") + +        leech = [m.groupdict() for m in re.finditer(self.LEECH_TRAFFIC_PATTERN, html)] +        if leech: +            leechtraffic = 0 +            try: +                for traffic in leech: +                    size = traffic['S'] + +                    if "nlimited" in size: +                        leechtraffic = -1 +                        if validuntil is None: +                            validuntil = -1 +                        break +                    else: +                        if 'U' in traffic: +                            unit = traffic['U'] +                        elif isinstance(self.LEECH_TRAFFIC_UNIT, basestring): +                            unit = self.LEECH_TRAFFIC_UNIT +                        else: +                            unit = "" + +                        leechtraffic += self.parseTraffic(size + unit) + +            except Exception, e: +                self.logError(e) +        else: +            self.logDebug("LEECH_TRAFFIC_PATTERN not found") + +        return {'validuntil'  : validuntil, +                'trafficleft' : trafficleft, +                'leechtraffic': leechtraffic, +                'premium'     : premium} + + +    def login(self, user, data, req): +        if not self.HOSTER_URL:  #@TODO: Remove in 0.4.10 +            raise Exception(_("Missing HOSTER_DOMAIN")) + +        if not self.LOGIN_URL: +            self.LOGIN_URL  = urljoin(self.HOSTER_URL, "login.html") +        html = req.load(self.LOGIN_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']}) + +        if not action: +            action = self.HOSTER_URL +        html = req.load(action, post=inputs, decode=True) + +        if re.search(self.LOGIN_FAIL_PATTERN, html): +            self.wrongPassword() diff --git a/pyload/plugin/internal/XFSCrypter.py b/pyload/plugin/internal/XFSCrypter.py new file mode 100644 index 000000000..bb2dac09e --- /dev/null +++ b/pyload/plugin/internal/XFSCrypter.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.internal.SimpleCrypter import SimpleCrypter + + +class XFSCrypter(SimpleCrypter): +    __name__    = "XFSCrypter" +    __type__    = "crypter" +    __version__ = "0.06" + +    __pattern__ = r'^unmatchable$' + +    __description__ = """XFileSharing decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = None + +    URL_REPLACEMENTS = [(r'&?per_page=\d+', ""), (r'[?/&]+$', ""), (r'(.+/[^?]+)$', r'\1?'), (r'$', r'&per_page=10000')] + +    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)' + + +    def prepare(self): +        if not self.HOSTER_DOMAIN: +            if self.account: +                account      = self.account +            else: +                account_name = (self.__class__.__name__ + ".py").replace("Folder.py", "").replace(".py", "") +                account      = self.pyfile.m.core.accountManager.getAccountPlugin(account_name) + +            if account and hasattr(account, "HOSTER_DOMAIN") and account.HOSTER_DOMAIN: +                self.HOSTER_DOMAIN = account.HOSTER_DOMAIN +            else: +                self.fail(_("Missing HOSTER_DOMAIN")) + +        if isinstance(self.COOKIES, list): +            self.COOKIES.insert((self.HOSTER_DOMAIN, "lang", "english")) + +        return super(XFSCrypter, self).prepare() diff --git a/pyload/plugin/internal/XFSHoster.py b/pyload/plugin/internal/XFSHoster.py new file mode 100644 index 000000000..0e265ce64 --- /dev/null +++ b/pyload/plugin/internal/XFSHoster.py @@ -0,0 +1,318 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from random import random +from urlparse import urljoin, urlparse + +from pyload.plugin.captcha.ReCaptcha import ReCaptcha +from pyload.plugin.captcha.SolveMedia import SolveMedia +from pyload.plugin.internal.SimpleHoster import SimpleHoster, secondsToMidnight +from pyload.utils import html_unescape + + +class XFSHoster(SimpleHoster): +    __name__    = "XFSHoster" +    __type__    = "hoster" +    __version__ = "0.46" + +    __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 + +    TEXT_ENCODING = False +    DIRECT_LINK   = None +    MULTI_HOSTER  = True  # @NOTE: Should be default to False for safe, but I'm lazy... + +    NAME_PATTERN = r'(Filename[ ]*:[ ]*</b>(</td><td nowrap>)?|name="fname"[ ]+value="|<[\w^_]+ class="(file)?name">)\s*(?P<N>.+?)(\s*<|")' +    SIZE_PATTERN = r'(Size[ ]*:[ ]*</b>(</td><td>)?|File:.*>|</font>\s*\(|<[\w^_]+ class="size">)\s*(?P<S>[\d.,]+)\s*(?P<U>[\w^_]+)' + +    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>|id="countdown" value=".*?(\d+).*?"' +    PREMIUM_ONLY_PATTERN = r'>This file is available for Premium Users only' +    ERROR_PATTERN        = r'(?:class=["\']err["\'].*?>|<[Cc]enter><b>|>Error</td>|>\(ERROR:)(?:\s*<.+?>\s*)*(.+?)(?:["\']|<|\))' + +    LINK_LEECH_PATTERN = r'<h2>Download Link</h2>\s*<textarea[^>]*>([^<]+)' +    LINK_PATTERN       = None  #: final download url pattern + +    CAPTCHA_PATTERN       = r'(https?://[^"\']+?/captchas?/[^"\']+)' +    CAPTCHA_BLOCK_PATTERN = r'>Enter code.*?<div.*?>(.+?)</div>' +    RECAPTCHA_PATTERN     = None +    SOLVEMEDIA_PATTERN    = None + +    FORM_PATTERN    = None +    FORM_INPUTS_MAP = None  #: dict passed as input_names to parseHtmlForm + +    def setup(self): +        self.chunkLimit     = -1 if self.premium else 1 +        self.resumeDownload = self.multiDL = self.premium + +    def prepare(self): +        """ Initialize important variables """ +        if not self.HOSTER_DOMAIN: +            if self.account: +                account = self.account +            else: +                account = self.pyfile.m.core.accountManager.getAccountPlugin(self.__class__.__name__) + +            if account and hasattr(account, "HOSTER_DOMAIN") and account.HOSTER_DOMAIN: +                self.HOSTER_DOMAIN = account.HOSTER_DOMAIN +            else: +                self.fail(_("Missing HOSTER_DOMAIN")) + +        if isinstance(self.COOKIES, list): +            self.COOKIES.insert((self.HOSTER_DOMAIN, "lang", "english")) + +        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 + +        super(XFSHoster, self).prepare() + +        if self.DIRECT_LINK is None: +            self.directDL = self.premium + +    def handleFree(self, pyfile): +        for i in xrange(1, 6): +            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() + +            self.html = self.load(pyfile.url, post=data, ref=True, decode=True, follow_location=False) + +            m = re.search(r'Location\s*:\s*(.+)', self.req.http.header, re.I) +            if m and not "op=" in m.group(1): +                break + +            m = re.search(self.LINK_PATTERN, self.html, re.S) +            if m: +                break +        else: +            self.logError(data['op'] if 'op' in data else _("UNKNOWN")) +            return "" + +        self.link = m.group(1).strip()  # @TODO: Remove .strip() in 0.4.10 + +    def handlePremium(self, pyfile): +        return self.handleFree(pyfile) + +    def handleMulti(self, pyfile): +        if not self.account: +            self.fail(_("Only registered or premium users can use url leech feature")) + +        # 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'] = 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.LINK_LEECH_PATTERN, self.html) +        if m is None: +            self.error(_("LINK_LEECH_PATTERN not found")) + +        header = self.load(m.group(1), just_header=True, decode=True) + +        if 'location' in header:  #: Direct download link +            self.link = header['location'] + +    def checkErrors(self): +        m = re.search(self.ERROR_PATTERN, self.html) +        if m is None: +            self.errmsg = None +        else: +            self.errmsg = m.group(1).strip() + +            self.logWarning(re.sub(r"<.*?>", " ", self.errmsg)) + +            if 'wait' in self.errmsg: +                wait_time = sum(int(v) * {"hr": 3600, "hour": 3600, "min": 60, "sec": 1, "": 1}[u.lower()] for v, u in +                                re.findall(r'(\d+)\s*(hr|hour|min|sec|)', self.errmsg, re.I)) +                self.wait(wait_time, wait_time > 300) + +            elif 'country' in self.errmsg: +                self.fail(_("Downloads are disabled for your country")) + +            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 = 24 + +                self.wantReconnect = True +                self.retry(retries, delay, _("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 'up to' in self.errmsg: +                self.fail(_("File too large for free download")) + +            else: +                self.wantReconnect = True +                self.retry(wait_time=60, reason=self.errmsg) + +        if self.errmsg: +            self.info['error'] = self.errmsg +        else: +            self.info.pop('error', None) + +    def getPostParameters(self): +        if self.FORM_PATTERN or self.FORM_INPUTS_MAP: +            action, inputs = self.parseHtmlForm(self.FORM_PATTERN or "", self.FORM_INPUTS_MAP or {}) +        else: +            action, inputs = self.parseHtmlForm(input_names={'op': re.compile(r'^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: +            if "password" in inputs: +                password = self.getPassword() +                if password: +                    inputs['password'] = password +                else: +                    self.fail(_("Missing password")) + +            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) + +                self.captcha = self.handleCaptcha(inputs) + +                self.wait() +        else: +            inputs['referer'] = self.pyfile.url + +        if self.premium: +            inputs['method_premium'] = "Premium Download" +            inputs.pop('method_free', None) +        else: +            inputs['method_free'] = "Free Download" +            inputs.pop('method_premium', None) + +        return inputs + +    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_BLOCK_PATTERN, self.html, re.S) +        if m: +            captcha_div = m.group(1) +            numerals    = re.findall(r'<span.*?padding-left\s*:\s*(\d+).*?>(\d)</span>', html_unescape(captcha_div)) + +            self.logDebug(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 Exception: +            captcha_key = recaptcha.detect_key() + +        else: +            self.logDebug("ReCaptcha key: %s" % captcha_key) + +        if captcha_key: +            inputs['recaptcha_response_field'], inputs['recaptcha_challenge_field'] = recaptcha.challenge(captcha_key) +            return 3 + +        solvemedia = SolveMedia(self) +        try: +            captcha_key = re.search(self.SOLVEMEDIA_PATTERN, self.html).group(1) + +        except Exception: +            captcha_key = solvemedia.detect_key() + +        else: +            self.logDebug("SolveMedia key: %s" % captcha_key) + +        if captcha_key: +            inputs['adcopy_response'], inputs['adcopy_challenge'] = solvemedia.challenge(captcha_key) +            return 4 + +        return 0 diff --git a/pyload/plugin/internal/__init__.py b/pyload/plugin/internal/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/pyload/plugin/internal/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/pyload/plugin/ocr/GigasizeCom.py b/pyload/plugin/ocr/GigasizeCom.py new file mode 100644 index 000000000..efa513a7e --- /dev/null +++ b/pyload/plugin/ocr/GigasizeCom.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.OCR import OCR + + +class GigasizeCom(OCR): +    __name__    = "GigasizeCom" +    __type__    = "ocr" +    __version__ = "0.11" + +    __description__ = """Gigasize.com ocr plugin""" +    __license__     = "GPLv3" +    __authors__     = [("pyLoad Team", "admin@pyload.org")] + + +    def __init__(self): +        OCR.__init__(self) + + +    def get_captcha(self, image): +        self.load_image(image) +        self.threshold(2.8) +        self.run_tesser(True, False, False, True) +        return self.result_captcha diff --git a/pyload/plugin/ocr/LinksaveIn.py b/pyload/plugin/ocr/LinksaveIn.py new file mode 100644 index 000000000..6de6f0123 --- /dev/null +++ b/pyload/plugin/ocr/LinksaveIn.py @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- + +try: +    from PIL import Image +except ImportError: +    import Image + +from glob import glob +from os import sep +from os.path import abspath, dirname + +from pyload.plugin.OCR import OCR + + +class LinksaveIn(OCR): +    __name__    = "LinksaveIn" +    __type__    = "ocr" +    __version__ = "0.11" + +    __description__ = """Linksave.in ocr plugin""" +    __license__     = "GPLv3" +    __authors__     = [("pyLoad Team", "admin@pyload.org")] + + +    def __init__(self): +        OCR.__init__(self) +        self.data_dir = dirname(abspath(__file__)) + sep + "LinksaveIn" + sep + + +    def load_image(self, image): +        im = Image.open(image) +        frame_nr = 0 + +        lut = im.resize((256, 1)) +        lut.putdata(range(256)) +        lut = list(lut.convert("RGB").getdata()) + +        new = Image.new("RGB", im.size) +        npix = new.load() +        while True: +            try: +                im.seek(frame_nr) +            except EOFError: +                break +            frame = im.copy() +            pix = frame.load() +            for x in xrange(frame.size[0]): +                for y in xrange(frame.size[1]): +                    if lut[pix[x, y]] != (0, 0, 0): +                        npix[x, y] = lut[pix[x, y]] +            frame_nr += 1 +        new.save(self.data_dir+"unblacked.png") +        self.image = new.copy() +        self.pixels = self.image.load() +        self.result_captcha = '' + + +    def get_bg(self): +        stat = {} +        cstat = {} +        img = self.image.convert("P") +        for bgpath in glob(self.data_dir+"bg/*.gif"): +            stat[bgpath] = 0 +            bg = Image.open(bgpath) + +            bglut = bg.resize((256, 1)) +            bglut.putdata(range(256)) +            bglut = list(bglut.convert("RGB").getdata()) + +            lut = img.resize((256, 1)) +            lut.putdata(range(256)) +            lut = list(lut.convert("RGB").getdata()) + +            bgpix = bg.load() +            pix = img.load() +            for x in xrange(bg.size[0]): +                for y in xrange(bg.size[1]): +                    rgb_bg = bglut[bgpix[x, y]] +                    rgb_c = lut[pix[x, y]] +                    try: +                        cstat[rgb_c] += 1 +                    except Exception: +                        cstat[rgb_c] = 1 +                    if rgb_bg == rgb_c: +                        stat[bgpath] += 1 +        max_p = 0 +        bg = "" +        for bgpath, value in stat.iteritems(): +            if max_p < value: +                bg = bgpath +                max_p = value +        return bg + + +    def substract_bg(self, bgpath): +        bg = Image.open(bgpath) +        img = self.image.convert("P") + +        bglut = bg.resize((256, 1)) +        bglut.putdata(range(256)) +        bglut = list(bglut.convert("RGB").getdata()) + +        lut = img.resize((256, 1)) +        lut.putdata(range(256)) +        lut = list(lut.convert("RGB").getdata()) + +        bgpix = bg.load() +        pix = img.load() +        orgpix = self.image.load() +        for x in xrange(bg.size[0]): +            for y in xrange(bg.size[1]): +                rgb_bg = bglut[bgpix[x, y]] +                rgb_c = lut[pix[x, y]] +                if rgb_c == rgb_bg: +                    orgpix[x, y] = (255, 255, 255) + + +    def eval_black_white(self): +        new = Image.new("RGB", (140, 75)) +        pix = new.load() +        orgpix = self.image.load() +        thresh = 4 +        for x in xrange(new.size[0]): +            for y in xrange(new.size[1]): +                rgb = orgpix[x, y] +                r, g, b = rgb +                pix[x, y] = (255, 255, 255) +                if r > max(b, g)+thresh: +                    pix[x, y] = (0, 0, 0) +                if g < min(r, b): +                    pix[x, y] = (0, 0, 0) +                if g > max(r, b)+thresh: +                    pix[x, y] = (0, 0, 0) +                if b > max(r, g)+thresh: +                    pix[x, y] = (0, 0, 0) +        self.image = new +        self.pixels = self.image.load() + + +    def get_captcha(self, image): +        self.load_image(image) +        bg = self.get_bg() +        self.substract_bg(bg) +        self.eval_black_white() +        self.to_greyscale() +        self.image.save(self.data_dir+"cleaned_pass1.png") +        self.clean(4) +        self.clean(4) +        self.image.save(self.data_dir+"cleaned_pass2.png") +        letters = self.split_captcha_letters() +        final = "" +        for n, letter in enumerate(letters): +            self.image = letter +            self.image.save(ocr.data_dir+"letter%d.png" % n) +            self.run_tesser(True, True, False, False) +            final += self.result_captcha + +        return final diff --git a/pyload/plugin/ocr/NetloadIn.py b/pyload/plugin/ocr/NetloadIn.py new file mode 100644 index 000000000..bc5c4d882 --- /dev/null +++ b/pyload/plugin/ocr/NetloadIn.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.OCR import OCR + + +class NetloadIn(OCR): +    __name__    = "NetloadIn" +    __type__    = "ocr" +    __version__ = "0.11" + +    __description__ = """Netload.in ocr plugin""" +    __license__     = "GPLv3" +    __authors__     = [("pyLoad Team", "admin@pyload.org")] + + +    def __init__(self): +        OCR.__init__(self) + + +    def get_captcha(self, image): +        self.load_image(image) +        self.to_greyscale() +        self.clean(3) +        self.clean(3) +        self.run_tesser(True, True, False, False) + +        self.result_captcha = self.result_captcha.replace(" ", "")[:4] # cut to 4 numbers + +        return self.result_captcha diff --git a/pyload/plugin/ocr/ShareonlineBiz.py b/pyload/plugin/ocr/ShareonlineBiz.py new file mode 100644 index 000000000..dc0894d4f --- /dev/null +++ b/pyload/plugin/ocr/ShareonlineBiz.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +from pyload.plugin.OCR import OCR + + +class ShareonlineBiz(OCR): +    __name__    = "ShareonlineBiz" +    __type__    = "ocr" +    __version__ = "0.11" + +    __description__ = """Shareonline.biz ocr plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org")] + + +    def __init__(self): +        OCR.__init__(self) + + +    def get_captcha(self, image): +        self.load_image(image) +        self.to_greyscale() +        self.image = self.image.resize((160, 50)) +        self.pixels = self.image.load() +        self.threshold(1.85) +        #self.eval_black_white(240) +        #self.derotate_by_average() + +        letters = self.split_captcha_letters() + +        final = "" +        for letter in letters: +            self.image = letter +            self.run_tesser(True, True, False, False) +            final += self.result_captcha + +        return final + +        #tesseract at 60% diff --git a/pyload/plugin/ocr/__init__.py b/pyload/plugin/ocr/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/pyload/plugin/ocr/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*-  | 
