diff options
Diffstat (limited to 'module')
483 files changed, 15817 insertions, 14004 deletions
diff --git a/module/plugins/Account.py b/module/plugins/Account.py index e75fc0ecb..c147404e0 100644 --- a/module/plugins/Account.py +++ b/module/plugins/Account.py @@ -13,7 +13,7 @@      You should have received a copy of the GNU General Public License      along with this program; if not, see <http://www.gnu.org/licenses/>. - +          @author: mkaay  """ @@ -38,9 +38,9 @@ class Account(Base):      __name__ = "Account"      __version__ = "0.2"      __type__ = "account" -    __description__ = """Base account plugin""" -    __author_name__ = "mkaay" -    __author_mail__ = "mkaay@mkaay.de" +    __description__ = """Account Plugin""" +    __author_name__ = ("mkaay") +    __author_mail__ = ("mkaay@mkaay.de")      #: after that time [in minutes] pyload will relogin the account      login_timeout = 600 @@ -76,7 +76,7 @@ class Account(Base):      def _login(self, user, data):          # set timestamp for login          self.timestamps[user] = time() - +                  req = self.getAccountRequest(user)          try:              self.login(user, data, req) diff --git a/module/plugins/AccountManager.py b/module/plugins/AccountManager.py index 1de55effc..fc521d36c 100644 --- a/module/plugins/AccountManager.py +++ b/module/plugins/AccountManager.py @@ -1,5 +1,5 @@ +#!/usr/bin/env python  # -*- coding: utf-8 -*- -  """      This program is free software; you can redistribute it and/or modify      it under the terms of the GNU General Public License as published by @@ -60,21 +60,21 @@ class AccountManager():      def getAccountPlugins(self):          """ get all account instances""" - +                  plugins = []          for plugin in self.accounts.keys():              plugins.append(self.getAccountPlugin(plugin)) - +                      return plugins      #----------------------------------------------------------------------      def loadAccounts(self):          """loads all accounts available""" - +                  if not exists("accounts.conf"):              f = open("accounts.conf", "wb")              f.write("version: " + str(ACC_VERSION))              f.close() - +                      f = open("accounts.conf", "rb")          content = f.readlines()          version = content[0].split(":")[1].strip() if content else "" @@ -87,57 +87,60 @@ class AccountManager():              f.close()              self.core.log.warning(_("Account settings deleted, due to new config format."))              return - +             +             +                  plugin = ""          name = "" - +                  for line in content[1:]:              line = line.strip() - +                          if not line: continue              if line.startswith("#"): continue              if line.startswith("version"): continue - +                          if line.endswith(":") and line.count(":") == 1:                  plugin = line[:-1]                  self.accounts[plugin] = {} - +                              elif line.startswith("@"):                  try:                      option = line[1:].split()                      self.accounts[plugin][name]["options"][option[0]] = [] if len(option) < 2 else ([option[1]] if len(option) < 3 else option[1:])                  except:                      pass - +                              elif ":" in line:                  name, sep, pw = line.partition(":")                  self.accounts[plugin][name] = {"password": pw, "options": {}, "valid": True}      #----------------------------------------------------------------------      def saveAccounts(self):          """save all account information""" - +                  f = open("accounts.conf", "wb")          f.write("version: " + str(ACC_VERSION) + "\n") - +                          for plugin, accounts in self.accounts.iteritems():              f.write("\n")              f.write(plugin+":\n") - +                          for name,data in accounts.iteritems():                  f.write("\n\t%s:%s\n" % (name,data["password"]) )                  if data["options"]:                      for option, values in data["options"].iteritems():                          f.write("\t@%s %s\n" % (option, " ".join(values))) - +                              f.close()          chmod(f.name, 0600) - +             +              #----------------------------------------------------------------------      def initAccountPlugins(self):          """init names"""          for name in self.core.pluginManager.getAccountPlugins():              self.accounts[name] = {} - +              @lock      def updateAccount(self, plugin , user, password=None, options={}):          """add or update account""" @@ -145,14 +148,14 @@ class AccountManager():              p = self.getAccountPlugin(plugin)              updated = p.updateAccounts(user, password, options)              #since accounts is a ref in plugin self.accounts doesnt need to be updated here - +                                  self.saveAccounts()              if updated: p.scheduleRefresh(user, force=False) - +                      @lock      def removeAccount(self, plugin, user):          """remove account""" - +                  if plugin in self.accounts:              p = self.getAccountPlugin(plugin)              p.removeAccount(user) @@ -166,7 +169,7 @@ class AccountManager():          if refresh:              self.core.scheduler.addJob(0, self.core.accountManager.getAccountInfos)              force = False - +                  for p in self.accounts.keys():              if self.accounts[p]:                  p = self.getAccountPlugin(p) @@ -176,7 +179,7 @@ class AccountManager():          e = AccountUpdateEvent()          self.core.pullManager.addEvent(e)          return data - +          def sendChange(self):          e = AccountUpdateEvent()          self.core.pullManager.addEvent(e) diff --git a/module/plugins/Container.py b/module/plugins/Container.py index 685ceac20..c233d3710 100644 --- a/module/plugins/Container.py +++ b/module/plugins/Container.py @@ -13,7 +13,7 @@      You should have received a copy of the GNU General Public License      along with this program; if not, see <http://www.gnu.org/licenses/>. - +          @author: mkaay  """ @@ -28,9 +28,9 @@ class Container(Crypter):      __version__ = "0.1"      __pattern__ = None      __type__ = "container" -    __description__ = """Base container decrypter plugin""" -    __author_name__ = "mkaay" -    __author_mail__ = "mkaay@mkaay.de" +    __description__ = """Base container plugin""" +    __author_name__ = ("mkaay") +    __author_mail__ = ("mkaay@mkaay.de")      def preprocessing(self, thread): @@ -38,19 +38,19 @@ class Container(Crypter):          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) @@ -58,7 +58,7 @@ class Container(Crypter):              f = open(self.pyfile.url, "wb" )              f.write(content)              f.close() - +                      else:              self.pyfile.name = basename(self.pyfile.url)              if not exists(self.pyfile.url): @@ -66,8 +66,10 @@ class Container(Crypter):                      self.pyfile.url = 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/module/plugins/Crypter.py b/module/plugins/Crypter.py index b2e6528e9..d1549fe80 100644 --- a/module/plugins/Crypter.py +++ b/module/plugins/Crypter.py @@ -13,7 +13,7 @@      You should have received a copy of the GNU General Public License      along with this program; if not, see <http://www.gnu.org/licenses/>. - +          @author: mkaay  """ @@ -23,23 +23,23 @@ class Crypter(Plugin):      __name__ = "Crypter"      __version__ = "0.1"      __pattern__ = None -    __type__ = "crypter" -    __description__ = """Base decrypter plugin""" -    __author_name__ = "mkaay" -    __author_mail__ = "mkaay@mkaay.de" - +    __type__ = "container" +    __description__ = """Base crypter plugin""" +    __author_name__ = ("mkaay") +    __author_mail__ = ("mkaay@mkaay.de") +          def __init__(self, pyfile):          Plugin.__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 = [] - +                  self.multiDL = True          self.limitDL = 0 - +          def preprocessing(self, thread):          """prepare""" @@ -47,9 +47,9 @@ class Crypter(Plugin):          self.thread = thread          self.decrypt(self.pyfile) - +                  self.createPackages() - +              def decrypt(self, pyfile):          raise NotImplementedError @@ -59,9 +59,9 @@ class Crypter(Plugin):          for pack in self.packages:              self.log.debug("Parsed package %(name)s with %(len)d links" % { "name" : pack[0], "len" : len(pack[1]) } ) - +                          links = [x.decode("utf-8") for x in pack[1]] - +                          pid = self.core.api.addPackage(pack[0], links, self.pyfile.package().queue)              if self.pyfile.package().password: @@ -69,3 +69,4 @@ class Crypter(Plugin):          if self.urls:              self.core.api.generateAndAddPackages(self.urls) +             diff --git a/module/plugins/Hook.py b/module/plugins/Hook.py index 6f5abffed..5efd08bae 100644 --- a/module/plugins/Hook.py +++ b/module/plugins/Hook.py @@ -13,7 +13,7 @@      You should have received a copy of the GNU General Public License      along with this program; if not, see <http://www.gnu.org/licenses/>. - +          @author: mkaay      @interface-version: 0.2  """ @@ -42,8 +42,8 @@ class Hook(Base):      __version__ = "0.2"      __type__ = "hook"      __threaded__ = [] -    __config__ = [("name", "type", "desc", "default")] -    __description__ = """Interface for hook""" +    __config__ = [ ("name", "type", "desc" , "default") ] +    __description__ = """interface for hook"""      __author_name__ = ("mkaay", "RaNaN")      __author_mail__ = ("mkaay@mkaay.de", "RaNaN@pyload.org") @@ -108,7 +108,7 @@ class Hook(Base):      def __repr__(self):          return "<Hook %s>" % self.__name__ - +                     def setup(self):          """ more init stuff if needed """          pass @@ -116,11 +116,11 @@ class Hook(Base):      def unload(self):          """ called when hook was deactivated """          pass - +          def isActivated(self):          """ checks if hook is activated"""          return self.config.getPlugin(self.__name__, "activated") - +          #event methods - overwrite these if needed          def coreReady(self): @@ -128,25 +128,25 @@ class Hook(Base):      def coreExiting(self):          pass - +          def downloadPreparing(self, pyfile):          pass - +          def downloadFinished(self, pyfile):          pass - +          def downloadFailed(self, pyfile):          pass - +          def packageFinished(self, pypack):          pass      def beforeReconnecting(self, ip):          pass - +          def afterReconnecting(self, ip):          pass - +          def periodical(self):          pass @@ -158,4 +158,4 @@ class Hook(Base):          pass      def captchaInvalid(self, task): -        pass +        pass
\ No newline at end of file diff --git a/module/plugins/Hoster.py b/module/plugins/Hoster.py index 4979d383b..814a70949 100644 --- a/module/plugins/Hoster.py +++ b/module/plugins/Hoster.py @@ -13,7 +13,7 @@      You should have received a copy of the GNU General Public License      along with this program; if not, see <http://www.gnu.org/licenses/>. - +          @author: mkaay  """ @@ -29,5 +29,5 @@ class Hoster(Plugin):      __pattern__ = None      __type__ = "hoster"      __description__ = """Base hoster plugin""" -    __author_name__ = "mkaay" -    __author_mail__ = "mkaay@mkaay.de" +    __author_name__ = ("mkaay") +    __author_mail__ = ("mkaay@mkaay.de") diff --git a/module/plugins/Plugin.py b/module/plugins/Plugin.py index 584fcce49..15bf3971f 100644 --- a/module/plugins/Plugin.py +++ b/module/plugins/Plugin.py @@ -146,7 +146,7 @@ class Plugin(Base):      __pattern__ = None      __type__ = "hoster"      __config__ = [("name", "type", "desc", "default")] -    __description__ = """Base plugin""" +    __description__ = """Base Plugin"""      __author_name__ = ("RaNaN", "spoob", "mkaay")      __author_mail__ = ("RaNaN@pyload.org", "spoob@pyload.org", "mkaay@mkaay.de") @@ -263,7 +263,7 @@ class Plugin(Base):      def setWait(self, seconds, reconnect=False):          """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          """ @@ -339,7 +339,7 @@ class Plugin(Base):          :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          """ @@ -528,7 +528,7 @@ class Plugin(Base):      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 diff --git a/module/plugins/PluginManager.py b/module/plugins/PluginManager.py index e976b2c4a..f3f5f47bc 100644 --- a/module/plugins/PluginManager.py +++ b/module/plugins/PluginManager.py @@ -13,7 +13,7 @@      You should have received a copy of the GNU General Public License      along with this program; if not, see <http://www.gnu.org/licenses/>. - +          @author: mkaay, RaNaN  """ @@ -79,11 +79,11 @@ class PluginManager:          """          returns dict with information           home contains parsed plugins from module. - +                  {          name : {path, version, config, (pattern, re), (plugin, class)}          } - +                  """          plugins = {}          if home: @@ -371,9 +371,10 @@ if __name__ == "__main__":      a = time() -    test = ["http://www.youtube.com/watch?v=%s" % x for x in xrange(0, 100)] +    test = ["http://www.youtube.com/watch?v=%s" % x for x in range(0, 100)]      print p.parseUrls(test)      b = time()      print b - a, "s" +     diff --git a/module/plugins/ReCaptcha.py b/module/plugins/ReCaptcha.py deleted file mode 100644 index 3e8b33fbd..000000000 --- a/module/plugins/ReCaptcha.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- - -import re - -class ReCaptcha(): -    def __init__(self, plugin): -        self.plugin = plugin - -    def challenge(self, id): -        js = self.plugin.req.load("http://www.google.com/recaptcha/api/challenge", get={"k":id}, cookies=True) - -        try: -            challenge = re.search("challenge : '(.*?)',", js).group(1) -            server = re.search("server : '(.*?)',", js).group(1) -        except: -            self.plugin.fail("recaptcha error") -        result = self.result(server,challenge) - -        return challenge, result - -    def result(self, server, challenge): -        return self.plugin.decryptCaptcha("%simage"%server, get={"c":challenge}, cookies=True, imgtype="jpg") diff --git a/module/plugins/accounts/AlldebridCom.py b/module/plugins/accounts/AlldebridCom.py index acfb6874a..278f3af06 100644 --- a/module/plugins/accounts/AlldebridCom.py +++ b/module/plugins/accounts/AlldebridCom.py @@ -1,54 +1,63 @@  # -*- coding: utf-8 -*- +import re  import xml.dom.minidom as dom +  from time import time -import re -import urllib -from module.plugins.Account import Account  from BeautifulSoup import BeautifulSoup +from module.plugins.Account import Account +  class AlldebridCom(Account): -    __name__ = "AlldebridCom" +    __name__    = "AlldebridCom" +    __type__    = "account"      __version__ = "0.22" -    __type__ = "account" +      __description__ = """AllDebrid.com account plugin""" -    __author_name__ = "Andy Voigt" -    __author_mail__ = "spamsales@online.de" +    __license__     = "GPLv3" +    __authors__     = [("Andy Voigt", "spamsales@online.de")] +      def loadAccountInfo(self, user, req):          data = self.getAccountData(user) -        page = req.load("http://www.alldebrid.com/account/") -        soup = BeautifulSoup(page) -        #Try to parse expiration date directly from the control panel page (better accuracy)         +        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() + 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:              data = self.getAccountData(user) -            page = req.load("http://www.alldebrid.com/api.php?action=info_user&login=%s&pw=%s" % (user, -                                                                                                  data["password"])) -            self.logDebug(page) -            xml = dom.parseString(page) +            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() + 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): -        urlparams = urllib.urlencode({'action': 'login', 'login_login': user, 'login_password': data["password"]}) -        page = req.load("http://www.alldebrid.com/register/?%s" % urlparams) - -        if "This login doesn't exist" in page: -            self.wrongPassword() -        if "The password is not valid" in page: -            self.wrongPassword() - -        if "Invalid captcha" in page: +    def login(self, user, data, req): +        html = req.load("http://www.alldebrid.com/register/", +                        get={'action'        : "login", +                             'login_login'   : user, +                             'login_password': data['password']}) + +        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/module/plugins/accounts/BayfilesCom.py b/module/plugins/accounts/BayfilesCom.py deleted file mode 100644 index bbd81e10b..000000000 --- a/module/plugins/accounts/BayfilesCom.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" - -from time import time - -from module.plugins.Account import Account -from module.common.json_layer import json_loads - - -class BayfilesCom(Account): -    __name__ = "BayfilesCom" -    __version__ = "0.03" -    __type__ = "account" -    __description__ = """Bayfiles.com account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" - -    def loadAccountInfo(self, user, req): -        for _ in xrange(2): -            response = json_loads(req.load("http://api.bayfiles.com/v1/account/info")) -            self.logDebug(response) -            if not response["error"]: -                break -            self.logWarning(response["error"]) -            self.relogin(user) - -        return {"premium": bool(response['premium']), "trafficleft": -1, -                "validuntil": response['expires'] if response['expires'] >= int(time()) else -1} - -    def login(self, user, data, req): -        response = json_loads(req.load("http://api.bayfiles.com/v1/account/login/%s/%s" % (user, data["password"]))) -        self.logDebug(response) -        if response["error"]: -            self.logError(response["error"]) -            self.wrongPassword() diff --git a/module/plugins/accounts/BillionuploadsCom.py b/module/plugins/accounts/BillionuploadsCom.py new file mode 100644 index 000000000..11af36591 --- /dev/null +++ b/module/plugins/accounts/BillionuploadsCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from module.plugins.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/module/plugins/accounts/BitshareCom.py b/module/plugins/accounts/BitshareCom.py index 4fe01aa5b..960ff6c3c 100644 --- a/module/plugins/accounts/BitshareCom.py +++ b/module/plugins/accounts/BitshareCom.py @@ -1,46 +1,33 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: pking -""" -  from module.plugins.Account import Account  class BitshareCom(Account): -    __name__ = "BitshareCom" +    __name__    = "BitshareCom" +    __type__    = "account"      __version__ = "0.12" -    __type__ = "account" +      __description__ = """Bitshare account plugin""" -    __author_name__ = "Paul King" -    __author_mail__ = "" +    __license__     = "GPLv3" +    __authors__     = [("Paul King", None)] +      def loadAccountInfo(self, user, req): -        page = req.load("http://bitshare.com/mysettings.html") +        html = req.load("http://bitshare.com/mysettings.html") -        if "\"http://bitshare.com/myupgrade.html\">Free" in page: +        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 page: +        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): -        page = req.load("http://bitshare.com/login.html", -                        post={"user": user, "password": data["password"], "submit": "Login"}, cookies=True) +        html = req.load("http://bitshare.com/login.html", +                        post={"user": user, "password": data['password'], "submit": "Login"}, cookies=True) +          if "login" in req.lastEffectiveURL:              self.wrongPassword() diff --git a/module/plugins/accounts/CatShareNet.py b/module/plugins/accounts/CatShareNet.py new file mode 100644 index 000000000..c33219685 --- /dev/null +++ b/module/plugins/accounts/CatShareNet.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +import re + +from time import mktime, strptime + +from module.plugins.Account import Account + + +class CatShareNet(Account): +    __name__    = "CatShareNet" +    __type__    = "account" +    __version__ = "0.01" + +    __description__ = """CatShareNet account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("prOq", None)] + + +    PREMIUM_PATTERN = r'class="nav-collapse collapse pull-right">[\s\w<>=-."/:]*\sz.</a></li>\s*<li><a href="/premium">.*\s*<span style="color: red">(.*?)</span>[\s\w<>/]*href="/logout"' +    VALID_UNTIL_PATTERN = r'<div class="span6 pull-right">[\s\w<>=-":;]*<span style="font-size:13px;">.*?<strong>(.*?)</strong></span>' + + +    def loadAccountInfo(self, user, req): +        premium = False +        validuntil = -1 + +        html = req.load("http://catshare.net/", decode=True) + +        try: +            m = re.search(self.PREMIUM_PATTERN, html) +            if "Premium" in m.group(1): +                premium = True +        except: +            pass + +        try: +            m = re.search(self.VALID_UNTIL_PATTERN, html) +            expiredate = m.group(1) +            if "-" not in expiredate: +                validuntil = mktime(strptime(expiredate, "%d.%m.%Y")) +        except: +            pass + +        return {'premium': premium, 'trafficleft': -1, '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"}) + +        if not '<a href="/logout">Wyloguj</a>' in html: +            self.wrongPassword() diff --git a/module/plugins/accounts/CloudzillaTo.py b/module/plugins/accounts/CloudzillaTo.py new file mode 100644 index 000000000..f0676f42f --- /dev/null +++ b/module/plugins/accounts/CloudzillaTo.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +import re + +from module.plugins.Account import Account + + +class CloudzillaTo(Account): +    __name__    = "CloudzillaTo" +    __type__    = "account" +    __version__ = "0.01" + +    __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"}) + +        if "ERROR" in html: +            self.wrongPassword() diff --git a/module/plugins/accounts/CramitIn.py b/module/plugins/accounts/CramitIn.py index 56cb5390b..a9e2274a2 100644 --- a/module/plugins/accounts/CramitIn.py +++ b/module/plugins/accounts/CramitIn.py @@ -1,14 +1,16 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.XFSPAccount import XFSPAccount +from module.plugins.internal.XFSAccount import XFSAccount -class CramitIn(XFSPAccount): -    __name__ = "CramitIn" -    __version__ = "0.01" -    __type__ = "account" +class CramitIn(XFSAccount): +    __name__    = "CramitIn" +    __type__    = "account" +    __version__ = "0.03" +      __description__ = """Cramit.in account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    MAIN_PAGE = "http://cramit.in/" +    HOSTER_DOMAIN = "cramit.in" diff --git a/module/plugins/accounts/CyberlockerCh.py b/module/plugins/accounts/CyberlockerCh.py deleted file mode 100644 index 4f3189b0c..000000000 --- a/module/plugins/accounts/CyberlockerCh.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- - -from module.plugins.internal.XFSPAccount import XFSPAccount -from module.plugins.internal.SimpleHoster import parseHtmlForm - - -class CyberlockerCh(XFSPAccount): -    __name__ = "CyberlockerCh" -    __version__ = "0.01" -    __type__ = "account" -    __description__ = """Cyberlocker.ch account plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" - -    MAIN_PAGE = "http://cyberlocker.ch/" - -    def login(self, user, data, req): -        html = req.load(self.MAIN_PAGE + 'login.html', decode=True) - -        action, inputs = parseHtmlForm('name="FL"', html) -        if not inputs: -            inputs = {"op": "login", -                      "redirect": self.MAIN_PAGE} - -        inputs.update({"login": user, -                       "password": data['password']}) - -        # Without this a 403 Forbidden is returned -        req.http.lastURL = self.MAIN_PAGE + 'login.html' -        html = req.load(self.MAIN_PAGE, post=inputs, decode=True) - -        if 'Incorrect Login or Password' in html or '>Error<' in html: -            self.wrongPassword() diff --git a/module/plugins/accounts/CzshareCom.py b/module/plugins/accounts/CzshareCom.py index bdd2000c9..414883228 100644 --- a/module/plugins/accounts/CzshareCom.py +++ b/module/plugins/accounts/CzshareCom.py @@ -1,22 +1,5 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  from time import mktime, strptime  import re @@ -24,31 +7,35 @@ from module.plugins.Account import Account  class CzshareCom(Account): -    __name__ = "CzshareCom" -    __version__ = "0.14" -    __type__ = "account" +    __name__    = "CzshareCom" +    __type__    = "account" +    __version__ = "0.15" +      __description__ = """Czshare.com account plugin, now Sdilej.cz""" -    __author_name__ = ("zoidberg", "stickell") -    __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it") +    __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>' -    CREDIT_LEFT_PATTERN = r'<tr class="active">\s*<td>([0-9 ,]+) (KiB|MiB|GiB)</td>\s*<td>([^<]*)</td>\s*</tr>'      def loadAccountInfo(self, user, req):          html = req.load("http://sdilej.cz/prehled_kreditu/") -        found = re.search(self.CREDIT_LEFT_PATTERN, html) -        if not found: +        m = re.search(self.CREDIT_LEFT_PATTERN, html) +        if m is None:              return {"validuntil": 0, "trafficleft": 0}          else: -            credits = float(found.group(1).replace(' ', '').replace(',', '.')) -            credits = credits * 1024 ** {'KiB': 0, 'MiB': 1, 'GiB': 2}[found.group(2)] -            validuntil = mktime(strptime(found.group(3), '%d.%m.%y %H:%M')) -            return {"validuntil": validuntil, "trafficleft": credits} +            trafficleft = self.parseTraffic(m.group(1).replace(' ', '').replace(',', '.')) + m.group(2)] +            validuntil  = mktime(strptime(m.group(3), '%d.%m.%y %H:%M')) +            return {"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-password": data['password'],              "login-name": user          }) diff --git a/module/plugins/accounts/DdlstorageCom.py b/module/plugins/accounts/DdlstorageCom.py deleted file mode 100644 index f3eebc59b..000000000 --- a/module/plugins/accounts/DdlstorageCom.py +++ /dev/null @@ -1,52 +0,0 @@ -# -*- coding: utf-8 -*- - -from hashlib import md5 -from time import mktime, strptime - -from module.plugins.internal.XFSPAccount import XFSPAccount -from module.common.json_layer import json_loads -from module.utils import parseFileSize - -# DDLStorage API Documentation: -# http://www.ddlstorage.com/cgi-bin/api_req.cgi?req_type=doc - - -class DdlstorageCom(XFSPAccount): -    __name__ = "DdlstorageCom" -    __version__ = "1.00" -    __type__ = "account" -    __description__ = """DDLStorage.com account plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" - -    MAIN_PAGE = "http://ddlstorage.com/" - -    def loadAccountInfo(self, user, req): -        password = self.accounts[user]['password'] -        api_data = req.load('http://www.ddlstorage.com/cgi-bin/api_req.cgi', -                            post={'req_type': 'user_info', -                                  'client_id': 53472, -                                  'user_login': user, -                                  'user_password': md5(password).hexdigest(), -                                  'sign': md5('user_info%d%s%s%s' % (53472, user, md5(password).hexdigest(), -                                                                     '25JcpU2dPOKg8E2OEoRqMSRu068r0Cv3')).hexdigest()}) -        api_data = api_data.replace('<pre>', '').replace('</pre>', '') -        self.logDebug('Account Info API data: ' + api_data) -        api_data = json_loads(api_data) - -        if api_data['status'] != 'OK':  # 'status' must be always OK for a working account -            return {"premium": False, "valid": False} - -        if api_data['account_type'] == 'REGISTERED': -            premium = False -            validuntil = None -        else: -            premium = True -            validuntil = int(mktime(strptime(api_data['premium_expire'], "%Y-%m-%d %H:%M:%S"))) - -        if api_data['usr_bandwidth_available'] == 'UNLIMITED': -            trafficleft = -1 -        else: -            trafficleft = parseFileSize(api_data['usr_bandwidth_available']) / 1024 - -        return {"premium": premium, "validuntil": validuntil, "trafficleft": trafficleft} diff --git a/module/plugins/accounts/DebridItaliaCom.py b/module/plugins/accounts/DebridItaliaCom.py index dd714102f..878153fbf 100644 --- a/module/plugins/accounts/DebridItaliaCom.py +++ b/module/plugins/accounts/DebridItaliaCom.py @@ -1,48 +1,44 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################  import re -import time + +from time import mktime, strptime  from module.plugins.Account import Account  class DebridItaliaCom(Account): -    __name__ = "DebridItaliaCom" -    __version__ = "0.1" -    __type__ = "account" +    __name__    = "DebridItaliaCom" +    __type__    = "account" +    __version__ = "0.12" +      __description__ = """Debriditalia.com account plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    WALID_UNTIL_PATTERN = r'Premium valid till: (.+?) \|' -    WALID_UNTIL_PATTERN = r"Premium valid till: (?P<D>[^|]+) \|"      def loadAccountInfo(self, user, req): -        if 'Account premium not activated' in self.html: -            return {"premium": False, "validuntil": None, "trafficleft": None} +        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 = mktime(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 -        m = re.search(self.WALID_UNTIL_PATTERN, self.html) -        if m: -            validuntil = int(time.mktime(time.strptime(m.group('D'), "%d/%m/%Y %H:%M"))) -            return {"premium": True, "validuntil": validuntil, "trafficleft": -1} -        else: -            self.logError('Unable to retrieve account information - Plugin may be out of date')      def login(self, user, data, req): -        self.html = req.load("http://debriditalia.com/login.php", -                             get={"u": user, "p": data["password"]}) -        if 'NO' in self.html: +        html = req.load("http://debriditalia.com/login.php", +                        get={'u': user, 'p': data['password']}) + +        if 'NO' in html:              self.wrongPassword() diff --git a/module/plugins/accounts/DepositfilesCom.py b/module/plugins/accounts/DepositfilesCom.py index 427f5c34d..ec23f7a51 100644 --- a/module/plugins/accounts/DepositfilesCom.py +++ b/module/plugins/accounts/DepositfilesCom.py @@ -1,47 +1,35 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay -""" -  import re +  from time import strptime, mktime  from module.plugins.Account import Account  class DepositfilesCom(Account): -    __name__ = "DepositfilesCom" -    __version__ = "0.2" -    __type__ = "account" +    __name__    = "DepositfilesCom" +    __type__    = "account" +    __version__ = "0.31" +      __description__ = """Depositfiles.com account plugin""" -    __author_name__ = ("mkaay", "stickell") -    __author_mail__ = ("mkaay@mkaay.de", "l.stickell@yahoo.it") +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de"), +                       ("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] +      def loadAccountInfo(self, user, req): -        src = req.load("http://depositfiles.com/de/gold/") -        validuntil = re.search(r"Sie haben Gold Zugang bis: <b>(.*?)</b></div>", src).group(1) +        html = req.load("https://dfiles.eu/de/gold/") +        validuntil = re.search(r"Sie haben Gold Zugang bis: <b>(.*?)</b></div>", html).group(1) -        validuntil = int(mktime(strptime(validuntil, "%Y-%m-%d %H:%M:%S"))) +        validuntil = mktime(strptime(validuntil, "%Y-%m-%d %H:%M:%S"))          return {"validuntil": validuntil, "trafficleft": -1} +      def login(self, user, data, req): -        req.load("http://depositfiles.com/de/gold/payment.php") -        src = req.load("http://depositfiles.com/de/login.php", get={"return": "/de/gold/payment.php"}, -                       post={"login": user, "password": data["password"]}) -        if r'<div class="error_message">Sie haben eine falsche Benutzername-Passwort-Kombination verwendet.</div>' in src: +        html = req.load("https://dfiles.eu/de/login.php", get={"return": "/de/gold/payment.php"}, +                        post={"login": user, "password": data['password']}) +        if r'<div class="error_message">Sie haben eine falsche Benutzername-Passwort-Kombination verwendet.</div>' in html:              self.wrongPassword() diff --git a/module/plugins/accounts/EasybytezCom.py b/module/plugins/accounts/EasybytezCom.py index 16ce674e2..93d3e2c19 100644 --- a/module/plugins/accounts/EasybytezCom.py +++ b/module/plugins/accounts/EasybytezCom.py @@ -1,76 +1,19 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re -from time import mktime, strptime, gmtime - -from module.plugins.Account import Account -from module.plugins.internal.SimpleHoster import parseHtmlForm -from module.utils import parseFileSize +from module.plugins.internal.XFSAccount import XFSAccount -class EasybytezCom(Account): -    __name__ = "EasybytezCom" -    __version__ = "0.04" -    __type__ = "account" -    __description__ = """EasyBytez.com account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" -    VALID_UNTIL_PATTERN = r'Premium account expire:</TD><TD><b>([^<]+)</b>' -    TRAFFIC_LEFT_PATTERN = r'<TR><TD>Traffic available today:</TD><TD><b>(?P<S>[^<]+)</b>' +class EasybytezCom(XFSAccount): +    __name__    = "EasybytezCom" +    __type__    = "account" +    __version__ = "0.12" -    def loadAccountInfo(self, user, req): -        html = req.load("http://www.easybytez.com/?op=my_account", decode=True) - -        validuntil = trafficleft = None -        premium = False - -        found = re.search(self.VALID_UNTIL_PATTERN, html) -        if found: -            try: -                self.logDebug("Expire date: " + found.group(1)) -                validuntil = mktime(strptime(found.group(1), "%d %B %Y")) -            except Exception, e: -                self.logError(e) -            if validuntil > mktime(gmtime()): -                premium = True -                trafficleft = -1 -        else: -            found = re.search(self.TRAFFIC_LEFT_PATTERN, html) -            if found: -                trafficleft = found.group(1) -                if "Unlimited" in trafficleft: -                    trafficleft = -1 -                else: -                    trafficleft = parseFileSize(trafficleft) / 1024 - -        return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium} - -    def login(self, user, data, req): -        html = req.load('http://www.easybytez.com/login.html', decode=True) -        action, inputs = parseHtmlForm('name="FL"', html) -        inputs.update({"login": user, -                       "password": data['password'], -                       "redirect": "http://www.easybytez.com/"}) +    __description__ = """EasyBytez.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("guidobelix", "guidobelix@hotmail.it")] -        html = req.load(action, post=inputs, decode=True) -        if 'Incorrect Login or Password' in html or '>Error<' in html: -            self.wrongPassword() +    HOSTER_DOMAIN = "easybytez.com" diff --git a/module/plugins/accounts/EgoFilesCom.py b/module/plugins/accounts/EgoFilesCom.py deleted file mode 100644 index e5c781068..000000000 --- a/module/plugins/accounts/EgoFilesCom.py +++ /dev/null @@ -1,42 +0,0 @@ -# -*- coding: utf-8 -*- - -import re -import time - -from module.plugins.Account import Account -from module.utils import parseFileSize - - -class EgoFilesCom(Account): -    __name__ = "EgoFilesCom" -    __version__ = "0.2" -    __type__ = "account" -    __description__ = """Egofiles.com account plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" - -    PREMIUM_ACCOUNT_PATTERN = '<br/>\s*Premium: (?P<P>[^/]*) / Traffic left: (?P<T>[\d.]*) (?P<U>\w*)\s*\\n\s*<br/>' - -    def loadAccountInfo(self, user, req): -        html = req.load("http://egofiles.com") -        if 'You are logged as a Free User' in html: -            return {"premium": False, "validuntil": None, "trafficleft": None} - -        m = re.search(self.PREMIUM_ACCOUNT_PATTERN, html) -        if m: -            validuntil = int(time.mktime(time.strptime(m.group('P'), "%Y-%m-%d %H:%M:%S"))) -            trafficleft = parseFileSize(m.group('T'), m.group('U')) / 1024 -            return {"premium": True, "validuntil": validuntil, "trafficleft": trafficleft} -        else: -            self.logError('Unable to retrieve account information - Plugin may be out of date') - -    def login(self, user, data, req): -        # Set English language -        req.load("https://egofiles.com/ajax/lang.php?lang=en", just_header=True) - -        html = req.load("http://egofiles.com/ajax/register.php", -                        post={"log": 1, -                              "loginV": user, -                              "passV": data["password"]}) -        if 'Login successful' not in html: -            self.wrongPassword() diff --git a/module/plugins/accounts/EuroshareEu.py b/module/plugins/accounts/EuroshareEu.py index 3fb0d7f50..c75f8ee33 100644 --- a/module/plugins/accounts/EuroshareEu.py +++ b/module/plugins/accounts/EuroshareEu.py @@ -1,22 +1,5 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  from time import mktime, strptime  import re @@ -24,32 +7,34 @@ from module.plugins.Account import Account  class EuroshareEu(Account): -    __name__ = "EuroshareEu" +    __name__    = "EuroshareEu" +    __type__    = "account"      __version__ = "0.01" -    __type__ = "account" +      __description__ = """Euroshare.eu account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] +      def loadAccountInfo(self, user, req):          self.relogin(user)          html = req.load("http://euroshare.eu/customer-zone/settings/") -        found = re.search('id="input_expire_date" value="(\d+\.\d+\.\d+ \d+:\d+)"', html) -        if found is None: +        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 = mktime(strptime(found.group(1), "%d.%m.%Y %H:%M")) +            validuntil = mktime(strptime(m.group(1), "%d.%m.%Y %H:%M"))          return {"validuntil": validuntil, "trafficleft": -1, "premium": premium} -    def login(self, user, data, req): +    def login(self, user, data, req):          html = req.load('http://euroshare.eu/customer-zone/login/', post={              "trvale": "1",              "login": user, -            "password": data["password"] +            "password": data['password']          }, decode=True)          if u">Nesprávne prihlasovacie meno alebo heslo" in html: diff --git a/module/plugins/accounts/FastixRu.py b/module/plugins/accounts/FastixRu.py index dbfd1f33e..d33d611c9 100644 --- a/module/plugins/accounts/FastixRu.py +++ b/module/plugins/accounts/FastixRu.py @@ -5,30 +5,34 @@ from module.common.json_layer import json_loads  class FastixRu(Account): -    __name__ = "FastixRu" +    __name__    = "FastixRu" +    __type__    = "account"      __version__ = "0.02" -    __type__ = "account" +      __description__ = """Fastix account plugin""" -    __author_name__ = "Massimo Rosamilia" -    __author_mail__ = "max@spiritix.eu" +    __license__     = "GPLv3" +    __authors__     = [("Massimo Rosamilia", "max@spiritix.eu")] +      def loadAccountInfo(self, user, req):          data = self.getAccountData(user) -        page = req.load("http://fastix.ru/api_v2/?apikey=%s&sub=getaccountdetails" % (data["api"])) -        page = json_loads(page) -        points = page['points'] -        kb = float(points) -        kb = kb * 1024 ** 2 / 1000 +        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): -        page = req.load("http://fastix.ru/api_v2/?sub=get_apikey&email=%s&password=%s" % (user, data["password"])) -        api = json_loads(page) +        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 page: +        data['api'] = api +        if "error_code" in html:              self.wrongPassword() diff --git a/module/plugins/accounts/FastshareCz.py b/module/plugins/accounts/FastshareCz.py index ba6105e6e..d6e94f2e3 100644 --- a/module/plugins/accounts/FastshareCz.py +++ b/module/plugins/accounts/FastshareCz.py @@ -1,56 +1,52 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re +  from module.plugins.Account import Account -from module.utils import parseFileSize  class FastshareCz(Account): -    __name__ = "FastshareCz" -    __version__ = "0.03" -    __type__ = "account" +    __name__    = "FastshareCz" +    __type__    = "account" +    __version__ = "0.05" +      __description__ = """Fastshare.cz account plugin""" -    __author_name__ = ("zoidberg", "stickell") -    __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it") +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    CREDIT_PATTERN = r'My account\s*\((.+?)\)' -    CREDIT_PATTERN = r'(?:Kredit|Credit)\s*</td>\s*<td[^>]*>([\d. \w]+) '      def loadAccountInfo(self, user, req): +        validuntil  = None +        trafficleft = None +        premium     = None +          html = req.load("http://www.fastshare.cz/user", decode=True) -        found = re.search(self.CREDIT_PATTERN, html) -        if found: -            trafficleft = parseFileSize(found.group(1)) / 1024 -            premium = True if trafficleft else False +        m = re.search(self.CREDIT_PATTERN, html) +        if m: +            trafficleft = self.parseTraffic(m.group(1)) + +        if trafficleft: +            premium = True +            validuntil = -1          else: -            trafficleft = None              premium = False -        return {"validuntil": -1, "trafficleft": trafficleft, "premium": premium} +        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={ -            "heslo": data['password'], -            "login": user -        }, decode=True) -        if u'>Å patné uÅŸivatelské jméno nebo heslo.<' in html: +        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/module/plugins/accounts/File4safeCom.py b/module/plugins/accounts/File4safeCom.py index 6a11493d2..20053d895 100644 --- a/module/plugins/accounts/File4safeCom.py +++ b/module/plugins/accounts/File4safeCom.py @@ -1,17 +1,18 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.XFSPAccount import XFSPAccount +from module.plugins.internal.XFSAccount import XFSAccount -class File4safeCom(XFSPAccount): -    __name__ = "File4safeCom" -    __version__ = "0.01" -    __type__ = "account" +class File4safeCom(XFSAccount): +    __name__    = "File4safeCom" +    __type__    = "account" +    __version__ = "0.04" +      __description__ = """File4safe.com account plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + -    MAIN_PAGE = "http://file4safe.com/" +    HOSTER_DOMAIN = "file4safe.com"      LOGIN_FAIL_PATTERN = r'input_login' -    PREMIUM_PATTERN = r'Extend Premium' diff --git a/module/plugins/accounts/FileParadoxIn.py b/module/plugins/accounts/FileParadoxIn.py new file mode 100644 index 000000000..c12d99d6a --- /dev/null +++ b/module/plugins/accounts/FileParadoxIn.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from module.plugins.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/module/plugins/accounts/FilecloudIo.py b/module/plugins/accounts/FilecloudIo.py index f2becb8c9..d20f756f3 100644 --- a/module/plugins/accounts/FilecloudIo.py +++ b/module/plugins/accounts/FilecloudIo.py @@ -1,44 +1,30 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  from module.plugins.Account import Account  from module.common.json_layer import json_loads  class FilecloudIo(Account): -    __name__ = "FilecloudIo" -    __version__ = "0.02" -    __type__ = "account" +    __name__    = "FilecloudIo" +    __type__    = "account" +    __version__ = "0.03" +      __description__ = """FilecloudIo account plugin""" -    __author_name__ = ("zoidberg", "stickell") -    __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it") +    __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 _ in xrange(5): +        for _i in xrange(5):              rep = req.load("https://secure.filecloud.io/api-fetch_apikey.api",                             post={"username": user, "password": self.accounts[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") +                self.logError(_("Wrong username or password"))                  return {"valid": False, "premium": False}          else:              return {"premium": False} @@ -50,10 +36,11 @@ class FilecloudIo(Account):          rep = json_loads(rep)          if rep['is_premium'] == 1: -            return {"validuntil": int(rep["premium_until"]), "trafficleft": -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') @@ -61,8 +48,8 @@ class FilecloudIo(Account):          if not hasattr(self, "form_data"):              self.form_data = {} -        self.form_data["username"] = user -        self.form_data["password"] = data['password'] +        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, diff --git a/module/plugins/accounts/FilefactoryCom.py b/module/plugins/accounts/FilefactoryCom.py index 0eded0edf..8394c549e 100644 --- a/module/plugins/accounts/FilefactoryCom.py +++ b/module/plugins/accounts/FilefactoryCom.py @@ -1,18 +1,4 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################  import re  from time import mktime, strptime @@ -23,14 +9,18 @@ from module.plugins.Account import Account  class FilefactoryCom(Account): -    __name__ = "FilefactoryCom" +    __name__    = "FilefactoryCom" +    __type__    = "account"      __version__ = "0.14" -    __type__ = "account" +      __description__ = """Filefactory.com account plugin""" -    __author_name__ = ("zoidberg", "stickell") -    __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it") +    __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>' -    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/") @@ -38,7 +28,7 @@ class FilefactoryCom(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 = re.sub(self.VALID_UNTIL_PATTERN, '\g<D> \g<M> \g<Y>', m.group(0))              validuntil = mktime(strptime(validuntil, "%d %b %Y"))          else:              premium = False @@ -46,12 +36,13 @@ class FilefactoryCom(Account):          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"], +            "loginPassword": data['password'],              "Submit": "Sign In"})          if req.lastEffectiveURL != "http://www.filefactory.com/account/": diff --git a/module/plugins/accounts/FilejungleCom.py b/module/plugins/accounts/FilejungleCom.py index 304f20040..a3ec7af64 100644 --- a/module/plugins/accounts/FilejungleCom.py +++ b/module/plugins/accounts/FilejungleCom.py @@ -1,22 +1,5 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re  from time import mktime, strptime @@ -24,35 +7,39 @@ from module.plugins.Account import Account  class FilejungleCom(Account): -    __name__ = "FilejungleCom" +    __name__    = "FilejungleCom" +    __type__    = "account"      __version__ = "0.11" -    __type__ = "account" +      __description__ = """Filejungle.com account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] +      login_timeout = 60      URL = "http://filejungle.com/" -    TRAFFIC_LEFT_PATTERN = r'"/extend_premium\.php">Until (\d+ [A-Za-z]+ \d+)<br' +    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") -        found = re.search(self.TRAFFIC_LEFT_PATTERN, html) -        if found: +        m = re.search(self.TRAFFIC_LEFT_PATTERN, html) +        if m:              premium = True -            validuntil = mktime(strptime(found.group(1), "%d %b %Y")) +            validuntil = mktime(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"], +            "loginUserPassword": data['password'],              "loginFormSubmit": "Login",              "recaptcha_challenge_field": "",              "recaptcha_response_field": "", diff --git a/module/plugins/accounts/FileomCom.py b/module/plugins/accounts/FileomCom.py new file mode 100644 index 000000000..7c743f56a --- /dev/null +++ b/module/plugins/accounts/FileomCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from module.plugins.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/module/plugins/accounts/FilerNet.py b/module/plugins/accounts/FilerNet.py index 28ddf2e3f..a845e7ba4 100644 --- a/module/plugins/accounts/FilerNet.py +++ b/module/plugins/accounts/FilerNet.py @@ -1,61 +1,52 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################  import re  import time  from module.plugins.Account import Account -from module.utils import parseFileSize  class FilerNet(Account): -    __name__ = "FilerNet" -    __version__ = "0.01" -    __type__ = "account" +    __name__    = "FilerNet" +    __type__    = "account" +    __version__ = "0.03" +      __description__ = """Filer.net account plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __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>" +    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): -        self.html = req.load("https://filer.net/profile") +        html = req.load("https://filer.net/profile")          # Free user -        if re.search(self.FREE_PATTERN, self.html): +        if re.search(self.FREE_PATTERN, html):              return {"premium": False, "validuntil": None, "trafficleft": None} -        until = re.search(self.WALID_UNTIL_PATTERN, self.html) -        traffic = re.search(self.TRAFFIC_PATTERN, self.html) +        until   = re.search(self.WALID_UNTIL_PATTERN, html) +        traffic = re.search(self.TRAFFIC_PATTERN, html) +          if until and traffic: -            validuntil = int(time.mktime(time.strptime(until.group(1), "%d.%m.%Y %H:%M:%S"))) -            trafficleft = parseFileSize(traffic.group(1)) / 1024 +            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 - Plugin may be out of date') +            self.logError(_("Unable to retrieve account information"))              return {"premium": False, "validuntil": None, "trafficleft": None} +      def login(self, user, data, req): -        self.html = req.load("https://filer.net/login") -        token = re.search(self.TOKEN_PATTERN, self.html).group(1) -        self.html = req.load("https://filer.net/login_check", -                             post={"_username": user, "_password": data["password"], +        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/"}) -        if 'Logout' not in self.html: +        if 'Logout' not in html:              self.wrongPassword() diff --git a/module/plugins/accounts/FilerioCom.py b/module/plugins/accounts/FilerioCom.py index 5f2164cf1..4c6755293 100644 --- a/module/plugins/accounts/FilerioCom.py +++ b/module/plugins/accounts/FilerioCom.py @@ -1,14 +1,16 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.XFSPAccount import XFSPAccount +from module.plugins.internal.XFSAccount import XFSAccount -class FilerioCom(XFSPAccount): -    __name__ = "FilerioCom" -    __version__ = "0.01" -    __type__ = "account" +class FilerioCom(XFSAccount): +    __name__    = "FilerioCom" +    __type__    = "account" +    __version__ = "0.03" +      __description__ = """FileRio.in account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    MAIN_PAGE = "http://filerio.in/" +    HOSTER_DOMAIN = "filerio.in" diff --git a/module/plugins/accounts/FilesMailRu.py b/module/plugins/accounts/FilesMailRu.py index 794e80d84..f91f4d5ba 100644 --- a/module/plugins/accounts/FilesMailRu.py +++ b/module/plugins/accounts/FilesMailRu.py @@ -1,42 +1,28 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: RaNaN -""" -  from module.plugins.Account import Account  class FilesMailRu(Account): -    __name__ = "FilesMailRu" -    __version__ = "0.1" -    __type__ = "account" +    __name__    = "FilesMailRu" +    __type__    = "account" +    __version__ = "0.10" +      __description__ = """Filesmail.ru account plugin""" -    __author_name__ = "RaNaN" -    __author_mail__ = "RaNaN@pyload.org" +    __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("@") -        page = req.load("http://swa.mail.ru/cgi-bin/auth", None, +        html = req.load("http://swa.mail.ru/cgi-bin/auth", None,                          {"Domain": domain, "Login": user, "Password": data['password'],                           "Page": "http://files.mail.ru/"}, cookies=True) -        if "ÐевеÑМПе ÐžÐŒÑ Ð¿ÐŸÐ»ÑзПваÑÐµÐ»Ñ ÐžÐ»Ðž паÑПлÑ" in page:  # @TODO seems not to work +        if "ÐевеÑМПе ÐžÐŒÑ Ð¿ÐŸÐ»ÑзПваÑÐµÐ»Ñ ÐžÐ»Ðž паÑПлÑ" in html:  # @TODO seems not to work              self.wrongPassword() diff --git a/module/plugins/accounts/FileserveCom.py b/module/plugins/accounts/FileserveCom.py index 266935a9f..1cf2a3a3c 100644 --- a/module/plugins/accounts/FileserveCom.py +++ b/module/plugins/accounts/FileserveCom.py @@ -1,22 +1,5 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay -""" -  from time import mktime, strptime  from module.plugins.Account import Account @@ -24,35 +7,38 @@ from module.common.json_layer import json_loads  class FileserveCom(Account): -    __name__ = "FileserveCom" -    __version__ = "0.2" -    __type__ = "account" +    __name__    = "FileserveCom" +    __type__    = "account" +    __version__ = "0.20" +      __description__ = """Fileserve.com account plugin""" -    __author_name__ = "mkaay" -    __author_mail__ = "mkaay@mkaay.de" +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de")] +      def loadAccountInfo(self, user, req):          data = self.getAccountData(user) -        page = req.load("http://app.fileserve.com/api/login/", post={"username": user, "password": data["password"], +        html = req.load("http://app.fileserve.com/api/login/", post={"username": user, "password": data['password'],                                                                       "submit": "Submit+Query"}) -        res = json_loads(page) +        res = json_loads(html) -        if res["type"] == "premium": -            validuntil = mktime(strptime(res["expireTime"], "%Y-%m-%d %H:%M:%S")) -            return {"trafficleft": res["traffic"], "validuntil": validuntil} +        if res['type'] == "premium": +            validuntil = mktime(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): -        page = req.load("http://app.fileserve.com/api/login/", post={"username": user, "password": data["password"], +        html = req.load("http://app.fileserve.com/api/login/", post={"username": user, "password": data['password'],                                                                       "submit": "Submit+Query"}) -        res = json_loads(page) +        res = json_loads(html) -        if not res["type"]: +        if not res['type']:              self.wrongPassword() -        #login at fileserv page +        #login at fileserv html          req.load("http://www.fileserve.com/login.php", -                 post={"loginUserName": user, "loginUserPassword": data["password"], "autoLogin": "checked", +                 post={"loginUserName": user, "loginUserPassword": data['password'], "autoLogin": "checked",                         "loginFormSubmit": "Login"}) diff --git a/module/plugins/accounts/FourSharedCom.py b/module/plugins/accounts/FourSharedCom.py index 869705313..ec19f83f5 100644 --- a/module/plugins/accounts/FourSharedCom.py +++ b/module/plugins/accounts/FourSharedCom.py @@ -1,49 +1,33 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" - -from module.plugins.Account import Account  from module.common.json_layer import json_loads +from module.plugins.Account import Account  class FourSharedCom(Account): -    __name__ = "FourSharedCom" -    __version__ = "0.01" -    __type__ = "account" +    __name__    = "FourSharedCom" +    __type__    = "account" +    __version__ = "0.03" +      __description__ = """FourShared.com account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] +      def loadAccountInfo(self, user, req): -        #fixme -        return {"validuntil": -1, "trafficleft": -1, "premium": False} +        # Free mode only for now +        return {"premium": False} +      def login(self, user, data, req): -        req.cj.setCookie("www.4shared.com", "4langcookie", "en") -        response = req.load('http://www.4shared.com/login', -                            post={"login": user, -                                  "password": data['password'], -                                  "remember": "false", -                                  "doNotRedirect": "true"}) -        self.logDebug(response) -        response = json_loads(response) - -        if not "ok" in response or response['ok'] != True: -            if "rejectReason" in response and response['rejectReason'] != True: -                self.logError(response['rejectReason']) +        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"}) + +        if 'Please log in to access your 4shared account' in res:              self.wrongPassword() diff --git a/module/plugins/accounts/FreakshareCom.py b/module/plugins/accounts/FreakshareCom.py index 6628db6b1..576d835e2 100644 --- a/module/plugins/accounts/FreakshareCom.py +++ b/module/plugins/accounts/FreakshareCom.py @@ -1,53 +1,51 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: RaNaN -"""  import re +  from time import strptime, mktime  from module.plugins.Account import Account  class FreakshareCom(Account): -    __name__ = "FreakshareCom" -    __version__ = "0.1" -    __type__ = "account" +    __name__    = "FreakshareCom" +    __type__    = "account" +    __version__ = "0.12" +      __description__ = """Freakshare.com account plugin""" -    __author_name__ = "RaNaN" -    __author_mail__ = "RaNaN@pyload.org" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org")] +      def loadAccountInfo(self, user, req): -        page = req.load("http://freakshare.com/") +        premium = False +        validuntil  = None +        trafficleft = None + +        html = req.load("http://freakshare.com/") -        validuntil = r"ltig bis:</td>\s*<td><b>([0-9 \-:.]+)</b></td>" -        validuntil = re.search(validuntil, page, re.MULTILINE) -        validuntil = validuntil.group(1).strip() -        validuntil = mktime(strptime(validuntil, "%d.%m.%Y - %H:%M")) +        try: +            m = re.search(r'ltig bis:</td>\s*<td><b>([\d.:-]+)</b></td>', html, re.M) +            validuntil = mktime(strptime(m.group(1).strip(), "%d.%m.%Y - %H:%M")) -        traffic = r"Traffic verbleibend:</td>\s*<td>([^<]+)" -        traffic = re.search(traffic, page, re.MULTILINE) -        traffic = traffic.group(1).strip() -        traffic = self.parseTraffic(traffic) +        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} -        return {"validuntil": validuntil, "trafficleft": traffic}      def login(self, user, data, req): -        page = req.load("http://freakshare.com/login.html", None, +        req.load("http://freakshare.com/index.php?language=EN") + +        html = req.load("http://freakshare.com/login.html", None,                          {"submit": "Login", "user": user, "pass": data['password']}, cookies=True) -        if "Falsche Logindaten!" in page or "Wrong Username or Password!" in page: +        if ">Wrong Username or Password" in html:              self.wrongPassword() diff --git a/module/plugins/accounts/FreeWayMe.py b/module/plugins/accounts/FreeWayMe.py index 955cc3c7f..3b9841630 100644 --- a/module/plugins/accounts/FreeWayMe.py +++ b/module/plugins/accounts/FreeWayMe.py @@ -1,33 +1,18 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: Nicolas Giese -""" -  from module.plugins.Account import Account  from module.common.json_layer import json_loads  class FreeWayMe(Account): -    __name__ = "FreeWayMe" -    __version__ = "0.11" -    __type__ = "account" +    __name__    = "FreeWayMe" +    __type__    = "account" +    __version__ = "0.12" +      __description__ = """FreeWayMe account plugin""" -    __author_name__ = "Nicolas Giese" -    __author_mail__ = "james@free-way.me" +    __license__     = "GPLv3" +    __authors__     = [("Nicolas Giese", "james@free-way.me")] +      def loadAccountInfo(self, user, req):          status = self.getAccountStatus(user, req) @@ -36,19 +21,21 @@ class FreeWayMe(Account):          self.logDebug(status)          account_info = {"validuntil": -1, "premium": False} -        if status["premium"] == "Free": -            account_info["trafficleft"] = int(status["guthaben"]) * 1024 -        elif status["premium"] == "Spender": -            account_info["trafficleft"] = -1 -        elif status["premium"] == "Flatrate": -            account_info = {"validuntil": int(status["Flatrate"]), +        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 getpw(self, user): -        return self.accounts[user]["password"] +        return self.accounts[user]['password'] +      def login(self, user, data, req):          status = self.getAccountStatus(user, req) @@ -57,10 +44,11 @@ class FreeWayMe(Account):          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.accounts[user]["password"]}) -        self.logDebug("login: %s" % answer) +                          get={"id": 4, "user": user, "pass": self.accounts[user]['password']}) +        self.logDebug("Login: %s" % answer)          if answer == "Invalid login":              self.wrongPassword()              return False diff --git a/module/plugins/accounts/FshareVn.py b/module/plugins/accounts/FshareVn.py index cb2ad700c..2da45aac6 100644 --- a/module/plugins/accounts/FshareVn.py +++ b/module/plugins/accounts/FshareVn.py @@ -1,22 +1,5 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  from time import mktime, strptime  from pycurl import REFERER  import re @@ -25,30 +8,34 @@ from module.plugins.Account import Account  class FshareVn(Account): -    __name__ = "FshareVn" -    __version__ = "0.06" -    __type__ = "account" +    __name__    = "FshareVn" +    __type__    = "account" +    __version__ = "0.08" +      __description__ = """Fshare.vn account plugin""" -    __author_name__ = ("zoidberg", "stickell") -    __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it") +    __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[^>]*>([0-9.]+) ([kKMG])B</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): -        self.html = req.load("http://www.fshare.vn/account_info.php", decode=True) +        html = req.load("http://www.fshare.vn/account_info.php", decode=True) -        if re.search(self.LIFETIME_PATTERN, self.html): +        if re.search(self.LIFETIME_PATTERN, html):              self.logDebug("Lifetime membership detected")              trafficleft = self.getTrafficLeft()              return {"validuntil": -1, "trafficleft": trafficleft, "premium": True} -        found = re.search(self.VALID_UNTIL_PATTERN, self.html) -        if found: +        m = re.search(self.VALID_UNTIL_PATTERN, html) +        if m:              premium = True -            validuntil = mktime(strptime(found.group(1), '%I:%M:%S %p %d-%m-%Y')) +            validuntil = mktime(strptime(m.group(1), '%I:%M:%S %p %d-%m-%Y'))              trafficleft = self.getTrafficLeft()          else:              premium = False @@ -57,18 +44,20 @@ class FshareVn(Account):          return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium} +      def login(self, user, data, req):          req.http.c.setopt(REFERER, "https://www.fshare.vn/login.php") -        self.html = req.load('https://www.fshare.vn/login.php', post={ +        html = req.load('https://www.fshare.vn/login.php', post={              "login_password": data['password'],              "login_useremail": user, -            "url_refe": "https://www.fshare.vn/login.php" +            "url_refe": "http://www.fshare.vn/index.php"          }, referer=True, decode=True) -        if not '<img alt="VIP"' in self.html: +        if not re.search(r'<img\s+alt="VIP"', html):              self.wrongPassword() +      def getTrafficLeft(self): -        found = re.search(self.TRAFFIC_LEFT_PATTERN, self.html) -        return float(found.group(1)) * 1024 ** {'k': 0, 'K': 0, 'M': 1, 'G': 2}[found.group(2)] if found else 0 +        m = re.search(self.TRAFFIC_LEFT_PATTERN, html) +        return self.parseTraffic(m.group(1) + m.group(2)) if m else 0 diff --git a/module/plugins/accounts/Ftp.py b/module/plugins/accounts/Ftp.py index b454cba7a..f978d2fa0 100644 --- a/module/plugins/accounts/Ftp.py +++ b/module/plugins/accounts/Ftp.py @@ -4,11 +4,14 @@ from module.plugins.Account import Account  class Ftp(Account): -    __name__ = "Ftp" +    __name__    = "Ftp" +    __type__    = "account"      __version__ = "0.01" -    __type__ = "account" +      __description__ = """Ftp dummy account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    login_timeout = info_threshold = 1000000 +    info_threshold = 1000000 +    login_timeout = 1000000 diff --git a/module/plugins/accounts/HellshareCz.py b/module/plugins/accounts/HellshareCz.py index b6c738715..dff2fe394 100644 --- a/module/plugins/accounts/HellshareCz.py +++ b/module/plugins/accounts/HellshareCz.py @@ -1,22 +1,5 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re  import time @@ -24,26 +7,29 @@ from module.plugins.Account import Account  class HellshareCz(Account): -    __name__ = "HellshareCz" -    __version__ = "0.14" -    __type__ = "account" +    __name__    = "HellshareCz" +    __type__    = "account" +    __version__ = "0.15" +      __description__ = """Hellshare.cz account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __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/") -        found = re.search(self.CREDIT_LEFT_PATTERN, html) -        if found is None: +        m = re.search(self.CREDIT_LEFT_PATTERN, html) +        if m is None:              trafficleft = None              validuntil = None              premium = False          else: -            credit = found.group(1) +            credit = m.group(1)              premium = True              try:                  if "." in credit: @@ -55,32 +41,33 @@ class HellshareCz(Account):                      trafficleft = -1                  else:                      #Traffic-based account -                    trafficleft = int(credit) * 1024 +                    trafficleft = self.parseTraffic(credit + "MB")                      validuntil = -1              except Exception, e: -                self.logError('Unable to parse credit info', 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/')          if req.lastEffectiveURL != 'http://www.hellshare.com/':              #Switch to English -            self.logDebug('Switch lang - URL: %s' % req.lastEffectiveURL) +            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) +            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)          if re.search(self.CREDIT_LEFT_PATTERN, html): -            self.logDebug('Already logged in') +            self.logDebug("Already logged in")              return          html = req.load('http://www.hellshare.com/login?do=loginForm-submit', post={              "login": "Log in", -            "password": data["password"], +            "password": data['password'],              "username": user,              "perm_login": "on"          }) diff --git a/module/plugins/accounts/HotfileCom.py b/module/plugins/accounts/HotfileCom.py deleted file mode 100644 index f6988542f..000000000 --- a/module/plugins/accounts/HotfileCom.py +++ /dev/null @@ -1,89 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay, JoKoT3 -""" - -from time import strptime, mktime -import hashlib - -from module.plugins.Account import Account - - -class HotfileCom(Account): -    __name__ = "HotfileCom" -    __version__ = "0.2" -    __type__ = "account" -    __description__ = """Hotfile.com account plugin""" -    __author_name__ = ("mkaay", "JoKoT3") -    __author_mail__ = ("mkaay@mkaay.de", "jokot3@gmail.com") - -    def loadAccountInfo(self, user, req): -        resp = self.apiCall("getuserinfo", user=user) -        if resp.startswith("."): -            self.core.debug("HotfileCom API Error: %s" % resp) -            raise Exception -        info = {} -        for p in resp.split("&"): -            key, value = p.split("=") -            info[key] = value - -        if info['is_premium'] == '1': -            info["premium_until"] = info["premium_until"].replace("T", " ") -            zone = info["premium_until"][19:] -            info["premium_until"] = info["premium_until"][:19] -            zone = int(zone[:3]) - -            validuntil = int(mktime(strptime(info["premium_until"], "%Y-%m-%d %H:%M:%S"))) + (zone * 60 * 60) -            tmp = {"validuntil": validuntil, "trafficleft": -1, "premium": True} - -        elif info['is_premium'] == '0': -            tmp = {"premium": False} - -        return tmp - -    def apiCall(self, method, post={}, user=None): -        if user: -            data = self.getAccountData(user) -        else: -            user, data = self.selectAccount() - -        req = self.getAccountRequest(user) - -        digest = req.load("http://api.hotfile.com/", post={"action": "getdigest"}) -        h = hashlib.md5() -        h.update(data["password"]) -        hp = h.hexdigest() -        h = hashlib.md5() -        h.update(hp) -        h.update(digest) -        pwhash = h.hexdigest() - -        post.update({"action": method}) -        post.update({"username": user, "passwordmd5dig": pwhash, "digest": digest}) -        resp = req.load("http://api.hotfile.com/", post=post) -        req.close() -        return resp - -    def login(self, user, data, req): -        cj = self.getAccountCookies(user) -        cj.setCookie("hotfile.com", "lang", "en") -        req.load("http://hotfile.com/", cookies=True) -        page = req.load("http://hotfile.com/login.php", post={"returnto": "/", "user": user, "pass": data["password"]}, -                        cookies=True) - -        if "Bad username/password" in page: -            self.wrongPassword() diff --git a/module/plugins/accounts/Http.py b/module/plugins/accounts/Http.py index e2f236e41..07e46eb07 100644 --- a/module/plugins/accounts/Http.py +++ b/module/plugins/accounts/Http.py @@ -4,11 +4,14 @@ from module.plugins.Account import Account  class Http(Account): -    __name__ = "Http" +    __name__    = "Http" +    __type__    = "account"      __version__ = "0.01" -    __type__ = "account" +      __description__ = """Http dummy account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    login_timeout = info_threshold = 1000000 +    info_threshold = 1000000 +    login_timeout = 1000000 diff --git a/module/plugins/accounts/HugefilesNet.py b/module/plugins/accounts/HugefilesNet.py new file mode 100644 index 000000000..5da3bbc37 --- /dev/null +++ b/module/plugins/accounts/HugefilesNet.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from module.plugins.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/module/plugins/accounts/HundredEightyUploadCom.py b/module/plugins/accounts/HundredEightyUploadCom.py new file mode 100644 index 000000000..39f91a8af --- /dev/null +++ b/module/plugins/accounts/HundredEightyUploadCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.XFSAccount import XFSAccount + + +class HundredEightyUploadCom(XFSAccount): +    __name__    = "HundredEightyUploadCom" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """180upload.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "180upload.com" diff --git a/module/plugins/accounts/JunocloudMe.py b/module/plugins/accounts/JunocloudMe.py new file mode 100644 index 000000000..b0fc160f3 --- /dev/null +++ b/module/plugins/accounts/JunocloudMe.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from module.plugins.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/module/plugins/accounts/Keep2shareCc.py b/module/plugins/accounts/Keep2shareCc.py new file mode 100644 index 000000000..fac3cc4a6 --- /dev/null +++ b/module/plugins/accounts/Keep2shareCc.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- + +import re + +from time import gmtime, mktime, strptime + +from module.plugins.Account import Account + + +class Keep2shareCc(Account): +    __name__    = "Keep2shareCc" +    __type__    = "account" +    __version__ = "0.03" + +    __description__ = """Keep2share.cc account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("aeronaut", "aeronaut@pianoguy.de")] + + +    VALID_UNTIL_PATTERN  = r'Premium expires: <b>(.+?)</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 = None +        premium     = None + +        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) + +            try: +                validuntil = mktime(strptime(expiredate, "%Y.%m.%d")) + +            except Exception, e: +                self.logError(e) + +            else: +                if validuntil > mktime(gmtime()): +                    premium = True +                else: +                    premium = False +                    validuntil = None + +        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'                  : ""}) + +        if re.search(self.LOGIN_FAIL_PATTERN, html): +            self.wrongPassword() diff --git a/module/plugins/accounts/LetitbitNet.py b/module/plugins/accounts/LetitbitNet.py index e37c860a6..b8244a06d 100644 --- a/module/plugins/accounts/LetitbitNet.py +++ b/module/plugins/accounts/LetitbitNet.py @@ -1,37 +1,25 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################  from module.plugins.Account import Account  # from module.common.json_layer import json_loads, json_dumps  class LetitbitNet(Account): -    __name__ = "LetitbitNet" +    __name__    = "LetitbitNet" +    __type__    = "account"      __version__ = "0.01" -    __type__ = "account" +      __description__ = """Letitbit.net account plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __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.accounts[user]['password'] -        # json_data = [api_key, ["key/info"]] +        # 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) +        # self.logDebug("API Key Info: " + api_rep)          # api_rep = json_loads(api_rep)          #          # if api_rep['status'] == 'FAIL': @@ -40,6 +28,7 @@ class LetitbitNet(Account):          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.') +        self.logInfo(_("You must use your API KEY as username and the PREMIUM KEY as password")) diff --git a/module/plugins/accounts/LinestorageCom.py b/module/plugins/accounts/LinestorageCom.py new file mode 100644 index 000000000..a48d5beb9 --- /dev/null +++ b/module/plugins/accounts/LinestorageCom.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from module.plugins.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/module/plugins/accounts/LinksnappyCom.py b/module/plugins/accounts/LinksnappyCom.py index 63b9576d6..19986157b 100644 --- a/module/plugins/accounts/LinksnappyCom.py +++ b/module/plugins/accounts/LinksnappyCom.py @@ -7,12 +7,14 @@ from module.common.json_layer import json_loads  class LinksnappyCom(Account): -    __name__ = "LinksnappyCom" -    __version__ = "0.02" -    __type__ = "account" +    __name__    = "LinksnappyCom" +    __type__    = "account" +    __version__ = "0.03" +      __description__ = """Linksnappy.com account plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] +      def loadAccountInfo(self, user, req):          data = self.getAccountData(user) @@ -35,10 +37,11 @@ class LinksnappyCom(Account):          if 'trafficleft' not in j['return'] or isinstance(j['return']['trafficleft'], str):              trafficleft = -1          else: -            trafficleft = int(j['return']['trafficleft']) * 1024 +            trafficleft = self.parseTraffic(float(j['return']['trafficleft'] + "MB")          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()}) diff --git a/module/plugins/accounts/MegaDebridEu.py b/module/plugins/accounts/MegaDebridEu.py index 1fbe00ff7..a082b97af 100644 --- a/module/plugins/accounts/MegaDebridEu.py +++ b/module/plugins/accounts/MegaDebridEu.py @@ -1,49 +1,39 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################  from module.plugins.Account import Account  from module.common.json_layer import json_loads  class MegaDebridEu(Account): -    __name__ = "MegaDebridEu" -    __version__ = "0.2" -    __type__ = "account" +    __name__    = "MegaDebridEu" +    __type__    = "account" +    __version__ = "0.20" +      __description__ = """mega-debrid.eu account plugin""" -    __author_name__ = "D.Ducatel" -    __author_mail__ = "dducatel@je-geek.fr" +    __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"]}) -        response = json_loads(jsonResponse) +                                get={'action': 'connectUser', 'login': user, 'password': data['password']}) +        res = json_loads(jsonResponse) -        if response["response_code"] == "ok": -            return {"premium": True, "validuntil": float(response["vip_end"]), "status": True} +        if res['response_code'] == "ok": +            return {"premium": True, "validuntil": float(res['vip_end']), "status": True}          else: -            self.logError(response) +            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"]}) -        response = json_loads(jsonResponse) -        if response["response_code"] != "ok": +                                get={'action': 'connectUser', 'login': user, 'password': data['password']}) +        res = json_loads(jsonResponse) +        if res['response_code'] != "ok":              self.wrongPassword() diff --git a/module/plugins/accounts/MegaRapidCz.py b/module/plugins/accounts/MegaRapidCz.py new file mode 100644 index 000000000..41da7ac73 --- /dev/null +++ b/module/plugins/accounts/MegaRapidCz.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +import re + +from time import mktime, strptime +from module.plugins.Account import Account + + +class MegaRapidCz(Account): +    __name__    = "MegaRapidCz" +    __type__    = "account" +    __version__ = "0.34" + +    __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): +        html = req.load("http://megarapid.cz/mujucet/", decode=True) + +        m = re.search(self.LIMITDL_PATTERN, html) +        if m: +            data = self.getAccountData(user) +            data['options']['limitDL'] = [int(m.group(1))] + +        m = re.search(self.VALID_UNTIL_PATTERN, html) +        if m: +            validuntil = mktime(strptime(m.group(1), "%d.%m.%Y - %H:%M")) +            return {"premium": True, "trafficleft": -1, "validuntil": validuntil} + +        m = re.search(self.TRAFFIC_LEFT_PATTERN, html) +        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): +        htm = req.load("http://megarapid.cz/prihlaseni/") +        if "Heslo:" in htm: +            start = htm.index('id="inp_hash" name="hash" value="') +            htm = htm[start + 33:] +            hashes = htm[0:32] +            htm = req.load("http://megarapid.cz/prihlaseni/", +                           post={"hash": hashes, +                                 "login": user, +                                 "pass1": data['password'], +                                 "remember": 0, +                                 "sbmt": u"PÅihlásit"}) diff --git a/module/plugins/accounts/MegasharesCom.py b/module/plugins/accounts/MegasharesCom.py index 59aefe374..6e0a4358e 100644 --- a/module/plugins/accounts/MegasharesCom.py +++ b/module/plugins/accounts/MegasharesCom.py @@ -7,15 +7,18 @@ from module.plugins.Account import Account  class MegasharesCom(Account): -    __name__ = "MegasharesCom" +    __name__    = "MegasharesCom" +    __type__    = "account"      __version__ = "0.02" -    __type__ = "account" +      __description__ = """Megashares.com account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __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) @@ -32,6 +35,7 @@ class MegasharesCom(Account):          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": "", diff --git a/module/plugins/accounts/MovReelCom.py b/module/plugins/accounts/MovReelCom.py index 2225261f0..6128cddc8 100644 --- a/module/plugins/accounts/MovReelCom.py +++ b/module/plugins/accounts/MovReelCom.py @@ -1,20 +1,19 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.XFSPAccount import XFSPAccount +from module.plugins.internal.XFSAccount import XFSAccount -class MovReelCom(XFSPAccount): -    __name__ = "MovReelCom" -    __version__ = "0.01" -    __type__ = "account" +class MovReelCom(XFSAccount): +    __name__    = "MovReelCom" +    __type__    = "account" +    __version__ = "0.03" +      __description__ = """Movreel.com account plugin""" -    __author_name__ = "t4skforce" -    __author_mail__ = "t4skforce1337[AT]gmail[DOT]com" +    __license__     = "GPLv3" +    __authors__     = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")] -    login_timeout = 60  #: after that time [in minutes] pyload will relogin the account -    info_threshold = 30  #: account data will be reloaded after this time -    MAIN_PAGE = "http://movreel.com/" +    login_timeout = 60 +    info_threshold = 30 -    TRAFFIC_LEFT_PATTERN = r'Traffic.*?<b>([^<]+)</b>' -    LOGIN_FAIL_PATTERN = r'<b[^>]*>Incorrect Login or Password</b><br>' +    HOSTER_DOMAIN = "movreel.com" diff --git a/module/plugins/accounts/MultiDebridCom.py b/module/plugins/accounts/MultiDebridCom.py deleted file mode 100644 index 704b4ac78..000000000 --- a/module/plugins/accounts/MultiDebridCom.py +++ /dev/null @@ -1,46 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -from time import time - -from module.plugins.Account import Account -from module.common.json_layer import json_loads - - -class MultiDebridCom(Account): -    __name__ = "MultiDebridCom" -    __version__ = "0.01" -    __type__ = "account" -    __description__ = """Multi-debrid.com account plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" - -    def loadAccountInfo(self, user, req): -        if 'days_left' in self.json_data: -            validuntil = int(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://multi-debrid.com/myaccount -        self.html = req.load("http://multi-debrid.com/api.php", -                             get={"user": user, "pass": data["password"]}) -        self.logDebug('JSON data: ' + self.html) -        self.json_data = json_loads(self.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/module/plugins/accounts/MultihostersCom.py b/module/plugins/accounts/MultihostersCom.py new file mode 100644 index 000000000..3f96fdf41 --- /dev/null +++ b/module/plugins/accounts/MultihostersCom.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- + +from time import mktime, strptime +from module.plugins.Account import Account + +class MultihostersCom(Account): +    __name__ = "MultihostersCom" +    __version__ = "0.01" +    __type__ = "account" +    __description__ = """Multihosters.com account plugin""" +    __author_name__ = "tjeh" +    __author_mail__ = "tjeh@gmx.net" + +    def loadAccountInfo(self, user, req): +        data = self.getAPIData(req) +        if data == "No traffic": +            account_info = {"trafficleft": 0, "validuntil": 0, "premium": False} +        else: +            account_info = { +                "trafficleft": int(data['availabletodaytraffic']) * 1024, +                "validuntil": mktime(strptime(data['endsubscriptiondate'], "%Y/%m/%d %H:%M:%S")), +                "premium": True +            } +        return account_info + +    def login(self, user, data, req): +        self.loginname = user +        self.password = data['password'] +        if self.getAPIData(req) == "No traffic": +            self.wrongPassword() + +    def getAPIData(self, req, just_header=False, **kwargs): +        get_data = { +            'cmd': 'accountinfo', +            'login': self.loginname, +            'pass': self.password +        } +        get_data.update(kwargs) + +        response = req.load("http://www.multihosters.com/jDownloader.ashx", get=get_data, +                            decode=True, just_header=just_header) +        self.logDebug(response) + +        if ':' in response: +            if not just_header: +                response = response.replace(',', '\n') +            return dict((y.strip().lower(), z.strip()) for (y, z) in +                        [x.split(':', 1) for x in response.splitlines() if ':' in x]) +        else: +            return response diff --git a/module/plugins/accounts/MultishareCz.py b/module/plugins/accounts/MultishareCz.py index cd2fd575c..878413007 100644 --- a/module/plugins/accounts/MultishareCz.py +++ b/module/plugins/accounts/MultishareCz.py @@ -1,46 +1,30 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" +import re  from module.plugins.Account import Account -#from time import mktime, strptime -#from pycurl import REFERER -import re -from module.utils import parseFileSize  class MultishareCz(Account): -    __name__ = "MultishareCz" -    __version__ = "0.02" -    __type__ = "account" +    __name__    = "MultishareCz" +    __type__    = "account" +    __version__ = "0.04" +      __description__ = """Multishare.cz account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    TRAFFIC_LEFT_PATTERN = r'<span class="profil-zvyrazneni">Kredit:</span>\s*<strong>(?P<S>[0-9,]+) (?P<U>\w+)</strong>' +    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) -        found = re.search(self.TRAFFIC_LEFT_PATTERN, html) -        trafficleft = parseFileSize(found.group('S'), found.group('U')) / 1024 if found else 0 +        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) @@ -48,6 +32,7 @@ class MultishareCz(Account):          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", diff --git a/module/plugins/accounts/MyfastfileCom.py b/module/plugins/accounts/MyfastfileCom.py new file mode 100644 index 000000000..36923470e --- /dev/null +++ b/module/plugins/accounts/MyfastfileCom.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +from time import time + +from module.common.json_layer import json_loads +from module.plugins.Account import Account + + +class MyfastfileCom(Account): +    __name__    = "MyfastfileCom" +    __type__    = "account" +    __version__ = "0.03" + +    __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() + 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/module/plugins/accounts/NetloadIn.py b/module/plugins/accounts/NetloadIn.py index 3d2b52470..15bad6966 100755 --- a/module/plugins/accounts/NetloadIn.py +++ b/module/plugins/accounts/NetloadIn.py @@ -1,22 +1,5 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay -""" -  import re  from time import time @@ -24,17 +7,20 @@ from module.plugins.Account import Account  class NetloadIn(Account): -    __name__ = "NetloadIn" +    __name__    = "NetloadIn" +    __type__    = "account"      __version__ = "0.22" -    __type__ = "account" +      __description__ = """Netload.in account plugin""" -    __author_name__ = ("RaNaN", "CryNickSystems") -    __author_mail__ = ("RaNaN@pyload.org", "webmaster@pcProfil.de") +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org"), +                       ("CryNickSystems", "webmaster@pcProfil.de")] +      def loadAccountInfo(self, user, req): -        page = req.load("http://netload.in/index.php?id=2&lang=de") -        left = r">(\d+) (Tag|Tage), (\d+) Stunden<" -        left = re.search(left, page) +        html = req.load("http://netload.in/index.php", get={'id': 2, 'lang': "de"}) +        left = r'>(\d+) (Tag|Tage), (\d+) Stunden<' +        left = re.search(left, html)          if left:              validuntil = time() + int(left.group(1)) * 24 * 60 * 60 + int(left.group(3)) * 60 * 60              trafficleft = -1 @@ -45,9 +31,10 @@ class NetloadIn(Account):              trafficleft = None          return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium} +      def login(self, user, data, req): -        page = req.load("http://netload.in/index.php", None, +        html = req.load("http://netload.in/index.php", None,                          {"txtuser": user, "txtpass": data['password'], "txtcheck": "login", "txtlogin": "Login"},                          cookies=True) -        if "password or it might be invalid!" in page: +        if "password or it might be invalid!" in html:              self.wrongPassword() diff --git a/module/plugins/accounts/NoPremiumPl.py b/module/plugins/accounts/NoPremiumPl.py new file mode 100644 index 000000000..f2223b7d9 --- /dev/null +++ b/module/plugins/accounts/NoPremiumPl.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- + +from datetime import datetime +import hashlib + +from module.plugins.Account import Account +from time import mktime +from module.common.json_layer import json_loads as 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 = loads(self.runAuthQuery()) +        except: +            # 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 = mktime(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 = loads(self.runAuthQuery()) +        except: +            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/module/plugins/accounts/NosuploadCom.py b/module/plugins/accounts/NosuploadCom.py new file mode 100644 index 000000000..e523ee2f4 --- /dev/null +++ b/module/plugins/accounts/NosuploadCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from module.plugins.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/module/plugins/accounts/NovafileCom.py b/module/plugins/accounts/NovafileCom.py new file mode 100644 index 000000000..ab61bf0fc --- /dev/null +++ b/module/plugins/accounts/NovafileCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from module.plugins.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/module/plugins/accounts/NowVideoSx.py b/module/plugins/accounts/NowVideoSx.py new file mode 100644 index 000000000..e2dcaba12 --- /dev/null +++ b/module/plugins/accounts/NowVideoSx.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +import re + +from time import gmtime, mktime, strptime + +from module.plugins.Account import Account + + +class NowVideoSx(Account): +    __name__    = "NowVideoSx" +    __type__    = "account" +    __version__ = "0.02" + +    __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 = mktime(strptime(expiredate, "%Y-%b-%d")) + +            except Exception, e: +                self.logError(e) + +            else: +                if validuntil > mktime(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']}) + +        if ">Invalid login details" is html: +            self.wrongPassword() diff --git a/module/plugins/accounts/OboomCom.py b/module/plugins/accounts/OboomCom.py index 19fcea67a..4d90e1b25 100644 --- a/module/plugins/accounts/OboomCom.py +++ b/module/plugins/accounts/OboomCom.py @@ -2,49 +2,61 @@  import time -from module.plugins.Account import Account -from module.lib.beaker.crypto.pbkdf2 import PBKDF2 +from beaker.crypto.pbkdf2 import PBKDF2 +  from module.common.json_layer import json_loads +from module.plugins.Account import Account  class OboomCom(Account): -    __name__ = "OboomCom" -    __version__ = "0.1" -    __type__ = "account" +    __name__    = "OboomCom" +    __type__    = "account" +    __version__ = "0.22" +      __description__ = """Oboom.com account plugin""" -    __author_name__ = "stanley" -    __author_mail__ = "stanley.foerster@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("stanley", "stanley.foerster@gmail.com")] +      def loadAccountData(self, user, req): -        passwd = self.getAccountData(user)["password"] +        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.0/login", get={"auth": user, "pass": pbkdf2})) +        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.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 "premium_unix" in userData: -            validUntilUtc = int(userData["premium_unix"]) -            if validUntilUtc > int(time.time()): -                premium = True -                validUntil = validUntilUtc -                traffic = userData["traffic"] -                trafficLeft = traffic["current"] -                maxTraffic = traffic["max"] -                session = accountData["session"] -                return {"premium": premium, -                        "validuntil": validUntil, -                        "trafficleft": trafficLeft / 1024, -                        "maxtraffic": maxTraffic / 1024, -                        "session": session -                } -        return {"premium": False, "validuntil": -1} + +        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/module/plugins/accounts/OneFichierCom.py b/module/plugins/accounts/OneFichierCom.py index b10e34314..2f1c914c1 100644 --- a/module/plugins/accounts/OneFichierCom.py +++ b/module/plugins/accounts/OneFichierCom.py @@ -1,46 +1,55 @@  # -*- coding: utf-8 -*-  import re +  from time import strptime, mktime +  from pycurl import REFERER  from module.plugins.Account import Account  class OneFichierCom(Account): -    __name__ = "OneFichierCom" -    __version__ = "0.1" -    __type__ = "account" +    __name__    = "OneFichierCom" +    __type__    = "account" +    __version__ = "0.11" +      __description__ = """1fichier.com account plugin""" -    __author_name__ = ("Elrick69") -    __author_mail__ = ("elrick69[AT]rocketmail[DOT]com") +    __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+)' -    VALID_UNTIL_PATTERN = r'You are a premium user until (?P<d>\d{2})/(?P<m>\d{2})/(?P<y>\d{4})'      def loadAccountInfo(self, user, req): +        validuntil = None +        trafficleft = -1 +        premium = None -        html = req.load("http://1fichier.com/console/abo.pl") +        html = req.load("https://1fichier.com/console/abo.pl")          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 = int(mktime(strptime(validuntil, "%d/%m/%Y"))) -        else: -            premium = False -            validuntil = -1 +            expiredate = m.group(1) +            self.logDebug("Expire date: " + expiredate) -        return {"premium": premium, "trafficleft": -1, "validuntil": validuntil} +            try: +                validuntil = mktime(strptime(expiredate, "%d/%m/%Y")) +            except Exception, e: +                self.logError(e) +            else: +                premium = True -    def login(self, user, data, req): +        return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium or False} -        req.http.c.setopt(REFERER, "http://1fichier.com/login.pl?lg=en") -        html = req.load("http://1fichier.com/login.pl?lg=en", post={ -            "mail": user, -            "pass": data["password"], -            "Login": "Login"}) +    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"}) -        if r'<div class="error_message">Invalid username or password.</div>' in html: +        if '>Invalid email address' in html or '>Invalid password' in html:              self.wrongPassword() diff --git a/module/plugins/accounts/OverLoadMe.py b/module/plugins/accounts/OverLoadMe.py index eab20480f..fb9732986 100644 --- a/module/plugins/accounts/OverLoadMe.py +++ b/module/plugins/accounts/OverLoadMe.py @@ -5,29 +5,32 @@ from module.common.json_layer import json_loads  class OverLoadMe(Account): -    __name__ = "OverLoadMe" +    __name__    = "OverLoadMe" +    __type__    = "account"      __version__ = "0.01" -    __type__ = "account" +      __description__ = """Over-Load.me account plugin""" -    __author_name__ = "marley" -    __author_mail__ = "marley@over-load.me" +    __license__     = "GPLv3" +    __authors__     = [("marley", "marley@over-load.me")] +      def loadAccountInfo(self, user, req):          data = self.getAccountData(user) -        page = req.load("https://api.over-load.me/account.php", get={"user": user, "auth": data["password"]}).strip() -        data = json_loads(page) +        html = req.load("https://api.over-load.me/account.php", get={"user": user, "auth": data['password']}).strip() +        data = json_loads(html)          # Check for premium -        if data["membership"] == "Free": +        if data['membership'] == "Free":              return {"premium": False} -        account_info = {"validuntil": data["expirationunix"], "trafficleft": -1} +        account_info = {"validuntil": data['expirationunix'], "trafficleft": -1}          return account_info +      def login(self, user, data, req):          jsondata = req.load("https://api.over-load.me/account.php", -                            get={"user": user, "auth": data["password"]}).strip() +                            get={"user": user, "auth": data['password']}).strip()          data = json_loads(jsondata) -        if data["err"] == 1: +        if data['err'] == 1:              self.wrongPassword() diff --git a/module/plugins/accounts/Premium4Me.py b/module/plugins/accounts/Premium4Me.py deleted file mode 100644 index c80f40f5c..000000000 --- a/module/plugins/accounts/Premium4Me.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- - -from module.plugins.Account import Account - - -class Premium4Me(Account): -    __name__ = "Premium4Me" -    __version__ = "0.03" -    __type__ = "account" -    __description__ = """Premium.to account plugin""" -    __author_name__ = ("RaNaN", "zoidberg", "stickell") -    __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz", "l.stickell@yahoo.it") - -    def loadAccountInfo(self, user, req): -        traffic = req.load("http://premium.to/api/traffic.php?authcode=%s" % self.authcode) - -        account_info = {"trafficleft": int(traffic) / 1024, -                        "validuntil": -1} - -        return account_info - -    def login(self, user, data, req): -        self.authcode = req.load("http://premium.to/api/getauthcode.php?username=%s&password=%s" % ( -                                 user, data["password"])).strip() - -        if "wrong username" in self.authcode: -            self.wrongPassword() diff --git a/module/plugins/accounts/PremiumTo.py b/module/plugins/accounts/PremiumTo.py new file mode 100644 index 000000000..ef3d0cc19 --- /dev/null +++ b/module/plugins/accounts/PremiumTo.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +from module.plugins.Account import Account + + +class PremiumTo(Account): +    __name__    = "PremiumTo" +    __type__    = "account" +    __version__ = "0.05" + +    __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): +        api_r = req.load("http://premium.to/api/straffic.php", +                         get={'username': self.username, 'password': self.password}) + +        trafficleft = sum(map(float, api_r.split(';'))) / 1024  #@TODO: Remove `/ 1024` in 0.4.10 + +        return {'premium': True, 'trafficleft': trafficleft, 'validuntil': -1} + + +    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}).strip() + +        if "wrong username" in authcode: +            self.wrongPassword() diff --git a/module/plugins/accounts/PremiumizeMe.py b/module/plugins/accounts/PremiumizeMe.py index 1da5d9002..c1abde309 100644 --- a/module/plugins/accounts/PremiumizeMe.py +++ b/module/plugins/accounts/PremiumizeMe.py @@ -6,12 +6,14 @@ from module.common.json_layer import json_loads  class PremiumizeMe(Account): -    __name__ = "PremiumizeMe" -    __version__ = "0.11" -    __type__ = "account" +    __name__    = "PremiumizeMe" +    __type__    = "account" +    __version__ = "0.12" +      __description__ = """Premiumize.me account plugin""" -    __author_name__ = "Florian Franzen" -    __author_mail__ = "FlorianFranzen@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("Florian Franzen", "FlorianFranzen@gmail.com")] +      def loadAccountInfo(self, user, req):          # Get user data from premiumize.me @@ -20,13 +22,14 @@ class PremiumizeMe(Account):          # Parse account info          account_info = {"validuntil": float(status['result']['expires']), -                        "trafficleft": max(0, status['result']['trafficleft_bytes'] / 1024)} +                        "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) @@ -35,10 +38,12 @@ class PremiumizeMe(Account):          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?method=accountstatus¶ms[login]=%s¶ms[pass]=%s" % ( -            user, self.accounts[user]['password'])) +        answer = req.load("https://api.premiumize.me/pm-api/v1.php", +                           get={'method'       : "accountstatus", +                                'params[login]': user, +                                'params[pass]' : self.accounts[user]['password']})          return json_loads(answer) diff --git a/module/plugins/accounts/QuickshareCz.py b/module/plugins/accounts/QuickshareCz.py index 5a69a7f66..18af5f736 100644 --- a/module/plugins/accounts/QuickshareCz.py +++ b/module/plugins/accounts/QuickshareCz.py @@ -1,41 +1,29 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re +  from module.plugins.Account import Account -from module.utils import parseFileSize  class QuickshareCz(Account): -    __name__ = "QuickshareCz" -    __version__ = "0.01" -    __type__ = "account" +    __name__    = "QuickshareCz" +    __type__    = "account" +    __version__ = "0.02" +      __description__ = """Quickshare.cz account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __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) -        found = re.search(r'Stav kreditu: <strong>(.+?)</strong>', html) -        if found: -            trafficleft = parseFileSize(found.group(1)) / 1024 +        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 @@ -43,6 +31,7 @@ class QuickshareCz(Account):          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', diff --git a/module/plugins/accounts/RPNetBiz.py b/module/plugins/accounts/RPNetBiz.py index 49a294d14..813453c03 100644 --- a/module/plugins/accounts/RPNetBiz.py +++ b/module/plugins/accounts/RPNetBiz.py @@ -5,20 +5,22 @@ from module.common.json_layer import json_loads  class RPNetBiz(Account): -    __name__ = "RPNetBiz" -    __version__ = "0.1" -    __type__ = "account" +    __name__    = "RPNetBiz" +    __type__    = "account" +    __version__ = "0.11" +      __description__ = """RPNet.biz account plugin""" -    __author_name__ = "Dman" -    __author_mail__ = "dmanugm@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("Dman", "dmanugm@gmail.com")] +      def loadAccountInfo(self, user, req):          # Get account information from rpnet.biz -        response = self.getAccountStatus(user, req) +        res = self.getAccountStatus(user, req)          try: -            if response['accountInfo']['isPremium']: +            if res['accountInfo']['isPremium']:                  # Parse account info. Change the trafficleft later to support per host info. -                account_info = {"validuntil": int(response['accountInfo']['premiumExpiry']), +                account_info = {"validuntil": float(res['accountInfo']['premiumExpiry']),                                  "trafficleft": -1, "premium": True}              else:                  account_info = {"validuntil": None, "trafficleft": None, "premium": False} @@ -29,19 +31,21 @@ class RPNetBiz(Account):          return account_info +      def login(self, user, data, req):          # Get account information from rpnet.biz -        response = self.getAccountStatus(user, req) +        res = self.getAccountStatus(user, req) -        # If we have an error in the response, we have wrong login information -        if 'error' in response: +        # 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 -        response = req.load("https://premium.rpnet.biz/client_api.php", +        res = req.load("https://premium.rpnet.biz/client_api.php",                              get={"username": user, "password": self.accounts[user]['password'],                                   "action": "showAccountInformation"}) -        self.logDebug("JSON data: %s" % response) +        self.logDebug("JSON data: %s" % res) -        return json_loads(response) +        return json_loads(res) diff --git a/module/plugins/accounts/RapideoPl.py b/module/plugins/accounts/RapideoPl.py new file mode 100644 index 000000000..438ce7ad3 --- /dev/null +++ b/module/plugins/accounts/RapideoPl.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- + +from datetime import datetime +import hashlib + +from module.plugins.Account import Account +from time import mktime +from module.common.json_layer import json_loads as 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 = loads(self.runAuthQuery()) +        except: +            # 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 = mktime(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 = loads(self.runAuthQuery()) +        except: +            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/module/plugins/accounts/RapidfileshareNet.py b/module/plugins/accounts/RapidfileshareNet.py new file mode 100644 index 000000000..c0dd7eaee --- /dev/null +++ b/module/plugins/accounts/RapidfileshareNet.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.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/module/plugins/accounts/RapidgatorNet.py b/module/plugins/accounts/RapidgatorNet.py index c008a0db3..2899d5a68 100644 --- a/module/plugins/accounts/RapidgatorNet.py +++ b/module/plugins/accounts/RapidgatorNet.py @@ -1,36 +1,22 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  from module.plugins.Account import Account  from module.common.json_layer import json_loads  class RapidgatorNet(Account): -    __name__ = "RapidgatorNet" -    __version__ = "0.04" -    __type__ = "account" +    __name__    = "RapidgatorNet" +    __type__    = "account" +    __version__ = "0.05" +      __description__ = """Rapidgator.net account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] +      API_URL = 'http://rapidgator.net/api/user' +      def loadAccountInfo(self, user, req):          try:              sid = self.getAccountData(user).get('SID') @@ -45,7 +31,7 @@ class RapidgatorNet(Account):                      self.scheduleRefresh(user, json['response']['reset_in'])                  return {"validuntil": json['response']['expire_date'], -                        "trafficleft": int(json['response']['traffic_left']) / 1024, +                        "trafficleft": float(json['response']['traffic_left']) / 1024,  #@TODO: Remove `/ 1024` in 0.4.10                          "premium": True}              else:                  self.logError(json['response_details']) @@ -54,6 +40,7 @@ class RapidgatorNet(Account):          return {"validuntil": None, "trafficleft": None, "premium": False} +      def login(self, user, data, req):          try:              json = req.load('%s/login' % self.API_URL, post={"username": user, "password": data['password']}) diff --git a/module/plugins/accounts/RapidshareCom.py b/module/plugins/accounts/RapidshareCom.py deleted file mode 100644 index 17d7f0e08..000000000 --- a/module/plugins/accounts/RapidshareCom.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay -""" - -from module.plugins.Account import Account - - -class RapidshareCom(Account): -    __name__ = "RapidshareCom" -    __version__ = "0.22" -    __type__ = "account" -    __description__ = """Rapidshare.com account plugin""" -    __author_name__ = "mkaay" -    __author_mail__ = "mkaay@mkaay.de" - -    def loadAccountInfo(self, user, req): -        data = self.getAccountData(user) -        api_url_base = "http://api.rapidshare.com/cgi-bin/rsapi.cgi" -        api_param_prem = {"sub": "getaccountdetails", "type": "prem", "login": user, -                          "password": data["password"], "withcookie": 1} -        src = req.load(api_url_base, cookies=False, get=api_param_prem) -        if src.startswith("ERROR"): -            raise Exception(src) -        fields = src.split("\n") -        info = {} -        for t in fields: -            if not t.strip(): -                continue -            k, v = t.split("=") -            info[k] = v - -        validuntil = int(info["billeduntil"]) -        premium = True if validuntil else False - -        tmp = {"premium": premium, "validuntil": validuntil, "trafficleft": -1, "maxtraffic": -1} - -        return tmp - -    def login(self, user, data, req): -        api_url_base = "http://api.rapidshare.com/cgi-bin/rsapi.cgi" -        api_param_prem = {"sub": "getaccountdetails", "type": "prem", "login": user, -                          "password": data["password"], "withcookie": 1} -        src = req.load(api_url_base, cookies=False, get=api_param_prem) -        if src.startswith("ERROR"): -            raise Exception(src + "### Note you have to use your account number for login, instead of name.") -        fields = src.split("\n") -        info = {} -        for t in fields: -            if not t.strip(): -                continue -            k, v = t.split("=") -            info[k] = v -        cj = self.getAccountCookies(user) -        cj.setCookie("rapidshare.com", "enc", info["cookie"]) diff --git a/module/plugins/accounts/RapiduNet.py b/module/plugins/accounts/RapiduNet.py new file mode 100644 index 000000000..fe465bc48 --- /dev/null +++ b/module/plugins/accounts/RapiduNet.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- + +import re +from module.plugins.Account import Account +from module.common.json_layer import json_loads + + +class RapiduNet(Account): +    __name__    = "RapiduNet" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """Rapidu.net account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("prOq", None)] + + +    PREMIUM_PATTERN = r'<a href="premium/" style="padding-left: 0px;">Account: <b>Premium</b></a>' + + +    def loadAccountInfo(self, user, req): +        info = {'validuntil': None, 'trafficleft': None, 'premium': False} + +        req.load("https://rapidu.net/ajax.php", get={'a': "getChangeLang"}, post={"_go": "", "lang": "en"}) +        html = req.load("https://rapidu.net/", decode=True) + +        if re.search(self.PREMIUM_PATTERN, html): +            info['premium'] = True + +        return info + + +    def login(self, user, data, req): +        try: +            json = json_loads(req.load("https://rapidu.net/ajax.php?a=getUserLogin", +                                       post={'_go': "", +                                             'login': user, +                                             'pass': data['password'], +                                             'member': "1"})) + +            self.logDebug(json) + +            if not json['message'] == "success": +                self.wrongPassword() + +        except Exception, e: +            self.logError(e) diff --git a/module/plugins/accounts/RarefileNet.py b/module/plugins/accounts/RarefileNet.py index 494941aef..577a6c8f6 100644 --- a/module/plugins/accounts/RarefileNet.py +++ b/module/plugins/accounts/RarefileNet.py @@ -1,14 +1,16 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.XFSPAccount import XFSPAccount +from module.plugins.internal.XFSAccount import XFSAccount -class RarefileNet(XFSPAccount): -    __name__ = "RarefileNet" -    __version__ = "0.02" -    __type__ = "account" +class RarefileNet(XFSAccount): +    __name__    = "RarefileNet" +    __type__    = "account" +    __version__ = "0.04" +      __description__ = """RareFile.net account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    MAIN_PAGE = "http://rarefile.net/" +    HOSTER_DOMAIN = "rarefile.net" diff --git a/module/plugins/accounts/RealdebridCom.py b/module/plugins/accounts/RealdebridCom.py index 86ad18085..48b17df5f 100644 --- a/module/plugins/accounts/RealdebridCom.py +++ b/module/plugins/accounts/RealdebridCom.py @@ -6,28 +6,31 @@ from module.plugins.Account import Account  class RealdebridCom(Account): -    __name__ = "RealdebridCom" -    __version__ = "0.43" -    __type__ = "account" +    __name__    = "RealdebridCom" +    __type__    = "account" +    __version__ = "0.44" +      __description__ = """Real-Debrid.com account plugin""" -    __author_name__ = "Devirex Hazzard" -    __author_mail__ = "naibaf_11@yahoo.de" +    __license__     = "GPLv3" +    __authors__     = [("Devirex Hazzard", "naibaf_11@yahoo.de")] +      def loadAccountInfo(self, user, req):          if self.pin_code:              return {"premium": False} -        page = req.load("https://real-debrid.com/api/account.php") -        xml = dom.parseString(page) -        account_info = {"validuntil": int(xml.getElementsByTagName("expiration")[0].childNodes[0].nodeValue), +        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 -        page = req.load("https://real-debrid.com/ajax/login.php", get={"user": user, "pass": data["password"]}) -        if "Your login informations are incorrect" in page: +        html = req.load("https://real-debrid.com/ajax/login.php", get={"user": user, "pass": data['password']}) +        if "Your login informations are incorrect" in html:              self.wrongPassword() -        elif "PIN Code required" in page: -            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.') +        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/module/plugins/accounts/RehostTo.py b/module/plugins/accounts/RehostTo.py index c4aa85484..00a45dedd 100644 --- a/module/plugins/accounts/RehostTo.py +++ b/module/plugins/accounts/RehostTo.py @@ -4,32 +4,42 @@ from module.plugins.Account import Account  class RehostTo(Account): -    __name__ = "RehostTo" -    __version__ = "0.1" -    __type__ = "account" +    __name__    = "RehostTo" +    __type__    = "account" +    __version__ = "0.11" +      __description__ = """Rehost.to account plugin""" -    __author_name__ = "RaNaN" -    __author_mail__ = "RaNaN@pyload.org" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org")] +      def loadAccountInfo(self, user, req):          data = self.getAccountData(user) -        page = req.load("http://rehost.to/api.php?cmd=login&user=%s&pass=%s" % (user, data["password"])) -        data = [x.split("=") for x in page.split(",")] +        html = req.load("http://rehost.to/api.php", +                        get={'cmd': "login", 'user': user, 'pass': data['password']}) +        data = [x.split("=") for x in html.split(",")]          ses = data[0][1]          long_ses = data[1][1] -        page = req.load("http://rehost.to/api.php?cmd=get_premium_credits&long_ses=%s" % long_ses) -        traffic, valid = page.split(",") +        html = req.load("http://rehost.to/api.php", +                        get={'cmd': "get_premium_credits", 'long_ses': long_ses}) + +        traffic, valid = html.split(",") -        account_info = {"trafficleft": int(traffic) * 1024, -                        "validuntil": int(valid), -                        "long_ses": long_ses, -                        "ses": ses} +        trafficleft = self.parseTraffic(traffic + "MB") +        validuntil  = float(valid) + +        account_info = {"trafficleft": trafficleft, +                        "validuntil" : validuntil, +                        "long_ses"   : long_ses, +                        "ses"        : ses}          return account_info +      def login(self, user, data, req): -        page = req.load("http://rehost.to/api.php?cmd=login&user=%s&pass=%s" % (user, data["password"])) +        html = req.load("http://rehost.to/api.php", +                        get={'cmd': "login", 'user': user, 'pass': data['password']}) -        if "Login failed." in page: +        if "Login failed." in html:              self.wrongPassword() diff --git a/module/plugins/accounts/ReloadCc.py b/module/plugins/accounts/ReloadCc.py deleted file mode 100644 index af23d9936..000000000 --- a/module/plugins/accounts/ReloadCc.py +++ /dev/null @@ -1,75 +0,0 @@ -# -*- coding: utf-8 -*- - -from module.plugins.Account import Account - -from module.common.json_layer import json_loads - -from module.network.HTTPRequest import BadHeader - - -class ReloadCc(Account): -    __name__ = "ReloadCc" -    __version__ = "0.3" -    __type__ = "account" -    __description__ = """Reload.cc account plugin""" - -    __author_name__ = "Reload Team" -    __author_mail__ = "hello@reload.cc" - -    def loadAccountInfo(self, user, req): - -        # Get user data from reload.cc -        status = self.getAccountStatus(user, req) - -        # Parse account info -        account_info = {"validuntil": float(status['msg']['expires']), -                        "pwdhash": status['msg']['hash'], -                        "trafficleft": -1} - -        return account_info - -    def login(self, user, data, req): - -        # Get user data from reload.cc -        status = self.getAccountStatus(user, req) - -        if not status: -            raise Exception("There was an error upon logging in to Reload.cc!") - -        # Check if user and password are valid -        if status['status'] != "ok": -            self.wrongPassword() - -    def getAccountStatus(self, user, req): -        # Use reload.cc API v1 to retrieve account info and return the parsed json answer -        query_params = dict( -            via='pyload', -            v=1, -            get_traffic='true', -            user=user -        ) - -        try: -            query_params.update(dict(hash=self.infos[user]['pwdhash'])) -        except Exception: -            query_params.update(dict(pwd=self.accounts[user]['password'])) - -        try: -            answer = req.load("http://api.reload.cc/login", get=query_params) -        except BadHeader, e: -            if e.code == 400: -                raise Exception("There was an unknown error within the Reload.cc plugin.") -            elif e.code == 401: -                self.wrongPassword() -            elif e.code == 402: -                self.expired(user) -            elif e.code == 403: -                raise Exception("Your account is disabled. Please contact the Reload.cc support!") -            elif e.code == 409: -                self.empty(user) -            elif e.code == 503: -                self.logInfo("Reload.cc is currently in maintenance mode! Please check again later.") -                self.wrongPassword() -            return None - -        return json_loads(answer) diff --git a/module/plugins/accounts/RyushareCom.py b/module/plugins/accounts/RyushareCom.py index 6a15c4c82..ca476366b 100644 --- a/module/plugins/accounts/RyushareCom.py +++ b/module/plugins/accounts/RyushareCom.py @@ -1,21 +1,25 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.XFSPAccount import XFSPAccount +from module.plugins.internal.XFSAccount import XFSAccount -class RyushareCom(XFSPAccount): -    __name__ = "RyushareCom" -    __version__ = "0.03" -    __type__ = "account" +class RyushareCom(XFSAccount): +    __name__    = "RyushareCom" +    __type__    = "account" +    __version__ = "0.05" +      __description__ = """Ryushare.com account plugin""" -    __author_name__ = ("zoidberg", "trance4us") -    __author_mail__ = ("zoidberg@mujmail.cz", "") +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("trance4us", None)] + + +    HOSTER_DOMAIN = "ryushare.com" -    MAIN_PAGE = "http://ryushare.com/"      def login(self, user, data, req):          req.lastURL = "http://ryushare.com/login.python"          html = req.load("http://ryushare.com/login.python", -                        post={"login": user, "password": data["password"], "op": "login"}) +                        post={"login": user, "password": data['password'], "op": "login"})          if 'Incorrect Login or Password' in html or '>Error<' in html:              self.wrongPassword() diff --git a/module/plugins/accounts/SafesharingEu.py b/module/plugins/accounts/SafesharingEu.py new file mode 100644 index 000000000..2e58d33b3 --- /dev/null +++ b/module/plugins/accounts/SafesharingEu.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from module.plugins.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/module/plugins/accounts/SecureUploadEu.py b/module/plugins/accounts/SecureUploadEu.py new file mode 100644 index 000000000..b335c94da --- /dev/null +++ b/module/plugins/accounts/SecureUploadEu.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from module.plugins.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/module/plugins/accounts/SendmywayCom.py b/module/plugins/accounts/SendmywayCom.py new file mode 100644 index 000000000..4fcbe0b7a --- /dev/null +++ b/module/plugins/accounts/SendmywayCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from module.plugins.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/module/plugins/accounts/ShareRapidCom.py b/module/plugins/accounts/ShareRapidCom.py deleted file mode 100644 index 38150e5cf..000000000 --- a/module/plugins/accounts/ShareRapidCom.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- - -import re -from time import mktime, strptime -from module.plugins.Account import Account - - -class ShareRapidCom(Account): -    __name__ = "ShareRapidCom" -    __version__ = "0.33" -    __type__ = "account" -    __description__ = """ShareRapid account plugin""" -    __author_name__ = ("MikyWoW", "zoidberg") -    __author_mail__ = ("mikywow@seznam.cz", "zoidberg@mujmail.cz") - -    login_timeout = 60 - -    def loadAccountInfo(self, user, req): -        src = req.load("http://sharerapid.cz/mujucet/", decode=True) - -        found = re.search(ur'<td>Max. poÄet paralelnÃch stahovánÃ: </td><td>(\d+)', src) -        if found: -            data = self.getAccountData(user) -            data["options"]["limitDL"] = [int(found.group(1))] - -        found = re.search(ur'<td>Paušálnà stahovánà aktivnÃ. Vypršà </td><td><strong>(.*?)</strong>', src) -        if found: -            validuntil = mktime(strptime(found.group(1), "%d.%m.%Y - %H:%M")) -            return {"premium": True, "trafficleft": -1, "validuntil": validuntil} - -        found = re.search(r'<tr><td>Kredit</td><td>(.*?) GiB', src) -        if found: -            trafficleft = float(found.group(1)) * (1 << 20) -            return {"premium": True, "trafficleft": trafficleft, "validuntil": -1} - -        return {"premium": False, "trafficleft": None, "validuntil": None} - -    def login(self, user, data, req): -        htm = req.load("http://sharerapid.cz/prihlaseni/", cookies=True) -        if "Heslo:" in htm: -            start = htm.index('id="inp_hash" name="hash" value="') -            htm = htm[start + 33:] -            hashes = htm[0:32] -            htm = req.load("http://sharerapid.cz/prihlaseni/", -                           post={"hash": hashes, -                                 "login": user, -                                 "pass1": data["password"], -                                 "remember": 0, -                                 "sbmt": u"PÅihlásit"}, cookies=True) diff --git a/module/plugins/accounts/ShareonlineBiz.py b/module/plugins/accounts/ShareonlineBiz.py index f188fc580..57fe52385 100644 --- a/module/plugins/accounts/ShareonlineBiz.py +++ b/module/plugins/accounts/ShareonlineBiz.py @@ -1,57 +1,59 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay -""" +import re  from module.plugins.Account import Account  class ShareonlineBiz(Account): -    __name__ = "ShareonlineBiz" -    __version__ = "0.24" -    __type__ = "account" +    __name__    = "ShareonlineBiz" +    __type__    = "account" +    __version__ = "0.29" +      __description__ = """Share-online.biz account plugin""" -    __author_name__ = ("mkaay", "zoidberg") -    __author_mail__ = ("mkaay@mkaay.de", "zoidberg@mujmail.cz") +    __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.accounts[user]['password']}) -    def getUserAPI(self, user, req): -        return req.load("http://api.share-online.biz/account.php", -                        {"username": user, "password": self.accounts[user]["password"], "act": "userDetails"})      def loadAccountInfo(self, user, req): -        src = self.getUserAPI(user, req) +        premium     = False +        validuntil  = None +        trafficleft = -1 +        maxtraffic  = 100 * 1024 * 1024 * 1024  #: 100 GB -        info = {} -        for line in src.splitlines(): +        api = {} +        for line in self.api_response(user, req).splitlines():              if "=" in line:                  key, value = line.split("=") -                info[key] = value -        self.logDebug(info) +                api[key] = value + +        self.logDebug(api) + +        if api['a'].lower() != "not_available": +            req.cj.setCookie("share-online.biz", 'a', api['a']) + +            premium = api['group'] == "Premium" + +            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 -        if "dl" in info and info["dl"].lower() != "not_available": -            req.cj.setCookie("share-online.biz", "dl", info["dl"]) -        if "a" in info and info["a"].lower() != "not_available": -            req.cj.setCookie("share-online.biz", "a", info["a"]) +        return {'premium': premium, 'validuntil': validuntil, 'trafficleft': trafficleft, 'maxtraffic': maxtraffic} -        return {"validuntil": int(info["expire_date"]) if "expire_date" in info else -1, -                "trafficleft": -1, -                "premium": True if ("dl" in info or "a" in info) and (info["group"] != "Sammler") else False}      def login(self, user, data, req): -        src = self.getUserAPI(user, req) -        if "EXCEPTION" in src: +        html = self.api_response(user, req) +        err  = re.search(r'\*\*(.+?)\*\*', html) +        if err: +            self.logError(err.group(1))              self.wrongPassword() diff --git a/module/plugins/accounts/SimplyPremiumCom.py b/module/plugins/accounts/SimplyPremiumCom.py index 1e6d66806..465757457 100644 --- a/module/plugins/accounts/SimplyPremiumCom.py +++ b/module/plugins/accounts/SimplyPremiumCom.py @@ -1,33 +1,23 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -from module.plugins.Account import Account  from module.common.json_layer import json_loads +from module.plugins.Account import Account  class SimplyPremiumCom(Account): -    __name__ = "SimplyPremiumCom" -    __version__ = "0.01" -    __type__ = "account" -    __description__ = """Simply-Premium.Com account plugin""" -    __author_name__ = ("EvolutionClip") -    __author_mail__ = ("evolutionclip@live.de") +    __name__    = "SimplyPremiumCom" +    __type__    = "account" +    __version__ = "0.02" + +    __description__ = """Simply-Premium.com account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("EvolutionClip", "evolutionclip@live.de")] +      def loadAccountInfo(self, user, req): +        validuntil  = -1 +        trafficleft = None +          json_data = req.load('http://www.simply-premium.com/api/user.php?format=json')          self.logDebug("JSON data: " + json_data)          json_data = json_loads(json_data) @@ -35,14 +25,14 @@ class SimplyPremiumCom(Account):          if 'vip' in json_data['result'] and json_data['result']['vip'] == 0:              return {"premium": False} -        #Time package -        validuntil = float(json_data['result']['timeend']) -        #Traffic package -        # {"trafficleft": int(traffic) / 1024, "validuntil": -1} -        #trafficleft = int(json_data['result']['traffic'] / 1024) +        if 'timeend' in json_data['result'] and json_data['result']['timeend']: +            validuntil = float(json_data['result']['timeend']) + +        if 'traffic' in json_data['result'] and json_data['result']['traffic']: +            trafficleft = float(json_data['result']['traffic']) / 1024  #@TODO: Remove `/ 1024` in 0.4.10 + +        return {"premium": True, "validuntil": validuntil, "trafficleft": trafficleft} -        #return {"premium": True, "validuntil": validuntil, "trafficleft": trafficleft} -        return {"premium": True, "validuntil": validuntil}      def login(self, user, data, req):          req.cj.setCookie("simply-premium.com", "lang", "EN") @@ -50,9 +40,9 @@ class SimplyPremiumCom(Account):          if data['password'] == '' or data['password'] == '0':              post_data = {"key": user}          else: -            post_data = {"login_name": user, "login_pass": data["password"]} +            post_data = {"login_name": user, "login_pass": data['password']} -        self.html = req.load("http://www.simply-premium.com/login.php", post=post_data) +        html = req.load("http://www.simply-premium.com/login.php", post=post_data) -        if 'logout' not in self.html: +        if 'logout' not in html:              self.wrongPassword() diff --git a/module/plugins/accounts/SimplydebridCom.py b/module/plugins/accounts/SimplydebridCom.py index c07702105..406534364 100644 --- a/module/plugins/accounts/SimplydebridCom.py +++ b/module/plugins/accounts/SimplydebridCom.py @@ -6,26 +6,29 @@ from module.plugins.Account import Account  class SimplydebridCom(Account): -    __name__ = "SimplydebridCom" -    __version__ = "0.1" -    __type__ = "account" +    __name__    = "SimplydebridCom" +    __type__    = "account" +    __version__ = "0.10" +      __description__ = """Simply-Debrid.com account plugin""" -    __author_name__ = "Kagenoshin" -    __author_mail__ = "kagenoshin@gmx.ch" +    __license__     = "GPLv3" +    __authors__     = [("Kagenoshin", "kagenoshin@gmx.ch")] +      def loadAccountInfo(self, user, req):          get_data = {'login': 2, 'u': self.loginname, 'p': self.password} -        response = req.load("http://simply-debrid.com/api.php", get=get_data, decode=True) -        data = [x.strip() for x in response.split(";")] +        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": mktime(strptime(str(data[2]), "%d/%m/%Y"))} +      def login(self, user, data, req):          self.loginname = user -        self.password = data["password"] +        self.password = data['password']          get_data = {'login': 1, 'u': self.loginname, 'p': self.password} -        response = req.load("http://simply-debrid.com/api.php", get=get_data, decode=True) -        if response != "02: loggin success": +        res = req.load("http://simply-debrid.com/api.php", get=get_data, decode=True) +        if res != "02: loggin success":              self.wrongPassword() diff --git a/module/plugins/accounts/StahnuTo.py b/module/plugins/accounts/StahnuTo.py index 529e2131f..2b08c67cd 100644 --- a/module/plugins/accounts/StahnuTo.py +++ b/module/plugins/accounts/StahnuTo.py @@ -1,50 +1,33 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re  from module.plugins.Account import Account -from module.utils import parseFileSize  class StahnuTo(Account): -    __name__ = "StahnuTo" -    __version__ = "0.02" -    __type__ = "account" +    __name__    = "StahnuTo" +    __type__    = "account" +    __version__ = "0.04" +      __description__ = """StahnuTo account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] -    #login_timeout = 60      def loadAccountInfo(self, user, req):          html = req.load("http://www.stahnu.to/") -        found = re.search(r'>VIP: (\d+.*)<', html) -        trafficleft = parseFileSize(found.group(1)) * 1024 if found else 0 +        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} -        return {"premium": trafficleft > (512 * 1024), "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"], +            "password": data['password'],              "submit": "Login"})          if not '<a href="logout.php">' in html: diff --git a/module/plugins/accounts/StreamcloudEu.py b/module/plugins/accounts/StreamcloudEu.py new file mode 100644 index 000000000..aa1eafcbd --- /dev/null +++ b/module/plugins/accounts/StreamcloudEu.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from module.plugins.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/module/plugins/accounts/TurbobitNet.py b/module/plugins/accounts/TurbobitNet.py index 19ffaf1e5..f87d234a7 100644 --- a/module/plugins/accounts/TurbobitNet.py +++ b/module/plugins/accounts/TurbobitNet.py @@ -1,22 +1,5 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re  from time import mktime, strptime @@ -24,34 +7,35 @@ from module.plugins.Account import Account  class TurbobitNet(Account): -    __name__ = "TurbobitNet" +    __name__    = "TurbobitNet" +    __type__    = "account"      __version__ = "0.01" -    __type__ = "account" +      __description__ = """TurbobitNet account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] -    #login_timeout = 60      def loadAccountInfo(self, user, req):          html = req.load("http://turbobit.net") -        found = re.search(r'<u>Turbo Access</u> to ([0-9.]+)', html) -        if found: +        m = re.search(r'<u>Turbo Access</u> to ([\d.]+)', html) +        if m:              premium = True -            validuntil = mktime(strptime(found.group(1), "%d.%m.%Y")) +            validuntil = mktime(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[pass]": data['password'],              "user[submit]": "Login"})          if not '<div class="menu-item user-name">' in html: diff --git a/module/plugins/accounts/TusfilesNet.py b/module/plugins/accounts/TusfilesNet.py new file mode 100644 index 000000000..279dfd00a --- /dev/null +++ b/module/plugins/accounts/TusfilesNet.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +import re + +from time import mktime, strptime, gmtime + +from module.plugins.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/module/plugins/accounts/UlozTo.py b/module/plugins/accounts/UlozTo.py index 6b1067f28..7236a4fa8 100644 --- a/module/plugins/accounts/UlozTo.py +++ b/module/plugins/accounts/UlozTo.py @@ -2,42 +2,49 @@  import re +from urlparse import urljoin +  from module.plugins.Account import Account  class UlozTo(Account): -    __name__ = "UlozTo" -    __version__ = "0.06" -    __type__ = "account" +    __name__    = "UlozTo" +    __type__    = "account" +    __version__ = "0.10" +      __description__ = """Uloz.to account plugin""" -    __author_name__ = ("zoidberg", "pulpe") -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("pulpe", None)] + + +    TRAFFIC_LEFT_PATTERN = r'<li class="menu-kredit"><a .*?title="[^"]*?GB = ([\d.]+) MB"' -    TRAFFIC_LEFT_PATTERN = r'<li class="menu-kredit"><a href="/kredit" title="[^"]*?GB = ([0-9.]+) MB"'      def loadAccountInfo(self, user, req): -        #this cookie gets lost somehow after each request -        self.phpsessid = req.cj.getCookie("ULOSESSID")          html = req.load("http://www.ulozto.net/", decode=True) -        req.cj.setCookie("www.ulozto.net", "ULOSESSID", self.phpsessid) -        found = re.search(self.TRAFFIC_LEFT_PATTERN, html) -        trafficleft = int(float(found.group(1).replace(' ', '').replace(',', '.')) * 1000 * 1.048) if found else 0 -        self.premium = True if trafficleft else False +        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} -        return {"validuntil": -1, "trafficleft": trafficleft}      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('http://www.ulozto.net'+action, post={ -            "_token_": token, -            "login": "Submit", -            "password": data['password'], -            "username": user -        }, 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/module/plugins/accounts/UnrestrictLi.py b/module/plugins/accounts/UnrestrictLi.py index 2b647a49c..f5db3f888 100644 --- a/module/plugins/accounts/UnrestrictLi.py +++ b/module/plugins/accounts/UnrestrictLi.py @@ -1,30 +1,18 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################  from module.plugins.Account import Account  from module.common.json_layer import json_loads  class UnrestrictLi(Account): -    __name__ = "UnrestrictLi" -    __version__ = "0.03" -    __type__ = "account" +    __name__    = "UnrestrictLi" +    __type__    = "account" +    __version__ = "0.04" +      __description__ = """Unrestrict.li account plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __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') @@ -35,21 +23,22 @@ class UnrestrictLi(Account):              return {"premium": False}          validuntil = json_data['result']['expires'] -        trafficleft = int(json_data['result']['traffic'] / 1024) +        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")          if 'solvemedia' in html: -            self.logError("A Captcha is required. Go to http://unrestrict.li/sign_in and login, then retry") +            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"], +        post_data = {"username": user, "password": data['password'],                       "remember_me": "remember", "signin": "Sign in"} -        self.html = req.load("https://unrestrict.li/sign_in", post=post_data) +        html = req.load("https://unrestrict.li/sign_in", post=post_data) -        if 'sign_out' not in self.html: +        if 'sign_out' not in html:              self.wrongPassword() diff --git a/module/plugins/accounts/UploadableCh.py b/module/plugins/accounts/UploadableCh.py new file mode 100644 index 000000000..14c19adda --- /dev/null +++ b/module/plugins/accounts/UploadableCh.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +from module.plugins.Account import Account + + +class UploadableCh(Account): +    __name__    = "UploadableCh" +    __type__    = "account" +    __version__ = "0.02" + +    __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"}) + +        if "Login failed" in html: +            self.wrongPassword() diff --git a/module/plugins/accounts/UploadcCom.py b/module/plugins/accounts/UploadcCom.py new file mode 100644 index 000000000..d1e1a2ead --- /dev/null +++ b/module/plugins/accounts/UploadcCom.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from module.plugins.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/module/plugins/accounts/UploadedTo.py b/module/plugins/accounts/UploadedTo.py index 02f31c544..c09726799 100644 --- a/module/plugins/accounts/UploadedTo.py +++ b/module/plugins/accounts/UploadedTo.py @@ -1,22 +1,5 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay -""" -  import re  from time import time @@ -24,45 +7,62 @@ from module.plugins.Account import Account  class UploadedTo(Account): -    __name__ = "UploadedTo" -    __version__ = "0.26" -    __type__ = "account" +    __name__    = "UploadedTo" +    __type__    = "account" +    __version__ = "0.29" +      __description__ = """Uploaded.to account plugin""" -    __author_name__ = "mkaay" -    __author_mail__ = "mkaay@mkaay.de" +    __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 -        req.load("http://uploaded.net/language/en")          html = req.load("http://uploaded.net/me") -        premium = '<a href="register"><em>Premium</em>' in html or '<em>Premium</em></th>' in html - -        if premium: -            raw_traffic = re.search(r'<th colspan="2"><b class="cB">([^<]+)', html).group(1).replace('.', '') -            raw_valid = re.search(r"<td>Duration:</td>\s*<th>([^<]+)", html, re.MULTILINE).group(1).strip() +        premium = True if re.search(self.PREMIUM_PATTERN, html) else False -            traffic = int(self.parseTraffic(raw_traffic)) +        m = re.search(self.VALID_UNTIL_PATTERN, html, re.M) +        if m: +            expiredate = m.group(1).lower().strip() -            if raw_valid == "unlimited": +            if expiredate == "unlimited":                  validuntil = -1              else: -                raw_valid = re.findall(r"(\d+) (Week|weeks|days|day|hours|hour)", raw_valid) -                validuntil = time() -                for n, u in raw_valid: -                    validuntil += int(n) * 60 * 60 * {"Week": 168, "weeks": 168, "days": 24, -                                                      "day": 24, "hours": 1, "hour": 1}[u] +                m = re.findall(r'(\d+) (week|day|hour)', expiredate) +                if m: +                    validuntil = 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": traffic, "maxtraffic": 50 * 1024 * 1024} -        else: -            return {"premium": False, "validuntil": -1} +        return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium} -    def login(self, user, data, req): -        req.load("http://uploaded.net/language/en") +    def login(self, user, data, req):          req.cj.setCookie("uploaded.net", "lang", "en") -        page = req.load("http://uploaded.net/io/login", post={"id": user, "pw": data["password"], "_": ""}) +        html = req.load("http://uploaded.net/io/login", +                        post={'id': user, 'pw': data['password'], '_': ""}) -        if "User and password do not match!" in page: +        if "User and password do not match" in html:              self.wrongPassword() diff --git a/module/plugins/accounts/UploadheroCom.py b/module/plugins/accounts/UploadheroCom.py index 8adcff4ac..1e30b3771 100644 --- a/module/plugins/accounts/UploadheroCom.py +++ b/module/plugins/accounts/UploadheroCom.py @@ -8,21 +8,23 @@ from module.plugins.Account import Account  class UploadheroCom(Account): -    __name__ = "UploadheroCom" -    __version__ = "0.2" -    __type__ = "account" +    __name__    = "UploadheroCom" +    __type__    = "account" +    __version__ = "0.20" +      __description__ = """Uploadhero.co account plugin""" -    __author_name__ = "mcmyst" -    __author_mail__ = "mcmyst@hotmail.fr" +    __license__     = "GPLv3" +    __authors__     = [("mcmyst", "mcmyst@hotmail.fr")] +      def loadAccountInfo(self, user, req): -        premium_pattern = re.compile('Il vous reste <span class="bleu">([0-9]+)</span> jours premium.') +        premium_pattern = re.compile('Il vous reste <span class="bleu">(\d+)</span> jours premium')          data = self.getAccountData(user) -        page = req.load("http://uploadhero.co/my-account") +        html = req.load("http://uploadhero.co/my-account") -        if premium_pattern.search(page): -            end_date = datetime.date.today() + datetime.timedelta(days=int(premium_pattern.search(page).group(1))) +        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: @@ -30,9 +32,10 @@ class UploadheroCom(Account):          return account_info +      def login(self, user, data, req): -        page = req.load("http://uploadhero.co/lib/connexion.php", -                        post={"pseudo_login": user, "password_login": data["password"]}) +        html = req.load("http://uploadhero.co/lib/connexion.php", +                        post={"pseudo_login": user, "password_login": data['password']}) -        if "mot de passe invalide" in page: +        if "mot de passe invalide" in html:              self.wrongPassword() diff --git a/module/plugins/accounts/UploadingCom.py b/module/plugins/accounts/UploadingCom.py index f0395c13a..c70d2ec11 100644 --- a/module/plugins/accounts/UploadingCom.py +++ b/module/plugins/accounts/UploadingCom.py @@ -1,55 +1,63 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay -""" +import re  from time import time, strptime, mktime -import re  from module.plugins.Account import Account +from module.plugins.internal.SimpleHoster import set_cookies  class UploadingCom(Account): -    __name__ = "UploadingCom" -    __version__ = "0.1" -    __type__ = "account" +    __name__    = "UploadingCom" +    __type__    = "account" +    __version__ = "0.11" +      __description__ = """Uploading.com account plugin""" -    __author_name__ = "mkaay" -    __author_mail__ = "mkaay@mkaay.de" +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de")] + + +    PREMIUM_PATTERN = r'UPGRADE TO PREMIUM' +    VALID_UNTIL_PATTERN = r'Valid Until:(.+?)<' +      def loadAccountInfo(self, user, req): -        src = req.load("http://uploading.com/") -        premium = True -        if "UPGRADE TO PREMIUM" in src: -            return {"validuntil": -1, "trafficleft": -1, "premium": False} +        validuntil  = None +        trafficleft = None +        premium     = None + +        html = req.load("http://uploading.com/") -        m = re.search("Valid Until:(.*?)<", src) +        premium = False if re.search(self.PREMIUM_PATTERN, html) else True + +        m = re.search(self.VALID_UNTIL_PATTERN, html)          if m: -            validuntil = int(mktime(strptime(m.group(1).strip(), "%b %d, %Y"))) -        else: -            validuntil = -1 +            expiredate = m.group(1).strip() +            self.logDebug("Expire date: " + expiredate) + +            try: +                validuntil = mktime(strptime(expiredate, "%b %d, %Y")) + +            except Exception, e: +                self.logError(e) + +            else: +                if validuntil > mktime(gmtime()): +                    premium = True +                else: +                    premium = False +                    validuntil = None + +        return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium} -        return {"validuntil": validuntil, "trafficleft": -1, "premium": True}      def login(self, user, data, req): -        req.cj.setCookie("uploading.com", "lang", "1") -        req.cj.setCookie("uploading.com", "language", "1") -        req.cj.setCookie("uploading.com", "setlang", "en") -        req.cj.setCookie("uploading.com", "_lang", "en") +        set_cookies([("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() * 1000), -                 post={"email": user, "password": data["password"], "remember": "on"}) +                 post={'email': user, 'password': data['password'], 'remember': "on"}) diff --git a/module/plugins/accounts/UptoboxCom.py b/module/plugins/accounts/UptoboxCom.py index 3757ae0aa..299a0acc2 100644 --- a/module/plugins/accounts/UptoboxCom.py +++ b/module/plugins/accounts/UptoboxCom.py @@ -1,16 +1,17 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.XFSPAccount import XFSPAccount +from module.plugins.internal.XFSAccount import XFSAccount -class UptoboxCom(XFSPAccount): -    __name__ = "UptoboxCom" -    __version__ = "0.02" -    __type__ = "account" +class UptoboxCom(XFSAccount): +    __name__    = "UptoboxCom" +    __type__    = "account" +    __version__ = "0.07" +      __description__ = """DDLStorage.com account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] -    MAIN_PAGE = "http://uptobox.com/" -    VALID_UNTIL_PATTERN = r'>Premium.[Aa]ccount expire: ([^<]+)</strong>' +    HOSTER_DOMAIN = "uptobox.com" +    HOSTER_URL    = "https://uptobox.com/" diff --git a/module/plugins/accounts/VidPlayNet.py b/module/plugins/accounts/VidPlayNet.py new file mode 100644 index 000000000..5bfc24963 --- /dev/null +++ b/module/plugins/accounts/VidPlayNet.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from module.plugins.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/module/plugins/accounts/Vipleech4uCom.py b/module/plugins/accounts/Vipleech4uCom.py deleted file mode 100644 index 1e8463456..000000000 --- a/module/plugins/accounts/Vipleech4uCom.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -import re -from time import mktime, strptime - -from module.plugins.Account import Account - - -class Vipleech4uCom(Account): -    __name__ = "Vipleech4uCom" -    __version__ = "0.1" -    __type__ = "account" -    __description__ = """Vipleech4u.com account plugin""" -    __author_name__ = ("Kagenoshin") -    __author_mail__ = ("kagenoshin@gmx.ch") - -    STATUS_PATTERN = re.compile(r'status.*?<\s*?strong\s*?>[^<]*?vip[^<]*?<', re.I) -    VALIDUNTIL_PATTERN = re.compile(r'valid\s*?until.*?<\s*?strong\s*?>([^<]*?)<', re.I) - -    def loadAccountInfo(self, user, req): -        response = req.load("http://vipleech4u.com", decode=True) -        status = self.STATUS_PATTERN.search(response) - -        validuntil = self.VALIDUNTIL_PATTERN.search(response) -        if validuntil: -            validuntil = validuntil.group(1) - -        if status and validuntil: -            print status -            print validuntil -            return {"trafficleft": -1, "validuntil": mktime(strptime("%s 23:59" % validuntil, "%d-%m-%Y %H:%M"))} -        else: -            return {"premium": False} - -    def login(self, user, data, req): -        self.loginname = user -        self.password = data["password"] -        post_data = {'action': 'login', 'user': self.loginname, 'pass': self.password} -        req.load("http://vipleech4u.com/login.php") -        response = req.load("http://vipleech4u.com/login.php", post=post_data, decode=True) -        if 'Username or Password are incorrect' in response: -            self.wrongPassword() diff --git a/module/plugins/accounts/WarserverCz.py b/module/plugins/accounts/WarserverCz.py deleted file mode 100644 index a5acf5e52..000000000 --- a/module/plugins/accounts/WarserverCz.py +++ /dev/null @@ -1,68 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" - -import re - -from module.plugins.Account import Account -from module.utils import parseFileSize - - -class WarserverCz(Account): -    __name__ = "WarserverCz" -    __version__ = "0.02" -    __type__ = "account" -    __description__ = """Warserver.cz account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" - -    VALID_UNTIL_PATTERN = ur'<li>Neomezené stahovánà do: <strong>(.+?)<' -    TRAFFIC_LEFT_PATTERN = ur'<li>Kredit: <strong>(.+?)<' - -    DOMAIN = "http://www.warserver.cz" - -    def loadAccountInfo(self, user, req): -        html = req.load("%s/uzivatele/prehled" % self.DOMAIN, decode=True) - -        validuntil = trafficleft = None -        premium = False - -        found = re.search(self.VALID_UNTIL_PATTERN, html) -        if found: -            self.logDebug("VALID_UNTIL", found.group(1)) -            try: -                #validuntil = mktime(strptime(found.group(1), "%d %B %Y")) -                premium = True -                trafficleft = -1 -            except Exception, e: -                self.logError(e) - -        found = re.search(self.TRAFFIC_LEFT_PATTERN, html) -        if found: -            self.logDebug("TRAFFIC_LEFT", found.group(1)) -            trafficleft = parseFileSize((found.group(1).replace(" ", ""))) // 1024 -            premium = True if trafficleft > 1 << 18 else False - -        return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium} - -    def login(self, user, data, req): -        html = req.load('%s/uzivatele/prihlaseni?do=prihlaseni-submit' % self.DOMAIN, -                        post={"username": user, "password": data['password'], "send": u"PÅihlásit"}, decode=True) - -        if '<p class="chyba">' in html: -            self.wrongPassword() diff --git a/module/plugins/accounts/WebshareCz.py b/module/plugins/accounts/WebshareCz.py new file mode 100644 index 000000000..f8e3eeb73 --- /dev/null +++ b/module/plugins/accounts/WebshareCz.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- + +import re + +from hashlib import md5, sha1 +from passlib.hash import md5_crypt +from time import mktime, strptime, time + +from module.plugins.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  = mktime(strptime(expiredate, "%Y-%m-%d %H:%M:%S")) +        trafficleft = self.parseTraffic(re.search(self.TRAFFIC_LEFT_PATTERN, html).group(1)) +        premium     = validuntil > 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/module/plugins/accounts/XFileSharingPro.py b/module/plugins/accounts/XFileSharingPro.py new file mode 100644 index 000000000..8dc7f3a30 --- /dev/null +++ b/module/plugins/accounts/XFileSharingPro.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +from module.plugins.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/module/plugins/accounts/YibaishiwuCom.py b/module/plugins/accounts/YibaishiwuCom.py index 75aae25b9..92a6bfedf 100644 --- a/module/plugins/accounts/YibaishiwuCom.py +++ b/module/plugins/accounts/YibaishiwuCom.py @@ -1,46 +1,33 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re  from module.plugins.Account import Account  class YibaishiwuCom(Account): -    __name__ = "YibaishiwuCom" +    __name__    = "YibaishiwuCom" +    __type__    = "account"      __version__ = "0.01" -    __type__ = "account" +      __description__ = """115.com account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __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) -        found = re.search(self.ACCOUNT_INFO_PATTERN, html, re.S) -        premium = True if (found and 'is_vip: 1' in found.group(1)) else False -        validuntil = trafficleft = (-1 if found else 0) +        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/", diff --git a/module/plugins/accounts/ZeveraCom.py b/module/plugins/accounts/ZeveraCom.py index 1eb90801a..e8e3431e3 100644 --- a/module/plugins/accounts/ZeveraCom.py +++ b/module/plugins/accounts/ZeveraCom.py @@ -6,12 +6,14 @@ from module.plugins.Account import Account  class ZeveraCom(Account): -    __name__ = "ZeveraCom" -    __version__ = "0.21" -    __type__ = "account" +    __name__    = "ZeveraCom" +    __type__    = "account" +    __version__ = "0.22" +      __description__ = """Zevera.com account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] +      def loadAccountInfo(self, user, req):          data = self.getAPIData(req) @@ -19,18 +21,20 @@ class ZeveraCom(Account):              account_info = {"trafficleft": 0, "validuntil": 0, "premium": False}          else:              account_info = { -                "trafficleft": int(data['availabletodaytraffic']) * 1024, +                "trafficleft": float(data['availabletodaytraffic']) * 1024,                  "validuntil": mktime(strptime(data['endsubscriptiondate'], "%Y/%m/%d %H:%M:%S")),                  "premium": True              }          return account_info +      def login(self, user, data, req):          self.loginname = user -        self.password = data["password"] +        self.password = data['password']          if self.getAPIData(req) == "No traffic":              self.wrongPassword() +      def getAPIData(self, req, just_header=False, **kwargs):          get_data = {              'cmd': 'accountinfo', @@ -39,14 +43,14 @@ class ZeveraCom(Account):          }          get_data.update(kwargs) -        response = req.load("http://www.zevera.com/jDownloader.ashx", get=get_data, +        res = req.load("http://www.zevera.com/jDownloader.ashx", get=get_data,                              decode=True, just_header=just_header) -        self.logDebug(response) +        self.logDebug(res) -        if ':' in response: +        if ':' in res:              if not just_header: -                response = response.replace(',', '\n') +                res = res.replace(',', '\n')              return dict((y.strip().lower(), z.strip()) for (y, z) in -                        [x.split(':', 1) for x in response.splitlines() if ':' in x]) +                        [x.split(':', 1) for x in res.splitlines() if ':' in x])          else: -            return response +            return res diff --git a/module/plugins/captcha/GigasizeCom.py b/module/plugins/captcha/GigasizeCom.py index 2d0837257..244cf6a2a 100644 --- a/module/plugins/captcha/GigasizeCom.py +++ b/module/plugins/captcha/GigasizeCom.py @@ -1,20 +1,24 @@  # -*- coding: utf-8 -*- -from captcha import OCR +from module.plugins.captcha.captcha import OCR +  class GigasizeCom(OCR): +    __name__    = "GigasizeCom" +    __type__    = "ocr" +    __version__ = "0.10" + +    __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 - -if __name__ == '__main__': -    ocr = GigasizeCom() -    import urllib -    urllib.urlretrieve('http://www.gigasize.com/randomImage.php', "gigasize_tmp.jpg") - -    print ocr.get_captcha('gigasize_tmp.jpg') diff --git a/module/plugins/captcha/LinksaveIn.py b/module/plugins/captcha/LinksaveIn.py index 8ce26fbac..56cbd58a0 100644 --- a/module/plugins/captcha/LinksaveIn.py +++ b/module/plugins/captcha/LinksaveIn.py @@ -1,19 +1,32 @@  # -*- coding: utf-8 -*- -from captcha import OCR -import Image -from os import sep -from os.path import dirname -from os.path import abspath +try: +    from PIL import Image +except ImportError: +    import Image +  from glob import glob +from os import sep +from os.path import abspath, dirname + +from module.plugins.captcha.captcha import OCR  class LinksaveIn(OCR): -    __name__ = "LinksaveIn" +    __name__    = "LinksaveIn" +    __type__    = "ocr" +    __version__ = "0.10" + +    __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 @@ -41,6 +54,7 @@ class LinksaveIn(OCR):          self.pixels = self.image.load()          self.result_captcha = '' +      def get_bg(self):          stat = {}          cstat = {} @@ -71,12 +85,13 @@ class LinksaveIn(OCR):                          stat[bgpath] += 1          max_p = 0          bg = "" -        for bgpath, value in stat.items(): +        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") @@ -99,6 +114,7 @@ class LinksaveIn(OCR):                  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() @@ -120,6 +136,7 @@ class LinksaveIn(OCR):          self.image = new          self.pixels = self.image.load() +      def get_captcha(self, image):          self.load_image(image)          bg = self.get_bg() @@ -139,11 +156,3 @@ class LinksaveIn(OCR):              final += self.result_captcha          return final - -if __name__ == '__main__': -    import urllib -    ocr = LinksaveIn() -    testurl = "http://linksave.in/captcha/cap.php?hsh=2229185&code=ZzHdhl3UffV3lXTH5U4b7nShXj%2Bwma1vyoNBcbc6lcc%3D" -    urllib.urlretrieve(testurl, ocr.data_dir+"captcha.gif") - -    print ocr.get_captcha(ocr.data_dir+'captcha.gif') diff --git a/module/plugins/captcha/NetloadIn.py b/module/plugins/captcha/NetloadIn.py index 733fe99db..28eb18fb5 100644 --- a/module/plugins/captcha/NetloadIn.py +++ b/module/plugins/captcha/NetloadIn.py @@ -1,12 +1,22 @@  # -*- coding: utf-8 -*- -from captcha import OCR +from module.plugins.captcha.captcha import OCR +  class NetloadIn(OCR): -    __name__ = "NetloadIn" +    __name__    = "NetloadIn" +    __type__    = "ocr" +    __version__ = "0.10" + +    __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() @@ -17,10 +27,3 @@ class NetloadIn(OCR):          self.result_captcha = self.result_captcha.replace(" ", "")[:4] # cut to 4 numbers          return self.result_captcha - -if __name__ == '__main__': -    import urllib -    ocr = NetloadIn() -    urllib.urlretrieve("http://netload.in/share/includes/captcha.php", "captcha.png") - -    print  ocr.get_captcha('captcha.png') diff --git a/module/plugins/captcha/ShareonlineBiz.py b/module/plugins/captcha/ShareonlineBiz.py index 0c87b636d..8210e8859 100644 --- a/module/plugins/captcha/ShareonlineBiz.py +++ b/module/plugins/captcha/ShareonlineBiz.py @@ -1,31 +1,23 @@  # -*- coding: utf-8 -*- -# -#Copyright (C) 2009 kingzero, RaNaN -# -#This program is free software; you can redistribute it and/or modify -#it under the terms of the GNU General Public License as published by -#the Free Software Foundation; either version 3 of the License, -#or (at your option) any later version. -# -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -#See the GNU General Public License for more details. -# -#You should have received a copy of the GNU General Public License -# along with this program; if not, see <http://www.gnu.org/licenses/>. -# -### -from captcha import OCR +from module.plugins.captcha.captcha import OCR +  class ShareonlineBiz(OCR): -    __name__ = "ShareonlineBiz" +    __name__    = "ShareonlineBiz" +    __type__    = "ocr" +    __version__ = "0.10" + +    __description__ = """Shareonline.biz ocr plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org")] +      def __init__(self):          OCR.__init__(self) -    def get_captcha(self, image):  + +    def get_captcha(self, image):          self.load_image(image)          self.to_greyscale()          self.image = self.image.resize((160, 50)) @@ -45,9 +37,3 @@ class ShareonlineBiz(OCR):          return final          #tesseract at 60% - -if __name__ == '__main__': -    import urllib -    ocr = ShareonlineBiz() -    urllib.urlretrieve("http://www.share-online.biz/captcha.php", "captcha.jpeg") -    print  ocr.get_captcha('captcha.jpeg') diff --git a/module/plugins/captcha/captcha.py b/module/plugins/captcha/captcha.py index 7e4dec697..0f233ec00 100644 --- a/module/plugins/captcha/captcha.py +++ b/module/plugins/captcha/captcha.py @@ -1,56 +1,49 @@  # -*- coding: utf-8 -*- -# -#Copyright (C) 2009 kingzero, RaNaN -# -#This program is free software; you can redistribute it and/or modify -#it under the terms of the GNU General Public License as published by -#the Free Software Foundation; either version 3 of the License, -#or (at your option) any later version. -# -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -#See the GNU General Public License for more details. -# -#You should have received a copy of the GNU General Public License -# along with this program; if not, see <http://www.gnu.org/licenses/>. -# -###  from __future__ import with_statement -import os -from os.path import join -from os.path import abspath + +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 -import Image -import TiffImagePlugin -import PngImagePlugin -import GifImagePlugin -import JpegImagePlugin +from os.path import abspath, join  class OCR(object): +    __name__    = "OCR" +    __type__    = "ocr" +    __version__ = "0.10" + +    __description__ = """OCR base plugin""" +    __license__     = "GPLv3" +    __authors__     = [("pyLoad Team", "admin@pyload.org")] -    __name__ = "OCR"      def __init__(self):          self.logger = logging.getLogger("log") +      def load_image(self, image):          self.image = Image.open(image)          self.pixels = self.image.load()          self.result_captcha = '' +      def unload(self):          """delete all tmp images"""          pass +      def threshold(self, value):          self.image = self.image.point(lambda a: a * value + 10) +      def run(self, command):          """Run a command""" @@ -61,42 +54,46 @@ class OCR(object):          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): -        #self.logger.debug("create tmp tif") +        #tmpTif = tempfile.NamedTemporaryFile(suffix=".tif") +        try: +            tmpTif = open(join("tmp", "tmpTif_%s.tif" % self.__name__), "wb") +            tmpTif.close() + +            #tmpTxt = tempfile.NamedTemporaryFile(suffix=".txt") +            tmpTxt = open(join("tmp", "tmpTxt_%s.txt" % self.__name__), "wb") +            tmpTxt.close() -        #tmp = tempfile.NamedTemporaryFile(suffix=".tif") -        tmp = open(join("tmp", "tmpTif_%s.tif" % self.__name__), "wb") -        tmp.close() -        #self.logger.debug("create tmp txt") -        #tmpTxt = tempfile.NamedTemporaryFile(suffix=".txt") -        tmpTxt = open(join("tmp", "tmpTxt_%s.txt" % self.__name__), "wb") -        tmpTxt.close() +        except IOError, e: +            self.logError(e) +            return          self.logger.debug("save tiff") -        self.image.save(tmp.name, 'TIFF') +        self.image.save(tmpTif.name, 'TIFF')          if os.name == "nt": -            tessparams = [join(pypath,"tesseract","tesseract.exe")] +            tessparams = [join(pypath, "tesseract", "tesseract.exe")]          else: -            tessparams = ['tesseract'] +            tessparams = ["tesseract"] -        tessparams.extend( [abspath(tmp.name), abspath(tmpTxt.name).replace(".txt", "")] ) +        tessparams.extend( [abspath(tmpTif.name), abspath(tmpTxt.name).replace(".txt", "")] )          if subset and (digits or lowercase or uppercase): -            #self.logger.debug("create temp subset config")              #tmpSub = tempfile.NamedTemporaryFile(suffix=".subset") -            tmpSub = open(join("tmp", "tmpSub_%s.subset" % self.__name__), "wb") -            tmpSub.write("tessedit_char_whitelist ") -            if digits: -                tmpSub.write("0123456789") -            if lowercase: -                tmpSub.write("abcdefghijklmnopqrstuvwxyz") -            if uppercase: -                tmpSub.write("ABCDEFGHIJKLMNOPQRSTUVWXYZ") -            tmpSub.write("\n") -            tessparams.append("nobatch") -            tessparams.append(abspath(tmpSub.name)) -            tmpSub.close() +            with open(join("tmp", "tmpSub_%s.subset" % self.__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(abspath(tmpSub.name))          self.logger.debug("run tesseract")          self.run(tessparams) @@ -110,22 +107,25 @@ class OCR(object):          self.logger.debug(self.result_captcha)          try: -            os.remove(tmp.name) +            os.remove(tmpTif.name)              os.remove(tmpTxt.name)              if subset and (digits or lowercase or uppercase):                  os.remove(tmpSub.name)          except:              pass +      def get_captcha(self, name):          raise NotImplementedError +      def to_greyscale(self):          if self.image.mode != 'L':              self.image = self.image.convert('L')          self.pixels = self.image.load() +      def eval_black_white(self, limit):          self.pixels = self.image.load()          w, h = self.image.size @@ -136,6 +136,7 @@ class OCR(object):                  else:                      self.pixels[x, y] = 0 +      def clean(self, allowed):          pixels = self.pixels @@ -143,19 +144,28 @@ class OCR(object):          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 +                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 +                    if pixels[x-1, y-1] != 255: +                        count += 1 +                    if pixels[x-1, y] != 255: +                        count += 1 +                    if pixels[x-1, y + 1] != 255: +                        count += 1 +                    if pixels[x, y + 1] != 255: +                        count += 1 +                    if pixels[x + 1, y + 1] != 255: +                        count += 1 +                    if pixels[x + 1, y] != 255: +                        count += 1 +                    if pixels[x + 1, y-1] != 255: +                        count += 1 +                    if pixels[x, y-1] != 255: +                        count += 1                  except:                      pass @@ -167,10 +177,12 @@ class OCR(object):              # 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 +                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""" @@ -245,6 +257,7 @@ class OCR(object):          self.pixels = pixels +      def split_captcha_letters(self):          captcha = self.image          started = False @@ -262,13 +275,16 @@ class OCR(object):                          firstX = x                          lastX = x -                    if y > bottomY: bottomY = y -                    if y < topY: topY = y -                    if x > lastX: 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 == False and started == True: +            if black_pixel_in_col is False and started is True:                  rect = (firstX, topY, lastX, bottomY)                  new_captcha = captcha.crop(rect) @@ -281,8 +297,8 @@ class OCR(object):          return letters -    def correct(self, values, var=None): +    def correct(self, values, var=None):          if var:              result = var          else: @@ -300,14 +316,3 @@ class OCR(object):              return result          else:              self.result_captcha = result - - -if __name__ == '__main__': -    ocr = OCR() -    ocr.load_image("B.jpg") -    ocr.to_greyscale() -    ocr.eval_black_white(140) -    ocr.derotate_by_average() -    ocr.run_tesser() -    print "Tesseract", ocr.result_captcha -    ocr.image.save("derotated.jpg") diff --git a/module/plugins/container/CCF.py b/module/plugins/container/CCF.py index 8aec97f6a..bca535175 100644 --- a/module/plugins/container/CCF.py +++ b/module/plugins/container/CCF.py @@ -1,24 +1,31 @@  # -*- coding: utf-8 -*- +from __future__ import with_statement +  import re + +from os import makedirs +from os.path import exists  from urllib2 import build_opener +from MultipartPostHandler import MultipartPostHandler +  from module.plugins.Container import Container -from module.lib.MultipartPostHandler import MultipartPostHandler +from module.utils import save_join -from os import makedirs -from os.path import exists, join  class CCF(Container): -    __name__ = "CCF" -    __version__ = "0.2" +    __name__    = "CCF" +    __version__ = "0.20" +      __pattern__ = r'.+\.ccf' +      __description__ = """CCF container decrypter plugin""" -    __author_name__ = "Willnix" -    __author_mail__ = "Willnix@pyload.org" +    __license__     = "GPLv3" +    __authors__     = [("Willnix", "Willnix@pyload.org")] -    def decrypt(self, pyfile): +    def decrypt(self, pyfile):          infile = pyfile.url.replace("\n", "")          opener = build_opener(MultipartPostHandler) @@ -28,13 +35,9 @@ class CCF(Container):          tempdlc_content = opener.open('http://service.jdownloader.net/dlcrypt/getDLC.php', params).read()          download_folder = self.config['general']['download_folder'] -        location = download_folder #join(download_folder, pyfile.package().folder.decode(sys.getfilesystemencoding())) -        if not exists(location):  -            makedirs(location) -        tempdlc_name = join(location, "tmp_%s.dlc" % pyfile.name) -        tempdlc = open(tempdlc_name, "w") -        tempdlc.write(re.search(r'<dlc>(.*)</dlc>', tempdlc_content, re.DOTALL).group(1)) -        tempdlc.close() +        tempdlc_name = save_join(download_folder, "tmp_%s.dlc" % pyfile.name) +        with open(tempdlc_name, "w") as tempdlc: +            tempdlc.write(re.search(r'<dlc>(.*)</dlc>', tempdlc_content, re.S).group(1)) -        self.packages.append((tempdlc_name, [tempdlc_name], tempdlc_name)) +        self.urls = [tempdlc_name] diff --git a/module/plugins/container/DLC_25.pyc b/module/plugins/container/DLC_25.pyc Binary files differindex b8fde0051..409264902 100644 --- a/module/plugins/container/DLC_25.pyc +++ b/module/plugins/container/DLC_25.pyc diff --git a/module/plugins/container/DLC_26.pyc b/module/plugins/container/DLC_26.pyc Binary files differindex 41a4e0cb8..685995fc2 100644 --- a/module/plugins/container/DLC_26.pyc +++ b/module/plugins/container/DLC_26.pyc diff --git a/module/plugins/container/DLC_27.pyc b/module/plugins/container/DLC_27.pyc Binary files differindex a6bffaf74..6c6d663e5 100644 --- a/module/plugins/container/DLC_27.pyc +++ b/module/plugins/container/DLC_27.pyc diff --git a/module/plugins/container/LinkList.py b/module/plugins/container/LinkList.py index 79678954d..c6173ad73 100644 --- a/module/plugins/container/LinkList.py +++ b/module/plugins/container/LinkList.py @@ -1,18 +1,24 @@  # -*- coding: utf-8 -*-  import codecs -from module.utils import fs_encode +  from module.plugins.Container import Container +from module.utils import fs_encode +  class LinkList(Container): -    __name__ = "LinkList" +    __name__    = "LinkList"      __version__ = "0.12" +      __pattern__ = r'.+\.txt' -    __description__ = """Read link lists in txt format"""      __config__ = [("clear", "bool", "Clear Linklist after adding", False),                    ("encoding", "string", "File encoding (default utf-8)", "")] -    __author_name__ = ("spoob", "jeix") -    __author_mail__ = ("spoob@pyload.org", "jeix@hasnomail.com") + +    __description__ = """Read link lists in txt format""" +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org"), +                       ("jeix", "jeix@hasnomail.com")] +      def decrypt(self, pyfile):          try: @@ -20,9 +26,6 @@ class LinkList(Container):          except:              file_enc = "utf-8" -        print repr(pyfile.url) -        print pyfile.url -          file_name = fs_encode(pyfile.url)          txt = codecs.open(file_name, 'r', file_enc) @@ -33,7 +36,8 @@ class LinkList(Container):          for link in links:              link = link.strip() -            if not link: continue +            if not link: +                continue              if link.startswith(";"):                  continue @@ -61,7 +65,7 @@ class LinkList(Container):                  txt = open(file_name, 'wb')                  txt.close()              except: -                self.log.warning(_("LinkList could not be cleared.")) +                self.logWarning(_("LinkList could not be cleared"))          for name, links in packages.iteritems():              self.packages.append((name, links, name)) diff --git a/module/plugins/container/RSDF.py b/module/plugins/container/RSDF.py index 42296f2d5..0c43f0e6c 100644 --- a/module/plugins/container/RSDF.py +++ b/module/plugins/container/RSDF.py @@ -1,24 +1,32 @@  # -*- coding: utf-8 -*- +from __future__ import with_statement +  import base64  import binascii  import re  from module.plugins.Container import Container +from module.utils import fs_encode +  class RSDF(Container): -    __name__ = "RSDF" -    __version__ = "0.21" +    __name__    = "RSDF" +    __version__ = "0.24" +      __pattern__ = r'.+\.rsdf' +      __description__ = """RSDF container decrypter plugin""" -    __author_name__ = ("RaNaN", "spoob") -    __author_mail__ = ("RaNaN@pyload.org", "spoob@pyload.org") +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org"), +                       ("spoob", "spoob@pyload.org")] +      def decrypt(self, pyfile):          from Crypto.Cipher import AES -        infile = pyfile.url.replace("\n", "") +        infile = fs_encode(pyfile.url.replace("\n", ""))          Key = binascii.unhexlify('8C35192D964DC3182C6F84F3252239EB4A320D2500000000')          IV = binascii.unhexlify('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF') @@ -27,21 +35,22 @@ class RSDF(Container):          obj = AES.new(Key, AES.MODE_CFB, IV) -        rsdf = open(infile, 'r') - -        data = rsdf.read() -        rsdf.close() +        try: +            with open(infile, 'r') as rsdf: +                data = rsdf.read() +        except IOError, e: +            self.fail(str(e))          if re.search(r"<title>404 - Not Found</title>", data) is None:              data = binascii.unhexlify(''.join(data.split()))              data = data.splitlines() -            links = []              for link in data: +                if not link: +                    continue                  link = base64.b64decode(link)                  link = obj.decrypt(link)                  decryptedUrl = link.replace('CCF: ', '') -                links.append(decryptedUrl) +                self.urls.append(decryptedUrl) -            self.log.debug("%s: adding package %s with %d links" % (self.__name__,pyfile.package().name,len(links))) -            self.packages.append((pyfile.package().name, links)) +            self.logDebug("Adding package %s with %d links" % (pyfile.package().name, len(self.urls))) diff --git a/module/plugins/crypter/BitshareComFolder.py b/module/plugins/crypter/BitshareComFolder.py index 7139c26a6..c70a849b6 100644 --- a/module/plugins/crypter/BitshareComFolder.py +++ b/module/plugins/crypter/BitshareComFolder.py @@ -1,30 +1,24 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -from module.plugins.internal.SimpleCrypter import SimpleCrypter + +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  class BitshareComFolder(SimpleCrypter): -    __name__ = "BitshareComFolder" -    __type__ = "crypter" +    __name__    = "BitshareComFolder" +    __type__    = "crypter" +    __version__ = "0.03" +      __pattern__ = r'http://(?:www\.)?bitshare\.com/\?d=\w+' -    __version__ = "0.01" +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Bitshare.com folder decrypter plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __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>' + -    LINK_PATTERN = r'<a href="(http://bitshare.com/files/.+)">.+</a></td>' -    TITLE_PATTERN = r'View public folder "(?P<title>.+)"</h1>' +getInfo = create_getInfo(BitshareComFolder) diff --git a/module/plugins/crypter/C1neonCom.py b/module/plugins/crypter/C1neonCom.py index 8923cfc32..bc58fbf1c 100644 --- a/module/plugins/crypter/C1neonCom.py +++ b/module/plugins/crypter/C1neonCom.py @@ -1,13 +1,19 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.DeadCrypter import DeadCrypter +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo  class C1neonCom(DeadCrypter): -    __name__ = "C1neonCom" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?c1neon.com/.*?' +    __name__    = "C1neonCom" +    __type__    = "crypter"      __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?c1neon\.com/.+' +    __config__  = [] +      __description__ = """C1neon.com decrypter plugin""" -    __author_name__ = "godofdream" -    __author_mail__ = "soilfiction@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("godofdream", "soilfiction@gmail.com")] + + +getInfo = create_getInfo(C1neonCom) diff --git a/module/plugins/crypter/ChipDe.py b/module/plugins/crypter/ChipDe.py index 529ec6918..133e5a005 100644 --- a/module/plugins/crypter/ChipDe.py +++ b/module/plugins/crypter/ChipDe.py @@ -5,20 +5,25 @@ from module.plugins.Crypter import Crypter  class ChipDe(Crypter): -    __name__ = "ChipDe" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?chip.de/video/.*\.html' -    __version__ = "0.1" +    __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_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Chip.de decrypter plugin""" -    __author_name__ = "4Christopher" -    __author_mail__ = "4Christopher@gmx.de" +    __license__     = "GPLv3" +    __authors__     = [("4Christopher", "4Christopher@gmx.de")] +      def decrypt(self, pyfile):          self.html = self.load(pyfile.url)          try: -            url = re.search(r'"(http://video.chip.de/\d+?/.*)"', self.html).group(1) -            self.logDebug('The file URL is %s' % url) +            f = re.search(r'"(http://video\.chip\.de/.+)"', self.html)          except: -            self.fail('Failed to find the URL') - -        self.packages.append((pyfile.package().name, [url], pyfile.package().folder)) +            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/module/plugins/crypter/CloudzillaToFolder.py b/module/plugins/crypter/CloudzillaToFolder.py new file mode 100644 index 000000000..c156d4de4 --- /dev/null +++ b/module/plugins/crypter/CloudzillaToFolder.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo + + +class CloudzillaToFolder(SimpleHoster): +    __name__    = "CloudzillaToFolder" +    __type__    = "crypter" +    __version__ = "0.01" + +    __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") + + +    def getLinks(self): +        return [urljoin("http://www.cloudzilla.to", link) for link in super(CloudzillaToFolder, self).getLinks()] + + +getInfo = create_getInfo(CloudzillaToFolder) diff --git a/module/plugins/crypter/CrockoComFolder.py b/module/plugins/crypter/CrockoComFolder.py index e64e63534..57bb339ff 100644 --- a/module/plugins/crypter/CrockoComFolder.py +++ b/module/plugins/crypter/CrockoComFolder.py @@ -1,15 +1,23 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.SimpleCrypter import SimpleCrypter +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  class CrockoComFolder(SimpleCrypter): -    __name__ = "CrockoComFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?crocko.com/f/.*' +    __name__    = "CrockoComFolder" +    __type__    = "crypter"      __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?crocko\.com/f/.+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Crocko.com folder decrypter plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] +      LINK_PATTERN = r'<td class="last"><a href="([^"]+)">download</a>' + + +getInfo = create_getInfo(CrockoComFolder) diff --git a/module/plugins/crypter/CryptItCom.py b/module/plugins/crypter/CryptItCom.py index 6f8fbaa1a..2cf4e9f62 100644 --- a/module/plugins/crypter/CryptItCom.py +++ b/module/plugins/crypter/CryptItCom.py @@ -1,13 +1,19 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.DeadCrypter import DeadCrypter +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo  class CryptItCom(DeadCrypter): -    __name__ = "CryptItCom" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?crypt-it\.com/(s|e|d|c)/[\w]+' +    __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""" -    __author_name__ = "jeix" -    __author_mail__ = "jeix@hasnomail.de" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de")] + + +getInfo = create_getInfo(CryptItCom) diff --git a/module/plugins/crypter/CzshareComFolder.py b/module/plugins/crypter/CzshareComFolder.py index d1ba8335c..5623a4093 100644 --- a/module/plugins/crypter/CzshareComFolder.py +++ b/module/plugins/crypter/CzshareComFolder.py @@ -5,27 +5,28 @@ from module.plugins.Crypter import Crypter  class CzshareComFolder(Crypter): -    __name__ = "CzshareComFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?(czshare|sdilej)\.(com|cz)/folders/.*' -    __version__ = "0.2" +    __name__    = "CzshareComFolder" +    __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_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Czshare.com folder decrypter plugin, now Sdilej.cz""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.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>' -    #NEXT_PAGE_PATTERN = r'<a class="next " href="/([^"]+)"> </a>' +      def decrypt(self, pyfile):          html = self.load(pyfile.url) -        new_links = [] -        found = re.search(self.FOLDER_PATTERN, html, re.DOTALL) -        if found is None: self.fail("Parse error (FOLDER)") -        new_links.extend(re.findall(self.LINK_PATTERN, found.group(1))) +        m = re.search(self.FOLDER_PATTERN, html, re.S) +        if m is None: +            self.error(_("FOLDER_PATTERN not found")) -        if new_links: -            self.core.files.addLinks(new_links, pyfile.package().id) -        else: -            self.fail('Could not extract any links') +        self.urls.extend(re.findall(self.LINK_PATTERN, m.group(1))) diff --git a/module/plugins/crypter/DDLMusicOrg.py b/module/plugins/crypter/DDLMusicOrg.py index 27cff3f9d..55181e9ad 100644 --- a/module/plugins/crypter/DDLMusicOrg.py +++ b/module/plugins/crypter/DDLMusicOrg.py @@ -1,30 +1,37 @@  # -*- coding: utf-8 -*-  import re +  from time import sleep  from module.plugins.Crypter import Crypter  class DDLMusicOrg(Crypter): -    __name__ = "DDLMusicOrg" -    __type__ = "crypter" +    __name__    = "DDLMusicOrg" +    __type__    = "crypter" +    __version__ = "0.30" +      __pattern__ = r'http://(?:www\.)?ddl-music\.org/captcha/ddlm_cr\d\.php\?\d+\?\d+' -    __version__ = "0.3" +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Ddl-music.org decrypter plugin""" -    __author_name__ = "mkaay" -    __author_mail__ = "mkaay@mkaay.de" +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de")] +      def setup(self):          self.multiDL = False +      def decrypt(self, pyfile): -        html = self.req.load(pyfile.url, cookies=True) +        html = self.load(pyfile.url, cookies=True)          if re.search(r"Wer dies nicht rechnen kann", html) is not None:              self.offline() -        math = re.search(r"(\d+) ([\+-]) (\d+) =\s+<inp", self.html) +        math = re.search(r"(\d+) ([+-]) (\d+) =\s+<inp", self.html)          id = re.search(r"name=\"id\" value=\"(\d+)\"", self.html).group(1)          linknr = re.search(r"name=\"linknr\" value=\"(\d+)\"", self.html).group(1) @@ -34,11 +41,11 @@ class DDLMusicOrg(Crypter):          else:              solve = int(math.group(1)) - int(math.group(3))          sleep(3) -        htmlwithlink = self.req.load(pyfile.url, cookies=True, +        htmlwithlink = self.load(pyfile.url, cookies=True,                                       post={"calc%s" % linknr: solve, "send%s" % linknr: "Send", "id": id,                                             "linknr": linknr})          m = re.search(r"<form id=\"ff\" action=\"(.*?)\" method=\"post\">", htmlwithlink)          if m: -            self.packages.append((pyfile.package().name, [m.group(1)], pyfile.package().folder)) +            self.urls = [m.group(1)]          else:              self.retry() diff --git a/module/plugins/crypter/DailymotionBatch.py b/module/plugins/crypter/DailymotionBatch.py index 5c0dd9cec..8d4cb64df 100644 --- a/module/plugins/crypter/DailymotionBatch.py +++ b/module/plugins/crypter/DailymotionBatch.py @@ -1,24 +1,8 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: Walter Purcaro -""" +import re  from urlparse import urljoin -import re  from module.common.json_layer import json_loads  from module.plugins.Crypter import Crypter @@ -26,19 +10,25 @@ from module.utils import save_join  class DailymotionBatch(Crypter): -    __name__ = "DailymotionBatch" -    __type__ = "crypter" -    __pattern__ = r'https?://(?:www\.)?dailymotion\.com/((playlists/)?(?P<TYPE>playlist|user)/)?(?P<ID>[\w^_]+)(?(TYPE)|#)' +    __name__    = "DailymotionBatch" +    __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_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Dailymotion.com channel & playlist decrypter""" -    __author_name__ = "Walter Purcaro" -    __author_mail__ = "vuolter@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] +      def api_response(self, ref, req=None):          url = urljoin("https://api.dailymotion.com/", ref)          page = self.load(url, get=req)          return json_loads(page) +      def getPlaylistInfo(self, id):          ref = "playlist/" + id          req = {"fields": "name,owner.screenname"} @@ -47,10 +37,11 @@ class DailymotionBatch(Crypter):          if "error" in playlist:              return -        name = playlist["name"] -        owner = playlist["owner.screenname"] +        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} @@ -59,16 +50,18 @@ class DailymotionBatch(Crypter):          if "error" in user:              return -        for playlist in user["list"]: -            yield playlist["id"] +        for playlist in user['list']: +            yield playlist['id'] -        if user["has_more"]: +        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} @@ -77,20 +70,22 @@ class DailymotionBatch(Crypter):          if "error" in playlist:              return -        for video in playlist["list"]: -            yield video["url"] +        for video in playlist['list']: +            yield video['url'] -        if playlist["has_more"]: +        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") +        m_id = m.group('ID') +        m_type = m.group('TYPE')          if m_type == "playlist":              self.logDebug("Url recognized as Playlist") @@ -102,7 +97,7 @@ class DailymotionBatch(Crypter):              self.logDebug("%s playlist\s found on channel \"%s\"" % (len(playlists), m_id))          if not playlists: -            self.fail("No playlist available") +            self.fail(_("No playlist available"))          for p_id, p_name, p_owner in playlists:              p_videos = self.getVideos(p_id) diff --git a/module/plugins/crypter/DataHuFolder.py b/module/plugins/crypter/DataHuFolder.py index 04ef152fc..a5602d6c6 100644 --- a/module/plugins/crypter/DataHuFolder.py +++ b/module/plugins/crypter/DataHuFolder.py @@ -1,54 +1,43 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################  import re -from module.plugins.internal.SimpleCrypter import SimpleCrypter +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  class DataHuFolder(SimpleCrypter): -    __name__ = "DataHuFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?data.hu/dir/\w+' -    __version__ = "0.03" +    __name__    = "DataHuFolder" +    __type__    = "crypter" +    __version__ = "0.06" + +    __pattern__ = r'http://(?:www\.)?data\.hu/dir/\w+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Data.hu folder decrypter plugin""" -    __author_name__ = ("crash", "stickell") -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("crash", None), +                       ("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>' -    LINK_PATTERN = r"<a href='(http://data\.hu/get/.+)' target='_blank'>\1</a>" -    TITLE_PATTERN = ur'<title>(?P<title>.+) Let\xf6lt\xe9se</title>' -    def decrypt(self, pyfile): -        self.html = self.load(pyfile.url, decode=True) +    def prepare(self): +        super(DataHuFolder, self).prepare()          if u'K\xe9rlek add meg a jelsz\xf3t' in self.html:  # Password protected              password = self.getPassword() -            if password is '': -                self.fail("No password specified, please set right password on Add package form and retry") -            self.logDebug('The folder is password protected', 'Using password: ' + password) -            self.html = self.load(pyfile.url, post={'mappa_pass': password}, decode=True) -            if u'Hib\xe1s jelsz\xf3' in self.html:  # Wrong password -                self.fail("Incorrect password, please set right password on Add package form and retry") +            if not password: +                self.fail(_("Password required")) + +            self.logDebug("The folder is password protected', 'Using password: " + password) -        package_name, folder_name = self.getPackageNameAndFolder() +            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")) -        package_links = re.findall(self.LINK_PATTERN, self.html) -        self.logDebug('Package has %d links' % len(package_links)) -        if package_links: -            self.packages = [(package_name, package_links, folder_name)] -        else: -            self.fail('Could not extract any links') +getInfo = create_getInfo(DataHuFolder) diff --git a/module/plugins/crypter/DdlstorageComFolder.py b/module/plugins/crypter/DdlstorageComFolder.py index 6501de822..e02e77fda 100644 --- a/module/plugins/crypter/DdlstorageComFolder.py +++ b/module/plugins/crypter/DdlstorageComFolder.py @@ -1,29 +1,20 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ -from module.plugins.internal.SimpleCrypter import SimpleCrypter +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo -class DdlstorageComFolder(SimpleCrypter): -    __name__ = "DdlstorageComFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?ddlstorage.com/folder/\w{10}' -    __version__ = "0.02" +class DdlstorageComFolder(DeadCrypter): +    __name__    = "DdlstorageComFolder" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'https?://(?:www\.)?ddlstorage\.com/folder/\w+' +    __config__  = [] +      __description__ = """DDLStorage.com folder decrypter plugin""" -    __author_name__ = ("godofdream", "stickell") -    __author_mail__ = ("soilfiction@gmail.com", "l.stickell@yahoo.it") +    __license__     = "GPLv3" +    __authors__     = [("godofdream", "soilfiction@gmail.com"), +                       ("stickell", "l.stickell@yahoo.it")] + -    LINK_PATTERN = '<a class="sub_title" style="text-decoration:none;" href="(http://www.ddlstorage.com/.*)">' +getInfo = create_getInfo(DdlstorageComFolder) diff --git a/module/plugins/crypter/DepositfilesComFolder.py b/module/plugins/crypter/DepositfilesComFolder.py index 74d199e12..147f093c3 100644 --- a/module/plugins/crypter/DepositfilesComFolder.py +++ b/module/plugins/crypter/DepositfilesComFolder.py @@ -1,15 +1,23 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.SimpleCrypter import SimpleCrypter +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  class DepositfilesComFolder(SimpleCrypter): -    __name__ = "DepositfilesComFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?depositfiles.com/folders/\w+' +    __name__    = "DepositfilesComFolder" +    __type__    = "crypter"      __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?depositfiles\.com/folders/\w+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Depositfiles.com folder decrypter plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] +      LINK_PATTERN = r'<div class="progressName"[^>]*>\s*<a href="([^"]+)" title="[^"]*" target="_blank">' + + +getInfo = create_getInfo(DepositfilesComFolder) diff --git a/module/plugins/crypter/Dereferer.py b/module/plugins/crypter/Dereferer.py index 4b6309c17..0729c8cb6 100644 --- a/module/plugins/crypter/Dereferer.py +++ b/module/plugins/crypter/Dereferer.py @@ -1,35 +1,17 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. +from module.plugins.internal.SimpleDereferer import SimpleDereferer -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. -""" +class Dereferer(SimpleDereferer): +    __name__    = "Dereferer" +    __type__    = "crypter" +    __version__ = "0.11" -import re -import urllib +    __pattern__ = r'https?://([^/]+)/.*?(?P<LINK>(ht|f)tps?(://|%3A%2F%2F).+)' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] -from module.plugins.Crypter import Crypter - - -class Dereferer(Crypter): -    __name__ = "Dereferer" -    __type__ = "crypter" -    __pattern__ = r'https?://([^/]+)/.*?(?P<url>(ht|f)tps?(://|%3A%2F%2F).*)' -    __version__ = "0.1"      __description__ = """Crypter for dereferers""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" - -    def decrypt(self, pyfile): -        link = re.match(self.__pattern__, pyfile.url).group('url') -        self.core.files.addLinks([urllib.unquote(link).rstrip('+')], pyfile.package().id) +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] diff --git a/module/plugins/crypter/DevhostStFolder.py b/module/plugins/crypter/DevhostStFolder.py new file mode 100644 index 000000000..5ac9d4faf --- /dev/null +++ b/module/plugins/crypter/DevhostStFolder.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://d-h.st/users/shine/?fld_id=37263#files + +import re + +from urlparse import urljoin + +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo + + +class DevhostStFolder(SimpleCrypter): +    __name__    = "DevhostStFolder" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?d-h\.st/users/(?P<USER>\w+)(/\?fld_id=(?P<ID>\d+))?' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "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 getFileInfo(self): +        if re.search(self.OFFLINE_PATTERN, self.html): +            self.offline() + +        try: +            id = re.match(self.__pattern__, self.pyfile.url).group('ID') +            if 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.*?">(.+?)<' % id +            m = re.search(p, html) +            name = folder = m.group(1) + +        except Exception, e: +            self.logDebug(e) +            name = folder = re.match(self.__pattern__, self.pyfile.url).group('USER') + +        return {'name': name, 'folder': folder} + + +    def getLinks(self): +        return [urljoin("http://d-h.st", link) for link in re.findall(self.LINK_PATTERN, self.html)] + + +getInfo = create_getInfo(DevhostStFolder) diff --git a/module/plugins/crypter/DlProtectCom.py b/module/plugins/crypter/DlProtectCom.py index dbe5bf705..a99ed0be9 100644 --- a/module/plugins/crypter/DlProtectCom.py +++ b/module/plugins/crypter/DlProtectCom.py @@ -1,45 +1,36 @@  # -*- coding: utf-8 -*- -############################################################################### -#  This program is free software: you can redistribute it and/or modify -#  it under the terms of the GNU Affero General Public License as -#  published by the Free Software Foundation, either version 3 of the -#  License, or (at your option) any later version. -# -#  This program is distributed in the hope that it will be useful, -#  but WITHOUT ANY WARRANTY; without even the implied warranty of -#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -#  GNU Affero General Public License for more details. -# -#  You should have received a copy of the GNU Affero General Public License -#  along with this program.  If not, see <http://www.gnu.org/licenses/>. -# -#  @author: Walter Purcaro -###############################################################################  import re +  from base64 import urlsafe_b64encode  from time import time -from module.plugins.internal.SimpleCrypter import SimpleCrypter +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  class DlProtectCom(SimpleCrypter): -    __name__ = "DlProtectCom" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?dl-protect\.com/((en|fr)/)?(?P<ID>\w+)' +    __name__    = "DlProtectCom" +    __type__    = "crypter"      __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?dl-protect\.com/((en|fr)/)?(?P<ID>\w+)' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Dl-protect.com decrypter plugin""" -    __author_name__ = "Walter Purcaro" -    __author_mail__ = "vuolter@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    OFFLINE_PATTERN = r'>Unfortunately, the link you are looking for is not found' -    OFFLINE_PATTERN = ">Unfortunately, the link you are looking for is not found"      def getLinks(self):          # Direct link with redirect          if not re.match(r"http://(?:www\.)?dl-protect\.com", self.req.http.lastEffectiveURL):              return [self.req.http.lastEffectiveURL] -        #id = re.match(self.__pattern__, self.pyfile.url).group("ID") +        #id = re.match(self.__pattern__, self.pyfile.url).group('ID')          key = re.search(r'name="id_key" value="(.+?)"', self.html).group(1)          post_req = {"id_key": key, "submitform": ""} @@ -55,20 +46,23 @@ class DlProtectCom(SimpleCrypter):              post_req.update({"i": b64time, "submitform": "Decrypt+link"})              if ">Password :" in self.html: -                post_req["pwd"] = self.getPassword() +                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 +                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:]) +                self.fail(_(errmsg[1:]))          pattern = r'<a href="([^/].+?)" target="_blank">'          return re.findall(pattern, self.html) + + +getInfo = create_getInfo(DlProtectCom) diff --git a/module/plugins/crypter/DontKnowMe.py b/module/plugins/crypter/DontKnowMe.py index 42a38e98f..d656cde4c 100644 --- a/module/plugins/crypter/DontKnowMe.py +++ b/module/plugins/crypter/DontKnowMe.py @@ -1,22 +1,17 @@  # -*- coding: utf-8 -*- -import re -import urllib +from module.plugins.internal.SimpleDereferer import SimpleDereferer -from module.plugins.Crypter import Crypter +class DontKnowMe(SimpleDereferer): +    __name__    = "DontKnowMe" +    __type__    = "crypter" +    __version__ = "0.11" -class DontKnowMe(Crypter): -    __name__ = "DontKnowMe" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?dontknow.me/at/\?.+$' -    __version__ = "0.1" -    __description__ = """DontKnow.me decrypter plugin""" -    __author_name__ = "selaux" -    __author_mail__ = "" - -    LINK_PATTERN = r"http://dontknow.me/at/\?(.+)$" +    __pattern__ = r'http://(?:www\.)?dontknow\.me/at/\?(?P<LINK>.+)' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] -    def decrypt(self, pyfile): -        link = re.findall(self.LINK_PATTERN, pyfile.url)[0] -        self.core.files.addLinks([urllib.unquote(link)], pyfile.package().id) +    __description__ = """DontKnow.me decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("selaux", "")] diff --git a/module/plugins/crypter/DownloadVimeoCom.py b/module/plugins/crypter/DownloadVimeoCom.py deleted file mode 100644 index 3e137bab1..000000000 --- a/module/plugins/crypter/DownloadVimeoCom.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- - -import re -import HTMLParser -from module.plugins.Crypter import Crypter - - -class DownloadVimeoCom(Crypter): -    __name__ = 'DownloadVimeoCom' -    __type__ = 'crypter' -    __pattern__ = r'(?:http://vimeo\.com/\d*|http://smotri\.com/video/view/\?id=.*)' -    __version__ = '0.1' -    __description__ = """Vimeo.com decrypter plugin""" -    __author_name__ = "4Christopher" -    __author_mail__ = "4Christopher@gmx.de" - -    BASE_URL = 'http://downloadvimeo.com' - -    def decrypt(self, pyfile): -        self.package = pyfile.package() -        html = self.load('%s/generate?url=%s' % (self.BASE_URL, pyfile.url)) -        h = HTMLParser.HTMLParser() -        try: -            f = re.search(r'cmd quality="(?P<quality>[^"]+?)">\s*?(?P<URL>[^<]*?)</cmd>', html) -        except: -            self.logDebug('Failed to find the URL') -        else: -            url = h.unescape(f.group('URL')) -            self.logDebug('Quality: %s, URL: %s' % (f.group('quality'), url)) -            self.packages.append((self.package.name, [url], self.package.folder)) diff --git a/module/plugins/crypter/DuckCryptInfo.py b/module/plugins/crypter/DuckCryptInfo.py index f44bac2e9..07cc5cdc4 100644 --- a/module/plugins/crypter/DuckCryptInfo.py +++ b/module/plugins/crypter/DuckCryptInfo.py @@ -1,57 +1,59 @@  # -*- coding: utf-8 -*-  import re -from module.lib.BeautifulSoup import BeautifulSoup + +from BeautifulSoup import BeautifulSoup +  from module.plugins.Crypter import Crypter  class DuckCryptInfo(Crypter): -    __name__ = "DuckCryptInfo" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?duckcrypt.info/(folder|wait|link)/(\w+)/?(\w*)' +    __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_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """DuckCrypt.info decrypter plugin""" -    __author_name__ = "godofdream" -    __author_mail__ = "soilfiction@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("godofdream", "soilfiction@gmail.com")] +      TIMER_PATTERN = r'<span id="timer">(.*)</span>' +      def decrypt(self, pyfile):          url = pyfile.url -        # seems we don't need to wait -        #src = self.req.load(str(url)) -        #found = re.search(self.TIMER_PATTERN, src) -        #if found: -        #    self.logDebug("Sleeping for" % found.group(1)) -        #    self.setWait(int(found.group(1)) ,False) -        found = re.match(self.__pattern__, url) -        if not found: -            self.fail('Weird error in link') -        if str(found.group(1)) == "link": + +        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(found) +            self.handleFolder(m) -    def handleFolder(self, found): -        src = self.load("http://duckcrypt.info/ajax/auth.php?hash=" + str(found.group(2))) -        found = re.match(self.__pattern__, src) -        self.logDebug("Redirectet to " + str(found.group(0))) -        src = self.load(str(found.group(0))) -        soup = BeautifulSoup(src) +    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.fail('no links found - (Plugin out of date?)') +            self.error(_("No link found"))          for clink in cryptlinks:              if clink.find("a"):                  self.handleLink(clink.find("a")['href']) +      def handleLink(self, url): -        src = self.load(url) -        soup = BeautifulSoup(src) -        link = soup.find("iframe")["src"] -        if not link: -            self.logDebug('no links found - (Plugin out of date?)') -        else: -            self.core.files.addLinks([link], self.pyfile.package().id) +        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/module/plugins/crypter/DuploadOrgFolder.py b/module/plugins/crypter/DuploadOrgFolder.py index 4913bdf2e..066fbe3d7 100644 --- a/module/plugins/crypter/DuploadOrgFolder.py +++ b/module/plugins/crypter/DuploadOrgFolder.py @@ -1,29 +1,19 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ -from module.plugins.internal.SimpleCrypter import SimpleCrypter +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo -class DuploadOrgFolder(SimpleCrypter): -    __name__ = "DuploadOrgFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?dupload\.org/folder/\d+/' -    __version__ = "0.01" +class DuploadOrgFolder(DeadCrypter): +    __name__    = "DuploadOrgFolder" +    __type__    = "crypter" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?dupload\.org/folder/\d+' +    __config__  = [] +      __description__ = """Dupload.org folder decrypter plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + -    LINK_PATTERN = r'<td style="[^"]+"><a href="(http://[^"]+)" target="_blank">[^<]+</a></td>' +getInfo = create_getInfo(DuploadOrgFolder) diff --git a/module/plugins/crypter/EasybytezComFolder.py b/module/plugins/crypter/EasybytezComFolder.py index b4a6284fc..04f9b853b 100644 --- a/module/plugins/crypter/EasybytezComFolder.py +++ b/module/plugins/crypter/EasybytezComFolder.py @@ -1,32 +1,25 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -from module.plugins.internal.SimpleCrypter import SimpleCrypter - - -class EasybytezComFolder(SimpleCrypter): -    __name__ = "EasybytezComFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?easybytez\.com/users/(?P<ID>\d+/\d+)' -    __version__ = "0.05" -    __description__ = """Easybytez.com decrypter plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" - -    FILE_URL_REPLACEMENTS = [(__pattern__, r"http://www.easybytez.com/users/\g<ID>?per_page=10000")] - -    LINK_PATTERN = r'<td><a href="(http://www\.easybytez\.com/\w+)" target="_blank">.+(?:</a>)?</td>' -    TITLE_PATTERN = r'<Title>Files of \d+: (?P<title>.+) folder</Title>' + +from module.plugins.internal.XFSCrypter import XFSCrypter, create_getInfo + + +class EasybytezComFolder(XFSCrypter): +    __name__    = "EasybytezComFolder" +    __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_package", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Easybytez.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    HOSTER_DOMAIN = "easybytez.com" + +    LOGIN_ACCOUNT = True + + +getInfo = create_getInfo(EasybytezComFolder) diff --git a/module/plugins/crypter/EmbeduploadCom.py b/module/plugins/crypter/EmbeduploadCom.py index fbccf71aa..be3181793 100644 --- a/module/plugins/crypter/EmbeduploadCom.py +++ b/module/plugins/crypter/EmbeduploadCom.py @@ -6,48 +6,55 @@ from module.network.HTTPRequest import BadHeader  class EmbeduploadCom(Crypter): -    __name__ = "EmbeduploadCom" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?embedupload.com/\?d=.*' +    __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_package", "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""" -    __config__ = [("preferedHoster", "str", "Prefered hoster list (bar-separated) ", "embedupload"), -                  ("ignoredHoster", "str", "Ignored hoster list (bar-separated) ", "")] -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __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 = [] -        new_links = [] -        found = re.findall(self.LINK_PATTERN, self.html) -        if found: +        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) -            print "PF", prefered_set -            tmp_links.extend([x[1] for x in found if x[0] in prefered_set]) -            self.getLocation(tmp_links, new_links) -            if not new_links: +            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) -                print "IG", ignored_set -                tmp_links.extend([x[1] for x in found if x[0] not in ignored_set]) -                self.getLocation(tmp_links, new_links) -        if new_links: -            self.core.files.addLinks(new_links, pyfile.package().id) -        else: -            self.fail('Could not extract any links') +                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): + +    def getLocation(self, tmp_links): +        new_links = []          for link in tmp_links:              try:                  header = self.load(link, just_header=True) -                if "location" in header: +                if 'location' in header:                      new_links.append(header['location'])              except BadHeader:                  pass +        return new_links diff --git a/module/plugins/crypter/FilebeerInfoFolder.py b/module/plugins/crypter/FilebeerInfoFolder.py index f5818e072..a3c7ee74c 100644 --- a/module/plugins/crypter/FilebeerInfoFolder.py +++ b/module/plugins/crypter/FilebeerInfoFolder.py @@ -1,13 +1,19 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.DeadCrypter import DeadCrypter +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo  class FilebeerInfoFolder(DeadCrypter): -    __name__ = "FilebeerInfoFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?filebeer\.info/(\d+~f).*' +    __name__    = "FilebeerInfoFolder" +    __type__    = "crypter"      __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?filebeer\.info/\d*~f\w+' +    __config__  = [] +      __description__ = """Filebeer.info folder decrypter plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +getInfo = create_getInfo(FilebeerInfoFolder) diff --git a/module/plugins/crypter/FilecloudIoFolder.py b/module/plugins/crypter/FilecloudIoFolder.py new file mode 100644 index 000000000..83cce352d --- /dev/null +++ b/module/plugins/crypter/FilecloudIoFolder.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo + + +class FilecloudIoFolder(SimpleCrypter): +    __name__    = "FilecloudIoFolder" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'https?://(?:www\.)?(filecloud\.io|ifile\.it)/_\w+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "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<' + + +getInfo = create_getInfo(FilecloudIoFolder) diff --git a/module/plugins/crypter/FilecryptCc.py b/module/plugins/crypter/FilecryptCc.py new file mode 100644 index 000000000..59960ab24 --- /dev/null +++ b/module/plugins/crypter/FilecryptCc.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- + +import base64 +import binascii +import re + +from Crypto.Cipher import AES +from urlparse import urljoin + +from module.plugins.Crypter import Crypter +from module.plugins.internal.CaptchaService import ReCaptcha + + +class FilecryptCc(Crypter): +    __name__    = "FilecryptCc" +    __type__    = "crypter" +    __version__ = "0.08" + +    __pattern__ = r'https?://(?:www\.)?filecrypt\.cc/Container/\w+' + +    __description__ = """Filecrypt.cc decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "")] + + +    # 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, cookies=True) + +        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, cookies=True).decode("utf-8", "replace") + + +    def handlePasswordProtection(self): +        if '<input type="text" name="password"' not in self.html: +            return + +        self.logInfo(_("Folder is password protected")) + +        if not self.pyfile.package().password: +            self.fail(_("Please enter the password in package section and try again")) + +        self.html = self.load(self.pyfile.url, post={"password": self.password}, cookies=True) + + +    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}, +                                           cookies=True, +                                           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]}, +                                           cookies=True, +                                           decode=True) +        else: +            recaptcha   = ReCaptcha(self) +            captcha_key = recaptcha.detect_key() + +            if captcha_key: +                self.siteWithLinks = self.load(self.pyfile.url, +                                               post={'g-recaptcha-response': recaptcha.challenge(captcha_key, True)}, +                                               cookies=True, +                                               decode=True) +            else: +                self.logDebug("No captcha found") +                self.siteWithLinks = self.html + +        if "recaptcha_image" 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, cookies=True) +                link2 = re.search('<iframe noresize src="(.*)"></iframe>', res) +                res2  = self.load(link2.group(1), just_header=True, cookies=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)) + +        # Decode crypted +        crypted = base64.standard_b64decode(crypted) + +        # Decrypt +        Key  = key +        IV   = key +        obj  = AES.new(Key, AES.MODE_CBC, IV) +        text = obj.decrypt(crypted) + +        # Extract links +        links = filter(lambda x: x != "", +                       text.replace("\x00", "").replace("\r", "").split("\n")) + +        return links diff --git a/module/plugins/crypter/FilefactoryComFolder.py b/module/plugins/crypter/FilefactoryComFolder.py index aece1a01d..26e28acbd 100644 --- a/module/plugins/crypter/FilefactoryComFolder.py +++ b/module/plugins/crypter/FilefactoryComFolder.py @@ -1,45 +1,31 @@  # -*- coding: utf-8 -*- -import re -from module.plugins.Crypter import Crypter +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo -class FilefactoryComFolder(Crypter): -    __name__ = "FilefactoryComFolder" -    __type__ = "crypter" -    __pattern__ = r'(http://(?:www\.)?filefactory\.com/f/\w+).*' -    __version__ = "0.1" +class FilefactoryComFolder(SimpleCrypter): +    __name__    = "FilefactoryComFolder" +    __type__    = "crypter" +    __version__ = "0.31" + +    __pattern__ = r'https?://(?:www\.)?filefactory\.com/(?:f|folder)/\w+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Filefactory.com folder decrypter plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" - -    FOLDER_PATTERN = r'<table class="items" cellspacing="0" cellpadding="0">(.*?)</table>' -    LINK_PATTERN = r'<td class="name"><a href="([^"]+)">' -    PAGINATOR_PATTERN = r'<div class="list">\s*<label>Pages</label>\s*<ul>(.*?)</ul>\s*</div>' -    NEXT_PAGE_PATTERN = r'<li class="current">.*?</li>\s*<li class=""><a href="([^"]+)">' - -    def decrypt(self, pyfile): -        url_base = re.match(self.__pattern__, pyfile.url).group(1) -        html = self.load(url_base) - -        new_links = [] -        for i in xrange(1, 100): -            self.logInfo("Fetching links from page %i" % i) -            found = re.search(self.FOLDER_PATTERN, html, re.DOTALL) -            if found is None: self.fail("Parse error (FOLDER)") - -            new_links.extend(re.findall(self.LINK_PATTERN, found.group(1))) - -            try: -                paginator = re.search(self.PAGINATOR_PATTERN, html, re.DOTALL).group(1) -                next_page = re.search(self.NEXT_PAGE_PATTERN, paginator).group(1) -                html = self.load("%s/%s" % (url_base, next_page)) -            except Exception, e: -                break -        else: -            self.logInfo("Limit of 99 pages reached, aborting") - -        if new_links: -            self.core.files.addLinks(new_links, pyfile.package().id) -        else: -            self.fail('Could not extract any links') +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    LINK_PATTERN = r'<td><a href="([^"]+)">' +    NAME_PATTERN = r'<h1>Files in <span>(?P<N>.+)</span></h1>' +    PAGES_PATTERN = r'data-paginator-totalPages="(\d+)"' + +    COOKIES = [("filefactory.com", "locale", "en_US.utf8")] + + +    def loadPage(self, page_n): +        return self.load(self.pyfile.url, get={'page': page_n}) + + +getInfo = create_getInfo(FilefactoryComFolder) diff --git a/module/plugins/crypter/FilerNetFolder.py b/module/plugins/crypter/FilerNetFolder.py new file mode 100644 index 000000000..00db173bb --- /dev/null +++ b/module/plugins/crypter/FilerNetFolder.py @@ -0,0 +1,29 @@ +import re + +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo + + +class FilerNetFolder(SimpleCrypter): +    __name__    = "FilerNetFolder" +    __type__    = "crypter" +    __version__ = "0.41" + +    __pattern__ = r'https?://filer\.net/folder/\w{16}' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "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' + + +    def getLinks(self): +        return ['http://filer.net%s' % link for link in re.findall(self.LINK_PATTERN, self.html)] + + +getInfo = create_getInfo(FilerNetFolder) diff --git a/module/plugins/crypter/FileserveComFolder.py b/module/plugins/crypter/FileserveComFolder.py index c3c8b58fe..e6b35fd36 100644 --- a/module/plugins/crypter/FileserveComFolder.py +++ b/module/plugins/crypter/FileserveComFolder.py @@ -6,28 +6,33 @@ from module.plugins.Crypter import Crypter  class FileserveComFolder(Crypter): -    __name__ = "FileserveComFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?fileserve.com/list/\w+' +    __name__    = "FileserveComFolder" +    __type__    = "crypter"      __version__ = "0.11" + +    __pattern__ = r'http://(?:www\.)?fileserve\.com/list/\w+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """FileServe.com folder decrypter plugin""" -    __author_name__ = "fionnc" -    __author_mail__ = "fionnc@gmail.com" +    __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.DOTALL) -        if folder is None: self.fail("Parse error (FOLDER)") +        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.core.files.addLinks(map(lambda s: "http://fileserve.com%s" % s, new_links), pyfile.package().id) -        else: -            self.fail('Could not extract any links') +            self.urls = [map(lambda s: "http://fileserve.com%s" % s, new_links)] diff --git a/module/plugins/crypter/FilesonicComFolder.py b/module/plugins/crypter/FilesonicComFolder.py new file mode 100644 index 000000000..d58516986 --- /dev/null +++ b/module/plugins/crypter/FilesonicComFolder.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo + + +class FilesonicComFolder(DeadCrypter): +    __name__    = "FilesonicComFolder" +    __type__    = "crypter" +    __version__ = "0.12" + +    __pattern__ = r'http://(?:www\.)?filesonic\.com/folder/\w+' + +    __description__ = """Filesonic.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +getInfo = create_getInfo(FilesonicComFolder) diff --git a/module/plugins/crypter/FilestubeCom.py b/module/plugins/crypter/FilestubeCom.py index f4f09e878..16ebdda37 100644 --- a/module/plugins/crypter/FilestubeCom.py +++ b/module/plugins/crypter/FilestubeCom.py @@ -1,30 +1,24 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -from module.plugins.internal.SimpleCrypter import SimpleCrypter + +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  class FilestubeCom(SimpleCrypter): -    __name__ = "FilestubeCom" -    __type__ = "crypter" +    __name__    = "FilestubeCom" +    __type__    = "crypter" +    __version__ = "0.05" +      __pattern__ = r'http://(?:www\.)?filestube\.(?:com|to)/\w+' -    __version__ = "0.03" +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Filestube.com decrypter plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __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>' + -    LINK_PATTERN = r"<a class=\"file-link-main(?: noref)?\" [^>]* href=\"(http://[^\"]+)" -    TITLE_PATTERN = r"<h1\s*> (?P<title>.+)  download\s*</h1>" +getInfo = create_getInfo(FilestubeCom) diff --git a/module/plugins/crypter/FiletramCom.py b/module/plugins/crypter/FiletramCom.py index 8cdf32ad5..76530c589 100644 --- a/module/plugins/crypter/FiletramCom.py +++ b/module/plugins/crypter/FiletramCom.py @@ -1,30 +1,25 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -from module.plugins.internal.SimpleCrypter import SimpleCrypter + +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  class FiletramCom(SimpleCrypter): -    __name__ = "FiletramCom" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?filetram.com/[^/]+/.+' -    __version__ = "0.01" +    __name__    = "FiletramCom" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?filetram\.com/[^/]+/.+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Filetram.com decrypter plugin""" -    __author_name__ = ("igel", "stickell") -    __author_mail__ = ("igelkun@myopera.com", "l.stickell@yahoo.it") +    __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' + -    LINK_PATTERN = r"\s+(http://.+)" -    TITLE_PATTERN = r"<title>(?P<title>[^<]+) - Free Download[^<]*</title>" +getInfo = create_getInfo(FiletramCom) diff --git a/module/plugins/crypter/FiredriveComFolder.py b/module/plugins/crypter/FiredriveComFolder.py new file mode 100644 index 000000000..7d3a357fd --- /dev/null +++ b/module/plugins/crypter/FiredriveComFolder.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo + + +class FiredriveComFolder(DeadCrypter): +    __name__    = "FiredriveComFolder" +    __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")] + + +getInfo = create_getInfo(FiredriveComFolder) diff --git a/module/plugins/crypter/FourChanOrg.py b/module/plugins/crypter/FourChanOrg.py index 010451c46..d6c5c86cc 100644 --- a/module/plugins/crypter/FourChanOrg.py +++ b/module/plugins/crypter/FourChanOrg.py @@ -1,4 +1,6 @@  # -*- coding: utf-8 -*- +# +# Based on 4chandl by Roland Beermann (https://gist.github.com/enkore/3492599)  import re @@ -6,20 +8,20 @@ from module.plugins.Crypter import Crypter  class FourChanOrg(Crypter): -    # Based on 4chandl by Roland Beermann -    # https://gist.github.com/enkore/3492599 -    __name__ = "FourChanOrg" -    __type__ = "crypter" -    __version__ = "0.3" -    __pattern__ = r'http://(?:www\.)?boards\.4chan.org/\w+/res/(\d+)' +    __name__    = "FourChanOrg" +    __type__    = "crypter" +    __version__ = "0.30" + +    __pattern__ = r'http://(?:www\.)?boards\.4chan\.org/\w+/res/(\d+)' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "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)) -        urls = [] -        for image in images: -            urls.append("http://" + image) - -        self.core.files.addLinks(urls, pyfile.package().id) +        self.urls = ["http://" + image for image in images] diff --git a/module/plugins/crypter/FreakhareComFolder.py b/module/plugins/crypter/FreakhareComFolder.py index c5df7ff17..7c1b7de2b 100644 --- a/module/plugins/crypter/FreakhareComFolder.py +++ b/module/plugins/crypter/FreakhareComFolder.py @@ -1,36 +1,28 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################  import re -from module.plugins.internal.SimpleCrypter import SimpleCrypter +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  class FreakhareComFolder(SimpleCrypter): -    __name__ = "FreakhareComFolder" -    __type__ = "crypter" +    __name__    = "FreakhareComFolder" +    __type__    = "crypter" +    __version__ = "0.03" +      __pattern__ = r'http://(?:www\.)?freakshare\.com/folder/.+' -    __version__ = "0.01" +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Freakhare.com folder decrypter plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __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+)' -    LINK_PATTERN = r'<a href="(http://freakshare.com/files/[^"]+)" target="_blank">' -    TITLE_PATTERN = r'Folder:</b> (?P<title>.+)' -    PAGES_PATTERN = r'Pages: +(?P<pages>\d+)'      def loadPage(self, page_n):          if not hasattr(self, 'f_id') and not hasattr(self, 'f_md5'): @@ -44,3 +36,6 @@ class FreakhareComFolder(SimpleCrypter):                                                          'entrys': '20',                                                          'page': page_n - 1,                                                          'order': ''}, decode=True) + + +getInfo = create_getInfo(FreakhareComFolder) diff --git a/module/plugins/crypter/FreetexthostCom.py b/module/plugins/crypter/FreetexthostCom.py index e5c9c3d21..c33c9ff64 100644 --- a/module/plugins/crypter/FreetexthostCom.py +++ b/module/plugins/crypter/FreetexthostCom.py @@ -1,36 +1,30 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################  import re -from module.plugins.internal.SimpleCrypter import SimpleCrypter +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  class FreetexthostCom(SimpleCrypter): -    __name__ = "FreetexthostCom" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?freetexthost\.com/\w+' +    __name__    = "FreetexthostCom" +    __type__    = "crypter"      __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?freetexthost\.com/\w+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Freetexthost.com decrypter plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __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.DOTALL) -        if not m: -            self.fail('Unable to extract links | Plugin may be out-of-date') +        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") + + +getInfo = create_getInfo(FreetexthostCom) diff --git a/module/plugins/crypter/FshareVnFolder.py b/module/plugins/crypter/FshareVnFolder.py index 6e67905d9..474364e40 100644 --- a/module/plugins/crypter/FshareVnFolder.py +++ b/module/plugins/crypter/FshareVnFolder.py @@ -1,15 +1,23 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.SimpleCrypter import SimpleCrypter +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  class FshareVnFolder(SimpleCrypter): -    __name__ = "FshareVnFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?fshare.vn/folder/.*' +    __name__    = "FshareVnFolder" +    __type__    = "crypter"      __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?fshare\.vn/folder/.+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Fshare.vn folder decrypter plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] +      LINK_PATTERN = r'<li class="w_80pc"><a href="([^"]+)" target="_blank">' + + +getInfo = create_getInfo(FshareVnFolder) diff --git a/module/plugins/crypter/Go4UpCom.py b/module/plugins/crypter/Go4UpCom.py new file mode 100644 index 000000000..102bc32b5 --- /dev/null +++ b/module/plugins/crypter/Go4UpCom.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo + + +class Go4UpCom(SimpleCrypter): +    __name__    = "Go4UpCom" +    __type__    = "crypter" +    __version__ = "0.11" + +    __pattern__ = r'http://go4up\.com/(dl/\w{12}|rd/\w{12}/\d+)' + +    __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: +                continue + +        return links + + +getInfo = create_getInfo(Go4UpCom) diff --git a/module/plugins/crypter/GooGl.py b/module/plugins/crypter/GooGl.py index 15f0a9a8c..d548a3375 100644 --- a/module/plugins/crypter/GooGl.py +++ b/module/plugins/crypter/GooGl.py @@ -1,40 +1,32 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################  from module.plugins.Crypter import Crypter  from module.common.json_layer import json_loads  class GooGl(Crypter): -    __name__ = "GooGl" -    __type__ = "crypter" -    __pattern__ = r'https?://(?:www\.)?goo\.gl/\w+' +    __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_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Goo.gl decrypter plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    API_URL = "https://www.googleapis.com/urlshortener/v1/url" -    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) +        self.logDebug("JSON data: " + rep)          rep = json_loads(rep)          if 'longUrl' in rep: -            self.core.files.addLinks([rep['longUrl']], pyfile.package().id) +            self.urls = [rep['longUrl']]          else: -            self.fail('Unable to expand shortened link') +            self.fail(_("Unable to expand shortened link")) diff --git a/module/plugins/crypter/HoerbuchIn.py b/module/plugins/crypter/HoerbuchIn.py index c6773b3f0..a347e4232 100644 --- a/module/plugins/crypter/HoerbuchIn.py +++ b/module/plugins/crypter/HoerbuchIn.py @@ -2,52 +2,59 @@  import re +from BeautifulSoup import BeautifulSoup, BeautifulStoneSoup +  from module.plugins.Crypter import Crypter -from module.lib.BeautifulSoup import BeautifulSoup, BeautifulStoneSoup  class HoerbuchIn(Crypter): -    __name__ = "HoerbuchIn" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?hoerbuch\.in/(wp/horbucher/\d+/.+/|tp/out.php\?.+|protection/folder_\d+\.html)' -    __version__ = "0.6" +    __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_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Hoerbuch.in decrypter plugin""" -    __author_name__ = ("spoob", "mkaay") -    __author_mail__ = ("spoob@pyload.org", "mkaay@mkaay.de") +    __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): -            src = self.load(pyfile.url) -            soup = BeautifulSoup(src, convertEntities=BeautifulStoneSoup.HTML_ENTITIES) +            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"]) +                links = self.decryptFolder(a['href']) -                self.packages.append((package, links, pyfile.package().folder)) +                self.packages.append((package, links, package))          else: -            links = self.decryptFolder(pyfile.url) +            self.urls = self.decryptFolder(pyfile.url) -            self.packages.append((pyfile.package().name, links, pyfile.package().folder))      def decryptFolder(self, url):          m = self.protection.search(url) -        if not m: -            self.fail("Bad URL") +        if m is None: +            self.fail(_("Bad URL"))          url = m.group(0)          self.pyfile.url = url -        src = self.req.load(url, post={"viewed": "adpg"}) +        html = self.load(url, post={"viewed": "adpg"})          links = []          pattern = re.compile("http://www\.hoerbuch\.in/protection/(\w+)/(.*?)\"") -        for hoster, lid in pattern.findall(src): +        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) diff --git a/module/plugins/crypter/HotfileComFolder.py b/module/plugins/crypter/HotfileComFolder.py new file mode 100644 index 000000000..4f40587ad --- /dev/null +++ b/module/plugins/crypter/HotfileComFolder.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo + + +class HotfileComFolder(DeadCrypter): +    __name__    = "HotfileComFolder" +    __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")] + + +getInfo = create_getInfo(HotfileComFolder) diff --git a/module/plugins/crypter/HotfileFolderCom.py b/module/plugins/crypter/HotfileFolderCom.py deleted file mode 100644 index 3a4a9d70a..000000000 --- a/module/plugins/crypter/HotfileFolderCom.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- - -import re - -from module.plugins.Crypter import Crypter - - -class HotfileFolderCom(Crypter): -    __name__ = "HotfileFolderCom" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?hotfile.com/list/\w+/\w+' -    __version__ = "0.1" -    __description__ = """Hotfile.com folder decrypter plugin""" -    __author_name__ = "RaNaN" -    __author_mail__ = "RaNaN@pyload.org" - -    def decrypt(self, pyfile): -        html = self.load(pyfile.url) - -        name = re.findall( -            r'<img src="/i/folder.gif" width="23" height="14" style="margin-bottom: -2px;" />([^<]+)', html, -            re.MULTILINE)[0].replace("/", "") -        new_links = re.findall(r'href="(http://(www.)?hotfile\.com/dl/\d+/[0-9a-zA-Z]+[^"]+)', html) - -        new_links = [x[0] for x in new_links] - -        self.packages.append((name, new_links, name)) diff --git a/module/plugins/crypter/ILoadTo.py b/module/plugins/crypter/ILoadTo.py index 3a388d41f..f3415706d 100644 --- a/module/plugins/crypter/ILoadTo.py +++ b/module/plugins/crypter/ILoadTo.py @@ -1,13 +1,19 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.DeadCrypter import DeadCrypter +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo  class ILoadTo(DeadCrypter): -    __name__ = "ILoadTo" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?iload\.to/go/\d+-[\w\.-]+/' +    __name__    = "ILoadTo" +    __type__    = "crypter"      __version__ = "0.11" + +    __pattern__ = r'http://(?:www\.)?iload\.to/go/\d+-[\w.-]+/' +    __config__  = [] +      __description__ = """Iload.to decrypter plugin""" -    __author_name__ = "hzpz" -    __author_mail__ = "" +    __license__     = "GPLv3" +    __authors__     = [("hzpz", None)] + + +getInfo = create_getInfo(ILoadTo) diff --git a/module/plugins/crypter/ImgurComAlbum.py b/module/plugins/crypter/ImgurComAlbum.py new file mode 100644 index 000000000..6c074f5f1 --- /dev/null +++ b/module/plugins/crypter/ImgurComAlbum.py @@ -0,0 +1,30 @@ +import re + +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo +from module.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_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "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))) + + +getInfo = create_getInfo(ImgurComAlbum) diff --git a/module/plugins/crypter/JunocloudMeFolder.py b/module/plugins/crypter/JunocloudMeFolder.py new file mode 100644 index 000000000..990f25902 --- /dev/null +++ b/module/plugins/crypter/JunocloudMeFolder.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.XFSCrypter import XFSCrypter, create_getInfo + + +class JunocloudMeFolder(XFSCrypter): +    __name__    = "JunocloudMeFolder" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?junocloud\.me/folders/(?P<ID>\d+/\w+)' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Junocloud.me folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("guidobelix", "guidobelix@hotmail.it")] + + +    HOSTER_DOMAIN = "junocloud.me" + + +getInfo = create_getInfo(JunocloudMeFolder) diff --git a/module/plugins/crypter/LetitbitNetFolder.py b/module/plugins/crypter/LetitbitNetFolder.py index 8e994984b..56ecbc7f8 100644 --- a/module/plugins/crypter/LetitbitNetFolder.py +++ b/module/plugins/crypter/LetitbitNetFolder.py @@ -5,29 +5,29 @@ from module.plugins.Crypter import Crypter  class LetitbitNetFolder(Crypter): -    __name__ = "LetitbitNetFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?letitbit.net/folder/\w+' -    __version__ = "0.1" +    __name__    = "LetitbitNetFolder" +    __type__    = "crypter" +    __version__ = "0.10" + +    __pattern__ = r'http://(?:www\.)?letitbit\.net/folder/\w+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Letitbit.net folder decrypter plugin""" -    __author_name__ = ("DHMH", "z00nx") -    __author_mail__ = ("webmaster@pcProfil.de", "z00nx0@gmail.com") +    __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) -        new_links = [] - -        folder = re.search(self.FOLDER_PATTERN, html, re.DOTALL) +        folder = re.search(self.FOLDER_PATTERN, html, re.S)          if folder is None: -            self.fail("Parse error (FOLDER)") - -        new_links.extend(re.findall(self.LINK_PATTERN, folder.group(0))) +            self.error(_("FOLDER_PATTERN not found")) -        if new_links: -            self.core.files.addLinks(new_links, pyfile.package().id) -        else: -            self.fail('Could not extract any links') +        self.urls.extend(re.findall(self.LINK_PATTERN, folder.group(0))) diff --git a/module/plugins/crypter/LinkCryptWs.py b/module/plugins/crypter/LinkCryptWs.py new file mode 100644 index 000000000..996a92c7b --- /dev/null +++ b/module/plugins/crypter/LinkCryptWs.py @@ -0,0 +1,327 @@ +# -*- coding: utf-8 -*- + +import base64 +import binascii +import re + +import pycurl + +from Crypto.Cipher import AES + +from module.plugins.Crypter import Crypter +from module.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", None), +                       ("Gummibaer", None)] + + +    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: +            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) + +        # Decode crypted +        crypted = base64.standard_b64decode(crypted) + +        # Decrypt +        Key  = key +        IV   = key +        obj  = AES.new(Key, AES.MODE_CBC, IV) +        text = obj.decrypt(crypted) + +        # Extract links +        text  = text.replace("\x00", "").replace("\r", "") +        links = text.split("\n") +        links = filter(lambda x: x != "", links) + +        # Log and return +        self.logDebug("Package has %d links" % len(links)) + +        return links diff --git a/module/plugins/crypter/LinkSaveIn.py b/module/plugins/crypter/LinkSaveIn.py index 85dde6c87..e96d0438e 100644 --- a/module/plugins/crypter/LinkSaveIn.py +++ b/module/plugins/crypter/LinkSaveIn.py @@ -1,228 +1,22 @@  # -*- coding: utf-8 -*- -# -# v2.01 - hagg -# * cnl2 and web links are skipped if JS is not available (instead of failing the package) -# * only best available link source is used (priority: cnl2>rsdf>ccf>dlc>web -# +from module.plugins.internal.SimpleDereferer import SimpleDereferer -import base64 -import binascii -import re -from Crypto.Cipher import AES -from module.plugins.Crypter import Crypter -from module.unescape import unescape +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_package", "bool", "Create a subfolder for each package", True)] -class LinkSaveIn(Crypter): -    __name__ = "LinkSaveIn" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?linksave.in/(?P<id>\w+)$' -    __version__ = "2.01"      __description__ = """LinkSave.in decrypter plugin""" -    __author_name__ = "fragonib" -    __author_mail__ = "fragonib[AT]yahoo[DOT]es" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] -    # Constants -    _JK_KEY_ = "jk" -    _CRYPTED_KEY_ = "crypted" -    HOSTER_DOMAIN = "linksave.in" -    def setup(self): -        self.html = None -        self.fileid = None -        self.captcha = False -        self.package = None -        self.preferred_sources = ['cnl2', 'rsdf', 'ccf', 'dlc', 'web'] +    COOKIES = [("linksave.in", "Linksave_Language", "english")] -    def decrypt(self, pyfile): - -        # Init -        self.package = pyfile.package() -        self.fileid = re.match(self.__pattern__, pyfile.url).group('id') -        self.req.cj.setCookie(self.HOSTER_DOMAIN, "Linksave_Language", "english") - -        # Request package -        self.html = self.load(pyfile.url) -        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 type_ in self.preferred_sources: -            package_links.extend(self.handleLinkSource(type_)) -            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)] -        else: -            self.fail('Could not extract any links') - -    def isOnline(self): -        if "<big>Error 404 - Folder not found!</big>" in self.html: -            self.logDebug("File not found") -            return False -        return True - -    def isPasswordProtected(self): -        if re.search(r'''<input.*?type="password"''', self.html): -            self.logDebug("Links are password protected") -            return True - -    def isCaptchaProtected(self): -        if "<b>Captcha:</b>" 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) -        post = {"id": self.fileid, "besucherpasswort": password, 'login': 'submit'} -        self.html = self.load(self.pyfile.url, post=post) - -    def unlockCaptchaProtection(self): -        captcha_hash = re.search(r'name="hash" value="([^"]+)', self.html).group(1) -        captcha_url = re.search(r'src=".(/captcha/cap.php\?hsh=[^"]+)', self.html).group(1) -        captcha_code = self.decryptCaptcha("http://linksave.in" + captcha_url, forceUser=True) -        self.html = self.load(self.pyfile.url, post={"id": self.fileid, "hash": captcha_hash, "code": captcha_code}) - -    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 handleErrors(self): -        if "The visitorpassword you have entered is 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 "Wrong code. Please retry" in self.html: -                self.logDebug("Invalid captcha, retrying") -                self.invalidCaptcha() -                self.retry() -            else: -                self.correctCaptcha() - -    def handleLinkSource(self, type_): -        if type_ == 'cnl2': -            return self.handleCNL2() -        elif type_ in ('rsdf', 'ccf', 'dlc'): -            return self.handleContainer(type_) -        elif type_ == 'web': -            return self.handleWebLinks() -        else: -            self.fail('unknown source type "%s" (this is probably a bug)' % type_) - -    def handleWebLinks(self): -        package_links = [] -        self.logDebug("Search for Web links") -        if not self.js: -            self.logDebug("no JS -> skip Web links") -        else: -        #@TODO: Gather paginated web links -            pattern = r'<a href="http://linksave\.in/(\w{43})"' -            ids = re.findall(pattern, self.html) -            self.logDebug("Decrypting %d Web links" % len(ids)) -            for i, weblink_id in enumerate(ids): -                try: -                    webLink = "http://linksave.in/%s" % weblink_id -                    self.logDebug("Decrypting Web link %d, %s" % (i + 1, webLink)) -                    fwLink = "http://linksave.in/fw-%s" % weblink_id -                    response = self.load(fwLink) -                    jscode = re.findall(r'<script type="text/javascript">(.*)</script>', response)[-1] -                    jseval = self.js.eval("document = { write: function(e) { return e; } }; %s" % jscode) -                    dlLink = re.search(r'http://linksave\.in/dl-\w+', jseval).group(0) -                    self.logDebug("JsEngine returns value [%s] for redirection link" % dlLink) -                    response = self.load(dlLink) -                    link = unescape(re.search(r'<iframe src="(.+?)"', response).group(1)) -                    package_links.append(link) -                except Exception, detail: -                    self.logDebug("Error decrypting Web link %s, %s" % (webLink, detail)) -        return package_links - -    def handleContainer(self, type_): -        package_links = [] -        type_ = type_.lower() -        self.logDebug('Seach 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" (this is probably a bug)' % type_) -        pattern = r"\('%s_link'\).href=unescape\('(.*?\.%s)'\)" % (type_, type_) -        containersLinks = re.findall(pattern, self.html) -        self.logDebug("Found %d %s Container links" % (len(containersLinks), type_.upper())) -        for containerLink in containersLinks: -            link = "http://linksave.in/%s" % unescape(containerLink) -            package_links.append(link) -        return package_links - -    def handleCNL2(self): -        package_links = [] -        self.logDebug("Search for CNL2 links") -        if not self.js: -            self.logDebug("no JS -> skip CNL2 links") -        elif 'cnl2_load' in self.html: -            try: -                (vcrypted, vjk) = self._getCipherParams() -                for (crypted, jk) in zip(vcrypted, vjk): -                    package_links.extend(self._getLinks(crypted, jk)) -            except: -                self.fail("Unable to decrypt CNL2 links") -        return package_links - -    def _getCipherParams(self): - -        # Get jk -        jk_re = r'<INPUT.*?NAME="%s".*?VALUE="(.*?)"' % LinkSaveIn._JK_KEY_ -        vjk = re.findall(jk_re, self.html) - -        # Get crypted -        crypted_re = r'<INPUT.*?NAME="%s".*?VALUE="(.*?)"' % LinkSaveIn._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) - -        # Decode crypted -        crypted = base64.standard_b64decode(crypted) - -        # Decrypt -        Key = key -        IV = key -        obj = AES.new(Key, AES.MODE_CBC, IV) -        text = obj.decrypt(crypted) - -        # Extract links -        text = text.replace("\x00", "").replace("\r", "") -        links = text.split("\n") -        links = filter(lambda x: x != "", links) - -        # Log and return -        self.logDebug("Package has %d links" % len(links)) -        return links +    OFFLINE_PATTERN = r'>(Error )?404 -' diff --git a/module/plugins/crypter/LinkdecrypterCom.py b/module/plugins/crypter/LinkdecrypterCom.py index a6e6faa40..7eb5d3096 100644 --- a/module/plugins/crypter/LinkdecrypterCom.py +++ b/module/plugins/crypter/LinkdecrypterCom.py @@ -1,89 +1,79 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re  from module.plugins.Crypter import Crypter  class LinkdecrypterCom(Crypter): -    __name__ = "LinkdecrypterCom" -    __type__ = "crypter" -    __version__ = "0.27" -    __description__ = """Linkdecrypter.com""" -    __author_name__ = ("zoidberg", "flowlee") -    __author_mail__ = ("zoidberg@mujmail.cz", "") +    __name__    = "LinkdecrypterCom" +    __type__    = "crypter" +    __version__ = "0.28" + +    __pattern__ = r'^unmatchable$' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Linkdecrypter.com decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("flowlee", None)] +      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>' +    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 decrypt(self, pyfile): -        self.passwords = self.getPassword().splitlines() +    def setup(self): +        self.password = self.getPassword() +        self.req.setOption("timeout", 300) + +    def decrypt(self, pyfile):          # API not working anymore -        new_links = self.decryptHTML() -        if new_links: -            self.core.files.addLinks(new_links, pyfile.package().id) -        else: -            self.fail('Could not extract any links') +        self.urls = self.decryptHTML() -    def decryptAPI(self): +    def decryptAPI(self):          get_dict = {"t": "link", "url": self.pyfile.url, "lcache": "1"}          self.html = self.load('http://linkdecrypter.com/api', get=get_dict)          if self.html.startswith('http://'):              return self.html.splitlines()          if self.html == 'INTERRUPTION(PASSWORD)': -            for get_dict['pass'] in self.passwords: -                self.html = self.load('http://linkdecrypter.com/api', get=get_dict) -                if self.html.startswith('http://'): -                    return self.html.splitlines() +            get_dict['pass'] = self.password -        self.logError('API', self.html) +            self.html = self.load('http://linkdecrypter.com/api', get=get_dict) +            if self.html.startswith('http://'): +                return self.html.splitlines() + +        self.logError("API", self.html)          if self.html == 'INTERRUPTION(PASSWORD)': -            self.fail("No or incorrect password") +            self.fail(_("No or incorrect password"))          return None -    def decryptHTML(self): +    def decryptHTML(self):          retries = 5          post_dict = {"link_cache": "on", "pro_links": self.pyfile.url, "modo_links": "text"}          self.html = self.load('http://linkdecrypter.com/', post=post_dict, cookies=True, decode=True) -        while self.passwords or retries: -            found = re.search(self.TEXTAREA_PATTERN, self.html, flags=re.DOTALL) -            if found: -                return [x for x in found.group(1).splitlines() if '[LINK-ERROR]' not in x] +        while retries: +            m = re.search(self.TEXTAREA_PATTERN, self.html, flags=re.S) +            if m: +                return [x for x in m.group(1).splitlines() if '[LINK-ERROR]' not in x] -            found = re.search(self.CAPTCHA_PATTERN, self.html) -            if found: -                captcha_url = 'http://linkdecrypter.com/' + found.group(1) -                result_type = "positional" if "getPos" in found.group(2) else "textual" +            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" -                found = re.search(r"<p><i><b>([^<]+)</b></i></p>", self.html) -                msg = found.group(1) if found else "" -                self.logInfo("Captcha protected link", result_type, msg) +                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": @@ -92,12 +82,11 @@ class LinkdecrypterCom(Crypter):                  retries -= 1              elif self.PASSWORD_PATTERN in self.html: -                if self.passwords: -                    password = self.passwords.pop(0) -                    self.logInfo("Password protected link, trying " + password) -                    self.html = self.load('http://linkdecrypter.com/', post={'password': password}, decode=True) +                if self.password: +                    self.logInfo(_("Password protected link")) +                    self.html = self.load('http://linkdecrypter.com/', post={'password': self.password}, decode=True)                  else: -                    self.fail("No or incorrect password") +                    self.fail(_("Missing password"))              else:                  retries -= 1 diff --git a/module/plugins/crypter/LixIn.py b/module/plugins/crypter/LixIn.py index 619a474f2..d899d58c7 100644 --- a/module/plugins/crypter/LixIn.py +++ b/module/plugins/crypter/LixIn.py @@ -6,53 +6,57 @@ from module.plugins.Crypter import Crypter  class LixIn(Crypter): -    __name__ = "LixIn" -    __type__ = "crypter" -    __pattern__ = r'http://(www.)?lix.in/(?P<id>.*)' +    __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_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Lix.in decrypter plugin""" -    __author_name__ = "spoob" -    __author_mail__ = "spoob@pyload.org" +    __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="(.*?)"' -    CAPTCHA_PATTERN = '<img src="(?P<image>captcha_img.php\?.*?)"' -    SUBMIT_PATTERN = r"value='continue.*?'" -    LINK_PATTERN = r'name="ifram" src="(?P<link>.*?)"'      def decrypt(self, pyfile):          url = pyfile.url -        matches = re.match(self.__pattern__, url) -        if not matches: -            self.fail("couldn't identify file id") +        m = re.match(self.__pattern__, url) +        if m is None: +            self.error(_("Unable to identify file ID")) -        id = matches.group("id") +        id = m.group('ID')          self.logDebug("File id is %s" % id) -        self.html = self.req.load(url, decode=True) +        self.html = self.load(url, decode=True) -        matches = re.search(self.SUBMIT_PATTERN, self.html) -        if not matches: -            self.fail("link doesn't seem valid") +        m = re.search(self.SUBMIT_PATTERN, self.html) +        if m is None: +            self.error(_("Link doesn't seem valid")) -        matches = re.search(self.CAPTCHA_PATTERN, self.html) -        if matches: -            for _ in xrange(5): -                matches = re.search(self.CAPTCHA_PATTERN, self.html) -                if matches: -                    self.logDebug("trying captcha") -                    captcharesult = self.decryptCaptcha("http://lix.in/" + matches.group("image")) -                self.html = self.req.load(url, decode=True, +        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") +                self.logDebug("No captcha/captcha solved")          else: -            self.html = self.req.load(url, decode=True, post={"submit": "submit", "tiny": id}) +            self.html = self.load(url, decode=True, post={"submit": "submit", "tiny": id}) -        matches = re.search(self.LINK_PATTERN, self.html) -        if not matches: -            self.fail("can't find destination url") - -        new_link = matches.group("link") -        self.logDebug("Found link %s, adding to package" % new_link) - -        self.packages.append((pyfile.package().name, [new_link], pyfile.package().name)) +        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/module/plugins/crypter/LofCc.py b/module/plugins/crypter/LofCc.py index a17d58884..3cac0fbf2 100644 --- a/module/plugins/crypter/LofCc.py +++ b/module/plugins/crypter/LofCc.py @@ -1,13 +1,19 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.DeadCrypter import DeadCrypter +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo  class LofCc(DeadCrypter): -    __name__ = "LofCc" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?lof.cc/(.*)' +    __name__    = "LofCc" +    __type__    = "crypter"      __version__ = "0.21" + +    __pattern__ = r'http://(?:www\.)?lof\.cc/(.+)' +    __config__  = [] +      __description__ = """Lof.cc decrypter plugin""" -    __author_name__ = "mkaay" -    __author_mail__ = "mkaay@mkaay.de" +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de")] + + +getInfo = create_getInfo(LofCc) diff --git a/module/plugins/crypter/MBLinkInfo.py b/module/plugins/crypter/MBLinkInfo.py index 8aa70e8ff..82c2d9719 100644 --- a/module/plugins/crypter/MBLinkInfo.py +++ b/module/plugins/crypter/MBLinkInfo.py @@ -1,13 +1,20 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.DeadCrypter import DeadCrypter +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo  class MBLinkInfo(DeadCrypter): -    __name__ = "MBLinkInfo" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?mblink\.info/?\?id=(\d+)' +    __name__    = "MBLinkInfo" +    __type__    = "crypter"      __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?mblink\.info/?\?id=(\d+)' +    __config__  = [] +      __description__ = """MBLink.info decrypter plugin""" -    __author_name__ = ("Gummibaer", "stickell") -    __author_mail__ = ("Gummibaer@wiki-bierkiste.de", "l.stickell@yahoo.it") +    __license__     = "GPLv3" +    __authors__     = [("Gummibaer", "Gummibaer@wiki-bierkiste.de"), +                       ("stickell", "l.stickell@yahoo.it")] + + +getInfo = create_getInfo(MBLinkInfo) diff --git a/module/plugins/crypter/MediafireComFolder.py b/module/plugins/crypter/MediafireComFolder.py index 46856229c..d1dc89518 100644 --- a/module/plugins/crypter/MediafireComFolder.py +++ b/module/plugins/crypter/MediafireComFolder.py @@ -7,51 +7,52 @@ from module.common.json_layer import json_loads  class MediafireComFolder(Crypter): -    __name__ = "MediafireComFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?mediafire\.com/(folder/|\?sharekey=|\?\w{13}($|[/#]))' +    __name__    = "MediafireComFolder" +    __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_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Mediafire.com folder decrypter plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] -    FOLDER_KEY_PATTERN = r"var afI= '(\w+)';" -    FILE_URL_PATTERN = '<meta property="og:url" content="http://www.mediafire.com/\?(\w+)"/>' -    def decrypt(self, pyfile): -        new_links = [] +    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)) +        self.logDebug("Location (%d): %s" % (result, url))          if result == 0: -            # load and parse html             +            # load and parse html              html = self.load(pyfile.url) -            found = re.search(self.FILE_URL_PATTERN, html) -            if found: +            m = re.search(self.LINK_PATTERN, html) +            if m:                  # file page -                new_links.append("http://www.mediafire.com/file/%s" % found.group(1)) +                self.urls.append("http://www.mediafire.com/file/%s" % m.group(1))              else:                  # folder page -                found = re.search(self.FOLDER_KEY_PATTERN, html) -                if found: -                    folder_key = found.group(1) +                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?folder_key=%s&response_format=json&version=1" % 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']: -                            new_links.append("http://www.mediafire.com/file/%s" % link['quickkey']) +                            self.urls.append("http://www.mediafire.com/file/%s" % link['quickkey'])                      else:                          self.fail(json_resp['response']['message'])          elif result == 1:              self.offline()          else: -            new_links.append(url) - -        if new_links: -            self.core.files.addLinks(new_links, pyfile.package().id) -        else: -            self.fail('Could not extract any links') +            self.urls.append(url) diff --git a/module/plugins/crypter/MegaCoNzFolder.py b/module/plugins/crypter/MegaCoNzFolder.py new file mode 100644 index 000000000..caa2ab563 --- /dev/null +++ b/module/plugins/crypter/MegaCoNzFolder.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.Crypter import Crypter + + +class MegaCoNzFolder(Crypter): +    __name__    = "MegaCoNzFolder" +    __type__    = "crypter" +    __version__ = "0.02" + +    __pattern__ = r'https?://(?:www\.)?mega\.co\.nz/#F![\w+^_]![\w,\\-]+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "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): +        self.html = self.load("http://rapidgen.org/linkfinder", post={'linklisturl': self.pyfile.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/module/plugins/crypter/MegaRapidCzFolder.py b/module/plugins/crypter/MegaRapidCzFolder.py new file mode 100644 index 000000000..e7dff6c8a --- /dev/null +++ b/module/plugins/crypter/MegaRapidCzFolder.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo + + +class MegaRapidCzFolder(SimpleCrypter): +    __name__    = "MegaRapidCzFolder" +    __type__    = "crypter" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?(share|mega)rapid\.cz/slozka/\d+/\w+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "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="([^"]+)">' + + +getInfo = create_getInfo(MegaRapidCzFolder) diff --git a/module/plugins/crypter/MegauploadComFolder.py b/module/plugins/crypter/MegauploadComFolder.py new file mode 100644 index 000000000..08f96700d --- /dev/null +++ b/module/plugins/crypter/MegauploadComFolder.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo + + +class MegauploadComFolder(DeadCrypter): +    __name__    = "MegauploadComFolder" +    __type__    = "crypter" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?megaupload\.com/(\?f|xml/folderfiles\.php\?.*&?folderid)=\w+' + +    __description__ = """Megaupload.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +getInfo = create_getInfo(MegauploadComFolder) diff --git a/module/plugins/crypter/Movie2kTo.py b/module/plugins/crypter/Movie2kTo.py index 991e1e1ee..7d71950fd 100644 --- a/module/plugins/crypter/Movie2kTo.py +++ b/module/plugins/crypter/Movie2kTo.py @@ -1,13 +1,19 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.DeadCrypter import DeadCrypter +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo  class Movie2kTo(DeadCrypter): -    __name__ = "Movie2kTo" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?movie2k\.to/(.*)\.html' +    __name__    = "Movie2kTo" +    __type__    = "crypter"      __version__ = "0.51" + +    __pattern__ = r'http://(?:www\.)?movie2k\.to/(.+)\.html' +    __config__  = [] +      __description__ = """Movie2k.to decrypter plugin""" -    __author_name__ = "4Christopher" -    __author_mail__ = "4Christopher@gmx.de" +    __license__     = "GPLv3" +    __authors__     = [("4Christopher", "4Christopher@gmx.de")] + + +getInfo = create_getInfo(Movie2kTo) diff --git a/module/plugins/crypter/MultiUpOrg.py b/module/plugins/crypter/MultiUpOrg.py index 997d60862..5209ebf09 100644 --- a/module/plugins/crypter/MultiUpOrg.py +++ b/module/plugins/crypter/MultiUpOrg.py @@ -1,41 +1,30 @@  # -*- coding: utf-8 -*- -############################################################################### -#  This program is free software: you can redistribute it and/or modify -#  it under the terms of the GNU Affero General Public License as -#  published by the Free Software Foundation, either version 3 of the -#  License, or (at your option) any later version. -# -#  This program is distributed in the hope that it will be useful, -#  but WITHOUT ANY WARRANTY; without even the implied warranty of -#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -#  GNU Affero General Public License for more details. -# -#  You should have received a copy of the GNU Affero General Public License -#  along with this program.  If not, see <http://www.gnu.org/licenses/>. -# -#  @author: Walter Purcaro -############################################################################### -  import re  from urlparse import urljoin -from module.plugins.internal.SimpleCrypter import SimpleCrypter +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  class MultiUpOrg(SimpleCrypter): -    __name__ = "MultiUpOrg" -    __type__ = "crypter" -    __pattern__ = r"http://(?:www\.)?multiup\.org/(en|fr)/(?P<TYPE>project|download|miror)/\w+(/\w+)?" -    __version__ = "0.01" +    __name__    = "MultiUpOrg" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?multiup\.org/(en|fr)/(?P<TYPE>project|download|miror)/\w+(/\w+)?' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """MultiUp.org crypter plugin""" -    __author_name__ = "Walter Purcaro" -    __author_mail__ = "vuolter@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    NAME_PATTERN = r'<title>.*(?:Project|Projet|ownload|élécharger) (?P<N>.+?) (\(|- )' -    TITLE_PATTERN = r'<title>.*(Project|Projet|ownload|élécharger) (?P<title>.+?) (\(|- )'      def getLinks(self): -        m_type = re.match(self.__pattern__, self.pyfile.url).group("TYPE") +        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/.*)' @@ -47,3 +36,6 @@ class MultiUpOrg(SimpleCrypter):                  self.html = self.load(miror_page)          return re.findall(pattern, self.html) + + +getInfo = create_getInfo(MultiUpOrg) diff --git a/module/plugins/crypter/MultiloadCz.py b/module/plugins/crypter/MultiloadCz.py index 5e7051bad..fa1eb02d7 100644 --- a/module/plugins/crypter/MultiloadCz.py +++ b/module/plugins/crypter/MultiloadCz.py @@ -5,38 +5,38 @@ from module.plugins.Crypter import Crypter  class MultiloadCz(Crypter): -    __name__ = "MultiloadCz" -    __type__ = "crypter" -    __pattern__ = r'http://(?:[^/]*\.)?multiload.cz/(stahnout|slozka)/.*' -    __version__ = "0.4" +    __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_package", "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""" -    __config__ = [("usedHoster", "str", "Prefered hoster list (bar-separated) ", ""), -                  ("ignoredHoster", "str", "Ignored hoster list (bar-separated) ", "")] -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __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) -        new_links = []          if re.match(self.__pattern__, pyfile.url).group(1) == "slozka": -            found = re.search(self.FOLDER_PATTERN, self.html) -            if found is not None: -                new_links.extend(found.group(1).split()) +            m = re.search(self.FOLDER_PATTERN, self.html) +            if m: +                self.urls.extend(m.group(1).split())          else: -            found = re.findall(self.LINK_PATTERN, self.html) -            if found: +            m = re.findall(self.LINK_PATTERN, self.html) +            if m:                  prefered_set = set(self.getConfig("usedHoster").split('|')) -                new_links.extend([x[1] for x in found if x[0] in prefered_set]) +                self.urls.extend([x[1] for x in m if x[0] in prefered_set]) -                if not new_links: +                if not self.urls:                      ignored_set = set(self.getConfig("ignoredHoster").split('|')) -                    new_links.extend([x[1] for x in found if x[0] not in ignored_set]) - -        if new_links: -            self.core.files.addLinks(new_links, pyfile.package().id) -        else: -            self.fail('Could not extract any links') +                    self.urls.extend([x[1] for x in m if x[0] not in ignored_set]) diff --git a/module/plugins/crypter/MultiuploadCom.py b/module/plugins/crypter/MultiuploadCom.py index 677080772..347b7e5af 100644 --- a/module/plugins/crypter/MultiuploadCom.py +++ b/module/plugins/crypter/MultiuploadCom.py @@ -1,64 +1,19 @@  # -*- coding: utf-8 -*- -import re -from time import time +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo -from module.plugins.Crypter import Crypter -from module.common.json_layer import json_loads +class MultiuploadCom(DeadCrypter): +    __name__    = "MultiuploadCom" +    __type__    = "crypter" +    __version__ = "0.02" -class MultiuploadCom(Crypter): -    __name__ = "MultiuploadCom" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?multiupload.com/(\w+)' -    __version__ = "0.01" -    __description__ = """MultiUpload.com decrypter plugin""" -    __config__ = [("preferedHoster", "str", "Prefered hoster list (bar-separated) ", "multiupload"), -                  ("ignoredHoster", "str", "Ignored hoster list (bar-separated) ", "")] -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __pattern__ = r'http://(?:www\.)?multiupload\.(com|nl)/\w+' +    __config__  = [] -    ML_LINK_PATTERN = r'<div id="downloadbutton_" style=""><a href="([^"]+)"' +    __description__ = """ MultiUpload.com decrypter plugin """ +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] -    def decrypt(self, pyfile): -        self.html = self.load(pyfile.url) -        found = re.search(self.ML_LINK_PATTERN, self.html) -        ml_url = found.group(1) if found else None -        json_list = json_loads(self.load("http://multiupload.com/progress/", get={ -            "d": re.match(self.__pattern__, pyfile.url).group(1), -            "r": str(int(time() * 1000)) -        })) -        new_links = [] - -        prefered_set = map(lambda s: s.lower().split('.')[0], set(self.getConfig("preferedHoster").split('|'))) - -        if ml_url and 'multiupload' in prefered_set: -            new_links.append(ml_url) - -        for link in json_list: -            if link['service'].lower() in prefered_set and int(link['status']) and not int(link['deleted']): -                url = self.getLocation(link['url']) -                if url: -                    new_links.append(url) - -        if not new_links: -            ignored_set = map(lambda s: s.lower().split('.')[0], set(self.getConfig("ignoredHoster").split('|'))) - -            if 'multiupload' not in ignored_set: -                new_links.append(ml_url) - -            for link in json_list: -                if link['service'].lower() not in ignored_set and int(link['status']) and not int(link['deleted']): -                    url = self.getLocation(link['url']) -                    if url: -                        new_links.append(url) - -        if new_links: -            self.core.files.addLinks(new_links, pyfile.package().id) -        else: -            self.fail('Could not extract any links') - -    def getLocation(self, url): -        header = self.load(url, just_header=True) -        return header['location'] if "location" in header else None +getInfo = create_getInfo(MultiuploadCom) diff --git a/module/plugins/crypter/NCryptIn.py b/module/plugins/crypter/NCryptIn.py index eacd4aa8d..8b7214157 100644 --- a/module/plugins/crypter/NCryptIn.py +++ b/module/plugins/crypter/NCryptIn.py @@ -5,34 +5,40 @@ import binascii  import re  from Crypto.Cipher import AES +  from module.plugins.Crypter import Crypter  from module.plugins.internal.CaptchaService import ReCaptcha  class NCryptIn(Crypter): -    __name__ = "NCryptIn" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?ncrypt.in/(?P<type>folder|link|frame)-([^/\?]+)' -    __version__ = "1.32" +    __name__    = "NCryptIn" +    __type__    = "crypter" +    __version__ = "1.33" + +    __pattern__ = r'http://(?:www\.)?ncrypt\.in/(?P<TYPE>folder|link|frame)-([^/\?]+)' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """NCrypt.in decrypter plugin""" -    __author_name__ = ("fragonib", "stickell") -    __author_mail__ = ("fragonib[AT]yahoo[DOT]es", "l.stickell@yahoo.it") +    __license__     = "GPLv3" +    __authors__     = [("fragonib", "fragonib[AT]yahoo[DOT]es"), +                       ("stickell", "l.stickell@yahoo.it")] + -    # Constants -    _JK_KEY_ = "jk" -    _CRYPTED_KEY_ = "crypted" +    JK_KEY = "jk" +    CRYPTED_KEY = "crypted"      NAME_PATTERN = r'<meta name="description" content="(?P<N>[^"]+)"' +      def setup(self):          self.package = None -        self.html = None          self.cleanedHtml = None -        self.links_source_order = ['cnl2', 'rsdf', 'ccf', 'dlc', 'web'] +        self.links_source_order = ["cnl2", "rsdf", "ccf", "dlc", "web"]          self.protection_type = None -    def decrypt(self, pyfile): +    def decrypt(self, pyfile):          # Init          self.package = pyfile.package()          package_links = [] @@ -52,7 +58,7 @@ class NCryptIn(Crypter):              if not self.isOnline():                  self.offline() -            # Check for folder protection     +            # Check for folder protection              if self.isProtected():                  self.html = self.unlockProtection()                  self.cleanedHtml = self.removeHtmlCrap(self.html) @@ -69,17 +75,19 @@ class NCryptIn(Crypter):              package_links = set(package_links)          # Pack and return links -        if not package_links: -            self.fail('Could not extract any links') -        self.packages = [(package_name, package_links, folder_name)] +        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') +        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)>', @@ -87,27 +95,30 @@ class NCryptIn(Crypter):                      r'<table class="global">(.*?)</table>',                      r'<iframe\s+style="display:none(.*?)</iframe>')          for pattern in patterns: -            rexpr = re.compile(pattern, re.DOTALL) +            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 found") +            self.logDebug("File not m")              return False          return True +      def isProtected(self): -        form_match = re.search(r'<form.*?name.*?protected.*?>(.*?)</form>', self.cleanedHtml, re.DOTALL) -        if form_match: -            form_content = form_match.group(1) +        form = re.search(r'<form.*?name.*?protected.*?>(.*?)</form>', self.cleanedHtml, re.S) +        if form is not None: +            content = form.group(1)              for keyword in ("password", "captcha"): -                if keyword in form_content: +                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: @@ -116,14 +127,14 @@ class NCryptIn(Crypter):          else:              name = self.package.name              folder = self.package.folder -            self.logDebug("Package info not found, defaulting to pyfile name [%s] and folder [%s]" % (name, folder)) +            self.logDebug("Package info not m, defaulting to pyfile name [%s] and folder [%s]" % (name, folder))          return name, folder -    def unlockProtection(self): +    def unlockProtection(self):          postData = {} -        form = re.search(r'<form name="protected"(.*?)</form>', self.cleanedHtml, re.DOTALL).group(1) +        form = re.search(r'<form name="protected"(.*?)</form>', self.cleanedHtml, re.S).group(1)          # Submit package password          if "password" in form: @@ -139,15 +150,15 @@ class NCryptIn(Crypter):              self.logDebug("Captcha resolved [%s]" % captcha)              postData['captcha'] = captcha -        # Resolve recaptcha            +        # 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) -            challenge, code = recaptcha.challenge(captcha_key) +            challenge, response = recaptcha.challenge(captcha_key)              postData['recaptcha_challenge_field'] = challenge -            postData['recaptcha_response_field'] = code +            postData['recaptcha_response_field']  = response          # Resolve circlecaptcha          if "circlecaptcha" in form: @@ -162,25 +173,24 @@ class NCryptIn(Crypter):          postData['submit_protected'] = 'Continue to folder'          return self.load(self.pyfile.url, post=postData, decode=True) -    def handleErrors(self): +    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") +                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.logDebug("Invalid captcha, retrying")                  self.invalidCaptcha()                  self.retry()              else:                  self.correctCaptcha() -    def handleLinkSource(self, link_source_type): +    def handleLinkSource(self, link_source_type):          # Check for JS engine -        require_js_engine = link_source_type in ('cnl2', 'rsdf', 'ccf', 'dlc') +        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 [] @@ -190,15 +200,15 @@ class NCryptIn(Crypter):              return self.handleSingleLink()          if link_source_type == 'cnl2':              return self.handleCNL2() -        elif link_source_type in ('rsdf', 'ccf', 'dlc'): +        elif link_source_type in ("rsdf", "ccf", "dlc"):              return self.handleContainer(link_source_type) -        elif link_source_type == 'web': +        elif link_source_type == "web":              return self.handleWebLinks()          else: -            self.fail('unknown source type "%s" (this is probably a bug)' % link_source_type) +            self.error('Unknown source type "%s" (this is probably a bug)' % link_source_type) -    def handleSingleLink(self): +    def handleSingleLink(self):          self.logDebug("Handling Single link")          package_links = [] @@ -209,8 +219,8 @@ class NCryptIn(Crypter):          return package_links -    def handleCNL2(self): +    def handleCNL2(self):          self.logDebug("Handling CNL2 links")          package_links = [] @@ -220,16 +230,16 @@ class NCryptIn(Crypter):                  for (crypted, jk) in zip(vcrypted, vjk):                      package_links.extend(self._getLinks(crypted, jk))              except: -                self.fail("Unable to decrypt CNL2 links") +                self.fail(_("Unable to decrypt CNL2 links"))          return package_links -    def handleContainers(self): +    def handleContainers(self):          self.logDebug("Handling Container links")          package_links = [] -        pattern = r"/container/(rsdf|dlc|ccf)/([a-z0-9]+)" +        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: @@ -238,10 +248,10 @@ class NCryptIn(Crypter):          return package_links -    def handleWebLinks(self): +    def handleWebLinks(self):          self.logDebug("Handling Web links") -        pattern = r"(http://ncrypt\.in/link-.*?=)" +        pattern = r'(http://ncrypt\.in/link-.*?=)'          links = re.findall(pattern, self.html)          package_links = [] @@ -254,6 +264,7 @@ class NCryptIn(Crypter):          return package_links +      def decryptLink(self, link):          try:              url = link.replace("link-", "frame-") @@ -262,24 +273,24 @@ class NCryptIn(Crypter):          except Exception, detail:              self.logDebug("Error decrypting link %s, %s" % (link, detail)) -    def _getCipherParams(self): +    def _getCipherParams(self):          pattern = r'<input.*?name="%s".*?value="(.*?)"'          # Get jk -        jk_re = pattern % NCryptIn._JK_KEY_ +        jk_re = pattern % NCryptIn.JK_KEY          vjk = re.findall(jk_re, self.html)          # Get crypted -        crypted_re = pattern % NCryptIn._CRYPTED_KEY_ +        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): +    def _getLinks(self, crypted, jk):          # Get key          jreturn = self.js.eval("%s f()" % jk)          self.logDebug("JsEngine returns value [%s]" % jreturn) diff --git a/module/plugins/crypter/NetfolderIn.py b/module/plugins/crypter/NetfolderIn.py index 28ee3a9dd..62dc5c914 100644 --- a/module/plugins/crypter/NetfolderIn.py +++ b/module/plugins/crypter/NetfolderIn.py @@ -2,58 +2,56 @@  import re -from module.plugins.internal.SimpleCrypter import SimpleCrypter +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  class NetfolderIn(SimpleCrypter): -    __name__ = "NetfolderIn" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?netfolder.in/((?P<id1>\w+)/\w+|folder.php\?folder_id=(?P<id2>\w+))' -    __version__ = "0.6" +    __name__    = "NetfolderIn" +    __type__    = "crypter" +    __version__ = "0.72" + +    __pattern__ = r'http://(?:www\.)?netfolder\.in/(folder\.php\?folder_id=)?(?P<ID>\w+)(?(1)|/\w+)' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """NetFolder.in decrypter plugin""" -    __author_name__ = ("RaNaN", "fragonib") -    __author_mail__ = ("RaNaN@pyload.org", "fragonib[AT]yahoo[DOT]es") +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org"), +                       ("fragonib", "fragonib[AT]yahoo[DOT]es")] -    TITLE_PATTERN = r'<div class="Text">Inhalt des Ordners <span(.*)>(?P<title>.+)</span></div>' -    def decrypt(self, pyfile): -        # Request package -        self.html = self.load(pyfile.url) +    NAME_PATTERN = r'<div class="Text">Inhalt des Ordners <span.*>(?P<N>.+)</span></div>' -        # Check for password protection     -        if self.isPasswordProtected(): -            self.html = self.submitPassword() -            if self.html is None: -                self.fail("Incorrect password, please set right password on Add package form and retry") -        # Get package name and folder -        (package_name, folder_name) = self.getPackageNameAndFolder() +    def prepare(self): +        super(NetfolderIn, self).prepare() -        # Get package links -        package_links = self.getLinks() +        # 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")) -        # Set package -        self.packages = [(package_name, package_links, folder_name)]      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 = max(m.group('id1'), m.group('id2')) +            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      +        # 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) @@ -65,7 +63,11 @@ class NetfolderIn(SimpleCrypter):          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 + + +getInfo = create_getInfo(NetfolderIn) diff --git a/module/plugins/crypter/NosvideoCom.py b/module/plugins/crypter/NosvideoCom.py index 63e199a7a..1e280abd2 100644 --- a/module/plugins/crypter/NosvideoCom.py +++ b/module/plugins/crypter/NosvideoCom.py @@ -1,16 +1,24 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.SimpleCrypter import SimpleCrypter +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  class NosvideoCom(SimpleCrypter): -    __name__ = "NosvideoCom" -    __type__ = "crypter" +    __name__    = "NosvideoCom" +    __type__    = "crypter" +    __version__ = "0.03" +      __pattern__ = r'http://(?:www\.)?nosvideo\.com/\?v=\w+' -    __version__ = "0.01" +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Nosvideo.com decrypter plugin""" -    __author_name__ = "igel" -    __author_mail__ = "igelkun@myopera.com" +    __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>.+?)<' + -    LINK_PATTERN = r'href="(http://(?:w{3}\.)?nosupload.com/\?d=\w+)"' -    TITLE_PATTERN = r"<[tT]itle>Watch (?P<title>.+)</[tT]itle>" +getInfo = create_getInfo(NosvideoCom) diff --git a/module/plugins/crypter/OneKhDe.py b/module/plugins/crypter/OneKhDe.py index 82fad86ce..cfb084da8 100644 --- a/module/plugins/crypter/OneKhDe.py +++ b/module/plugins/crypter/OneKhDe.py @@ -7,31 +7,35 @@ from module.plugins.Crypter import Crypter  class OneKhDe(Crypter): -    __name__ = "OneKhDe" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?1kh.de/f/' -    __version__ = "0.1" +    __name__    = "OneKhDe" +    __type__    = "crypter" +    __version__ = "0.10" + +    __pattern__ = r'http://(?:www\.)?1kh\.de/f/' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """1kh.de decrypter plugin""" -    __author_name__ = "spoob" -    __author_mail__ = "spoob@pyload.org" +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org")] +      def __init__(self, parent):          Crypter.__init__(self, parent)          self.parent = parent -        self.html = None +      def file_exists(self):          """ returns True or False          """          return True +      def proceed(self, url, location):          url = self.parent.url -        self.html = self.req.load(url) -        temp_links = [] +        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 = unescape( -                re.search("width=\"100%\" src=\"(.*)\"></iframe>", self.req.load("http://1kh.de/l/" + id)).group(1)) -            temp_links.append(new_link) -        self.links = temp_links +                re.search("width=\"100%\" src=\"(.*)\"></iframe>", self.load("http://1kh.de/l/" + id)).group(1)) +            self.urls.append(new_link) diff --git a/module/plugins/crypter/OronComFolder.py b/module/plugins/crypter/OronComFolder.py index f087370ac..9e06bdf32 100755 --- a/module/plugins/crypter/OronComFolder.py +++ b/module/plugins/crypter/OronComFolder.py @@ -1,13 +1,19 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.DeadCrypter import DeadCrypter +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo  class OronComFolder(DeadCrypter): -    __name__ = "OronComFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?oron.com/folder/\w+' +    __name__    = "OronComFolder" +    __type__    = "crypter"      __version__ = "0.11" + +    __pattern__ = r'http://(?:www\.)?oron\.com/folder/\w+' +    __config__  = [] +      __description__ = """Oron.com folder decrypter plugin""" -    __author_name__ = "DHMH" -    __author_mail__ = "webmaster@pcProfil.de" +    __license__     = "GPLv3" +    __authors__     = [("DHMH", "webmaster@pcProfil.de")] + + +getInfo = create_getInfo(OronComFolder) diff --git a/module/plugins/crypter/PastebinCom.py b/module/plugins/crypter/PastebinCom.py index e74f71a5c..b3d5a4bea 100644 --- a/module/plugins/crypter/PastebinCom.py +++ b/module/plugins/crypter/PastebinCom.py @@ -1,30 +1,24 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -from module.plugins.internal.SimpleCrypter import SimpleCrypter + +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  class PastebinCom(SimpleCrypter): -    __name__ = "PastebinCom" -    __type__ = "crypter" +    __name__    = "PastebinCom" +    __type__    = "crypter" +    __version__ = "0.03" +      __pattern__ = r'http://(?:www\.)?pastebin\.com/\w+' -    __version__ = "0.01" +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Pastebin.com decrypter plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] +      LINK_PATTERN = r'<div class="de\d+">(https?://[^ <]+)(?:[^<]*)</div>' -    TITLE_PATTERN = r'<div class="paste_box_line1" title="(?P<title>[^"]+)">' +    NAME_PATTERN = r'<div class="paste_box_line1" title="(?P<N>[^"]+)">' + + +getInfo = create_getInfo(PastebinCom) diff --git a/module/plugins/crypter/QuickshareCzFolder.py b/module/plugins/crypter/QuickshareCzFolder.py index 13d7671bf..70666b55a 100644 --- a/module/plugins/crypter/QuickshareCzFolder.py +++ b/module/plugins/crypter/QuickshareCzFolder.py @@ -5,27 +5,27 @@ from module.plugins.Crypter import Crypter  class QuickshareCzFolder(Crypter): -    __name__ = "QuickshareCzFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?quickshare.cz/slozka-\d+.*' -    __version__ = "0.1" +    __name__    = "QuickshareCzFolder" +    __type__    = "crypter" +    __version__ = "0.10" + +    __pattern__ = r'http://(?:www\.)?quickshare\.cz/slozka-\d+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Quickshare.cz folder decrypter plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] +      FOLDER_PATTERN = r'<textarea[^>]*>(.*?)</textarea>' -    LINK_PATTERN = r'(http://www.quickshare.cz/\S+)' +    LINK_PATTERN = r'(http://www\.quickshare\.cz/\S+)' +      def decrypt(self, pyfile):          html = self.load(pyfile.url) -        new_links = [] -        found = re.search(self.FOLDER_PATTERN, html, re.DOTALL) -        if found is None: -            self.fail("Parse error (FOLDER)") -        new_links.extend(re.findall(self.LINK_PATTERN, found.group(1))) - -        if new_links: -            self.core.files.addLinks(new_links, pyfile.package().id) -        else: -            self.fail('Could not extract any links') +        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/module/plugins/crypter/RSLayerCom.py b/module/plugins/crypter/RSLayerCom.py index 9f1a9a394..cc3b23bbc 100644 --- a/module/plugins/crypter/RSLayerCom.py +++ b/module/plugins/crypter/RSLayerCom.py @@ -1,13 +1,19 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.DeadCrypter import DeadCrypter +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo  class RSLayerCom(DeadCrypter): -    __name__ = "RSLayerCom" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?rs-layer.com/directory-' +    __name__    = "RSLayerCom" +    __type__    = "crypter"      __version__ = "0.21" + +    __pattern__ = r'http://(?:www\.)?rs-layer\.com/directory-' +    __config__  = [] +      __description__ = """RS-Layer.com decrypter plugin""" -    __author_name__ = "hzpz" -    __author_mail__ = "" +    __license__     = "GPLv3" +    __authors__     = [("hzpz", None)] + + +getInfo = create_getInfo(RSLayerCom) diff --git a/module/plugins/crypter/RapidfileshareNetFolder.py b/module/plugins/crypter/RapidfileshareNetFolder.py new file mode 100644 index 000000000..fc3d4241e --- /dev/null +++ b/module/plugins/crypter/RapidfileshareNetFolder.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.XFSCrypter import XFSCrypter, create_getInfo + + +class RapidfileshareNetFolder(XFSCrypter): +    __name__    = "RapidfileshareNetFolder" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?rapidfileshare\.net/users/\w+/\d+/\w+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] + +    __description__ = """Rapidfileshare.net folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("guidobelix", "guidobelix@hotmail.it")] + + +    HOSTER_DOMAIN = "rapidfileshare.net" + + +getInfo = create_getInfo(RapidfileshareNetFolder) diff --git a/module/plugins/crypter/RelinkUs.py b/module/plugins/crypter/RelinkUs.py index aad55c867..5933839ec 100644 --- a/module/plugins/crypter/RelinkUs.py +++ b/module/plugins/crypter/RelinkUs.py @@ -1,5 +1,7 @@  # -*- coding: utf-8 -*- +from __future__ import with_statement +  import base64  import binascii  import re @@ -10,47 +12,57 @@ from module.plugins.Crypter import Crypter  class RelinkUs(Crypter): -    __name__ = "RelinkUs" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?relink.us/(f/|((view|go).php\?id=))(?P<id>.+)' -    __version__ = "3.0" +    __name__    = "RelinkUs" +    __type__    = "crypter" +    __version__ = "3.11" + +    __pattern__ = r'http://(?:www\.)?relink\.us/(f/|((view|go)\.php\?id=))(?P<ID>.+)' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Relink.us decrypter plugin""" -    __author_name__ = "fragonib" -    __author_mail__ = "fragonib[AT]yahoo[DOT]es" - -    # Constants -    PREFERRED_LINK_SOURCES = ['cnl2', 'dlc', 'web'] - -    OFFLINE_TOKEN = "<title>Tattooside" -    PASSWORD_TOKEN = "container_password.php" -    PASSWORD_ERROR_ROKEN = "You have entered an incorrect password" -    PASSWORD_SUBMIT_URL = "http://www.relink.us/container_password.php" -    CAPTCHA_TOKEN = "container_captcha.php" -    CAPTCHA_ERROR_ROKEN = "You have solved the captcha wrong" -    CAPTCHA_IMG_URL = "http://www.relink.us/core/captcha/circlecaptcha.php" -    CAPTCHA_SUBMIT_URL = "http://www.relink.us/container_captcha.php" -    FILE_TITLE_REGEX = r"<th>Title</th><td><i>(.*)</i></td></tr>" -    FILE_NOTITLE = 'No title' +    __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 = "http://www.relink.us/download.php" -    WEB_FORWARD_REGEX = r"getFile\('(?P<link>.+)'\)" -    WEB_FORWARD_URL = "http://www.relink.us/frame.php" -    WEB_LINK_REGEX = r'<iframe name="Container" height="100%" frameborder="no" width="100%" src="(?P<link>.+)"></iframe>' +    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.password = None -        self.html = None          self.captcha = False -    def decrypt(self, pyfile): +    def decrypt(self, pyfile):          # Init          self.initPackage(pyfile) @@ -61,7 +73,7 @@ class RelinkUs(Crypter):          if not self.isOnline():              self.offline() -        # Check for protection     +        # Check for protection          if self.isPasswordProtected():              self.unlockPasswordProtection()              self.handleErrors() @@ -85,17 +97,17 @@ class RelinkUs(Crypter):          # Pack          if package_links:              self.packages = [(package_name, package_links, folder_name)] -        else: -            self.fail('Could not extract any links') +      def initPackage(self, pyfile): -        self.fileid = re.match(self.__pattern__, pyfile.url).group('id') +        self.fileid = re.match(self.__pattern__, pyfile.url).group('ID')          self.package = pyfile.package()          self.password = self.getPassword() -        self.url = pyfile.url +      def requestPackage(self): -        self.html = self.load(self.url, decode=True) +        self.html = self.load(self.pyfile.url, decode=True) +      def isOnline(self):          if self.OFFLINE_TOKEN in self.html: @@ -103,23 +115,27 @@ class RelinkUs(Crypter):              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):          self.logDebug("Submitting password [%s] for protected links" % self.password)          passwd_url = self.PASSWORD_SUBMIT_URL + "?id=%s" % self.fileid          passwd_data = {'id': self.fileid, 'password': self.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 @@ -129,10 +145,11 @@ class RelinkUs(Crypter):          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         +        # Try to get info from web          m = re.search(self.FILE_TITLE_REGEX, self.html)          if m is not None:              title = m.group(1).strip() @@ -146,23 +163,24 @@ class RelinkUs(Crypter):              folder = self.package.folder              self.logDebug("Package info not found, defaulting to pyfile name [%s] and folder [%s]" % (name, folder)) -        # Return package info  +        # 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) +            self.fail(_(msg))          if self.captcha:              if self.CAPTCHA_ERROR_ROKEN in self.html: -                self.logDebug("Invalid captcha, retrying")                  self.invalidCaptcha()                  self.retry()              else:                  self.correctCaptcha() +      def handleLinkSource(self, source):          if source == 'cnl2':              return self.handleCNL2Links() @@ -171,12 +189,13 @@ class RelinkUs(Crypter):          elif source == 'web':              return self.handleWEBLinks()          else: -            self.fail('Unknown source [%s] (this is probably a bug)' % source) +            self.error('Unknown source type "%s" (this is probably a bug)' % source) +      def handleCNL2Links(self):          self.logDebug("Search for CNL2 links")          package_links = [] -        m = re.search(self.CNL2_FORM_REGEX, self.html, re.DOTALL) +        m = re.search(self.CNL2_FORM_REGEX, self.html, re.S)          if m is not None:              cnl2_form = m.group(1)              try: @@ -187,8 +206,9 @@ class RelinkUs(Crypter):                  self.logDebug("Unable to decrypt CNL2 links")          return package_links +      def handleDLCLinks(self): -        self.logDebug('Search for DLC links') +        self.logDebug("Search for DLC links")          package_links = []          m = re.search(self.DLC_LINK_REGEX, self.html)          if m is not None: @@ -197,49 +217,58 @@ class RelinkUs(Crypter):              try:                  dlc = self.load(container_url)                  dlc_filename = self.fileid + ".dlc" -                dlc_filepath = os.path.join(self.config["general"]["download_folder"], dlc_filename) -                f = open(dlc_filepath, "wb") -                f.write(dlc) -                f.close() +                dlc_filepath = os.path.join(self.config['general']['download_folder'], dlc_filename) +                with open(dlc_filepath, "wb") as f: +                    f.write(dlc)                  package_links.append(dlc_filepath)              except: -                self.logDebug("Unable to download DLC container") +                self.fail("Unable to download DLC container")          return package_links +      def handleWEBLinks(self):          self.logDebug("Search for WEB links") +          package_links = [] -        fw_params = re.findall(self.WEB_FORWARD_REGEX, self.html) -        self.logDebug("Decrypting %d Web links" % len(fw_params)) -        for index, fw_param in enumerate(fw_params): +        params        = re.findall(self.WEB_FORWARD_REGEX, self.html) + +        self.logDebug("Decrypting %d Web links" % len(params)) + +        for index, param in enumerate(params):              try: -                fw_url = self.WEB_FORWARD_URL + "?%s" % fw_param -                self.logDebug("Decrypting Web link %d, %s" % (index + 1, fw_url)) -                fw_response = self.load(fw_url, decode=True) -                dl_link = re.search(self.WEB_LINK_REGEX, fw_response).group('link') -                package_links.append(dl_link) +                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): +    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.IGNORECASE) +        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.IGNORECASE) +        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): +    def _getLinks(self, crypted, jk):          # Get key          jreturn = self.js.eval("%s f()" % jk)          self.logDebug("JsEngine returns value [%s]" % jreturn) diff --git a/module/plugins/crypter/SafelinkingNet.py b/module/plugins/crypter/SafelinkingNet.py index 4a907c28d..709083b51 100644 --- a/module/plugins/crypter/SafelinkingNet.py +++ b/module/plugins/crypter/SafelinkingNet.py @@ -1,64 +1,67 @@  # -*- coding: utf-8 -*-  import re +  from pycurl import FOLLOWLOCATION +from BeautifulSoup import BeautifulSoup +  from module.common.json_layer import json_loads  from module.plugins.Crypter import Crypter  from module.plugins.internal.CaptchaService import SolveMedia -from module.lib.BeautifulSoup import BeautifulSoup  class SafelinkingNet(Crypter): -    __name__ = 'SafelinkingNet' -    __type__ = 'crypter' -    __pattern__ = r'https?://(?:www\.)?safelinking.net/([pd])/\w+' -    __version__ = '0.1' +    __name__    = "SafelinkingNet" +    __type__    = "crypter" +    __version__ = "0.11" + +    __pattern__ = r'https?://(?:www\.)?safelinking\.net/([pd])/\w+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Safelinking.net decrypter plugin""" -    __author_name__ = "quareevo" -    __author_mail__ = "quareevo@arcor.de" +    __license__     = "GPLv3" +    __authors__     = [("quareevo", "quareevo@arcor.de")] + + +    SOLVEMEDIA_PATTERN = "solvemediaApiKey = '([\w.-]+)';" -    __Solvemedia_pattern__ = "solvemediaApiKey = '([\w\.\-_]+)';"      def decrypt(self, pyfile):          url = pyfile.url +          if re.match(self.__pattern__, url).group(1) == "d": -            self.req.http.c.setopt(FOLLOWLOCATION, 0) -            self.load(url) -            m = re.search("^Location: (.+)$", self.req.http.header, re.MULTILINE) -            if m: -                self.core.files.addLinks([m.group(1)], pyfile.package().id) + +            header = self.load(url, just_header=True) +            if 'location' in header: +                self.urls = [header['location']]              else: -                self.fail("Couldn't find forwarded Link") +                self.error(_("Couldn't find forwarded Link"))          else: -            password = "" -            packageLinks = []              postData = {"post-protect": "1"} -            self.html = self.load(url) -              if "link-password" in self.html: -                password = pyfile.package().password -                postData["link-password"] = password +                postData['link-password'] = self.getPassword()              if "altcaptcha" in self.html: -                for _ in xrange(5): -                    m = re.search(self.__Solvemedia_pattern__, 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") +                        self.fail(_("Error parsing captcha"))                      challenge, response = captcha.challenge(captchaKey) -                    postData["adcopy_challenge"] = challenge -                    postData["adcopy_response"] = response +                    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") +                        self.fail(_("Incorrect Password"))                      if not "The CAPTCHA code you entered was wrong" in self.html:                          break @@ -72,9 +75,7 @@ class SafelinkingNet(Crypter):              if m:                  linkDict = json_loads(m.group(1))                  for link in linkDict: -                    if not "http://" in link["full"]: -                        packageLinks.append("https://safelinking.net/d/" + link["full"]) +                    if not "http://" in link['full']: +                        self.urls.append("https://safelinking.net/d/" + link['full'])                      else: -                        packageLinks.append(link["full"]) - -            self.core.files.addLinks(packageLinks, pyfile.package().id) +                        self.urls.append(link['full']) diff --git a/module/plugins/crypter/SecuredIn.py b/module/plugins/crypter/SecuredIn.py index 771205ec8..cbfa919ac 100644 --- a/module/plugins/crypter/SecuredIn.py +++ b/module/plugins/crypter/SecuredIn.py @@ -1,13 +1,19 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.DeadCrypter import DeadCrypter +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo  class SecuredIn(DeadCrypter): -    __name__ = "SecuredIn" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?secured\.in/download-[\d]+-[\w]{8}\.html' +    __name__    = "SecuredIn" +    __type__    = "crypter"      __version__ = "0.21" + +    __pattern__ = r'http://(?:www\.)?secured\.in/download-[\d]+-\w{8}\.html' +    __config__  = [] +      __description__ = """Secured.in decrypter plugin""" -    __author_name__ = "mkaay" -    __author_mail__ = "mkaay@mkaay.de" +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de")] + + +getInfo = create_getInfo(SecuredIn) diff --git a/module/plugins/crypter/SerienjunkiesOrg.py b/module/plugins/crypter/SerienjunkiesOrg.py deleted file mode 100644 index 6fbbfedb3..000000000 --- a/module/plugins/crypter/SerienjunkiesOrg.py +++ /dev/null @@ -1,318 +0,0 @@ -# -*- coding: utf-8 -*- - -import re -from time import sleep -import random -from module.plugins.Crypter import Crypter -from module.lib.BeautifulSoup import BeautifulSoup -from module.unescape import unescape - - -class SerienjunkiesOrg(Crypter): -    __name__ = "SerienjunkiesOrg" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?(serienjunkies.org|dokujunkies.org)/.*?' -    __version__ = "0.39" -    __config__ = [("changeNameSJ", "Packagename;Show;Season;Format;Episode", "Take SJ.org name", "Show"), -                  ("changeNameDJ", "Packagename;Show;Format;Episode", "Take DJ.org name", "Show"), -                  ("randomPreferred", "bool", "Randomize Preferred-List", False), -                  ("hosterListMode", "OnlyOne;OnlyPreferred(One);OnlyPreferred(All);All", -                   "Use for hosters (if supported)", "All"), -                  ("hosterList", "str", "Preferred Hoster list (comma separated)", -                   "RapidshareCom,UploadedTo,NetloadIn,FilefactoryCom,FreakshareNet,FilebaseTo,HotfileCom,DepositfilesCom,EasyshareCom,KickloadCom"), -                  ("ignoreList", "str", "Ignored Hoster list (comma separated)", "MegauploadCom")] -    __description__ = """Serienjunkies.org decrypter plugin""" -    __author_name__ = ("mkaay", "godofdream") -    __author_mail__ = ("mkaay@mkaay.de", "soilfiction@gmail.com") - -    def setup(self): -        self.multiDL = False - -    def getSJSrc(self, url): -        src = self.req.load(str(url)) -        if "This website is not available in your country" in src: -            self.fail("Not available in your country") -        if not src.find("Enter Serienjunkies") == -1: -            sleep(1) -            src = self.req.load(str(url)) -        return src - -    def handleShow(self, url): -        src = self.getSJSrc(url) -        soup = BeautifulSoup(src) -        packageName = self.pyfile.package().name -        if self.getConfig("changeNameSJ") == "Show": -            found = unescape(soup.find("h2").find("a").string.split(' –')[0]) -            if found: -                packageName = found - -        nav = soup.find("div", attrs={"id": "scb"}) - -        package_links = [] -        for a in nav.findAll("a"): -            if self.getConfig("changeNameSJ") == "Show": -                package_links.append(a["href"]) -            else: -                package_links.append(a["href"] + "#hasName") -        if self.getConfig("changeNameSJ") == "Show": -            self.packages.append((packageName, package_links, packageName)) -        else: -            self.core.files.addLinks(package_links, self.pyfile.package().id) - -    def handleSeason(self, url): -        src = self.getSJSrc(url) -        soup = BeautifulSoup(src) -        post = soup.find("div", attrs={"class": "post-content"}) -        ps = post.findAll("p") - -        seasonName = unescape(soup.find("a", attrs={"rel": "bookmark"}).string).replace("–", "-") -        groups = {} -        gid = -1 -        for p in ps: -            if re.search("<strong>Sprache|<strong>Format", str(p)): -                var = p.findAll("strong") -                opts = {"Sprache": "", "Format": ""} -                for v in var: -                    n = unescape(v.string).strip() -                    n = re.sub(r"^([:]?)(.*?)([:]?)$", r'\2', n) -                    if n.strip() not in opts: -                        continue -                    val = v.nextSibling -                    if not val: -                        continue -                    val = val.replace("|", "").strip() -                    val = re.sub(r"^([:]?)(.*?)([:]?)$", r'\2', val) -                    opts[n.strip()] = val.strip() -                gid += 1 -                groups[gid] = {} -                groups[gid]["ep"] = {} -                groups[gid]["opts"] = opts -            elif re.search("<strong>Download:", str(p)): -                parts = str(p).split("<br />") -                if re.search("<strong>", parts[0]): -                    ename = re.search('<strong>(.*?)</strong>', parts[0]).group(1).strip().decode("utf-8").replace( -                        "–", "-") -                    groups[gid]["ep"][ename] = {} -                    parts.remove(parts[0]) -                    for part in parts: -                        hostername = re.search(r" \| ([-a-zA-Z0-9]+\.\w+)", part) -                        if hostername: -                            hostername = hostername.group(1) -                            groups[gid]["ep"][ename][hostername] = [] -                            links = re.findall('href="(.*?)"', part) -                            for link in links: -                                groups[gid]["ep"][ename][hostername].append(link + "#hasName") - -        links = [] -        for g in groups.values(): -            for ename in g["ep"]: -                links.extend(self.getpreferred(g["ep"][ename])) -                if self.getConfig("changeNameSJ") == "Episode": -                    self.packages.append((ename, links, ename)) -                    links = [] -            package = "%s (%s, %s)" % (seasonName, g["opts"]["Format"], g["opts"]["Sprache"]) -            if self.getConfig("changeNameSJ") == "Format": -                self.packages.append((package, links, package)) -                links = [] -        if (self.getConfig("changeNameSJ") == "Packagename") or re.search("#hasName", url): -            self.core.files.addLinks(links, self.pyfile.package().id) -        elif (self.getConfig("changeNameSJ") == "Season") or not re.search("#hasName", url): -            self.packages.append((seasonName, links, seasonName)) - -    def handleEpisode(self, url): -        src = self.getSJSrc(url) -        if not src.find( -                "Du hast das Download-Limit überschritten! Bitte versuche es später nocheinmal.") == -1: -            self.fail(_("Downloadlimit reached")) -        else: -            soup = BeautifulSoup(src) -            form = soup.find("form") -            h1 = soup.find("h1") - -            if h1.get("class") == "wrap": -                captchaTag = soup.find(attrs={"src": re.compile("^/secure/")}) -                if not captchaTag: -                    sleep(5) -                    self.retry() - -                captchaUrl = "http://download.serienjunkies.org" + captchaTag["src"] -                result = self.decryptCaptcha(str(captchaUrl), imgtype="png") -                sinp = form.find(attrs={"name": "s"}) - -                self.req.lastURL = str(url) -                sj = self.load(str(url), post={'s': sinp["value"], 'c': result, 'action': "Download"}) - -                soup = BeautifulSoup(sj) -            rawLinks = soup.findAll(attrs={"action": re.compile("^http://download.serienjunkies.org/")}) - -            if not len(rawLinks) > 0: -                sleep(1) -                self.retry() -                return - -            self.correctCaptcha() - -            links = [] -            for link in rawLinks: -                frameUrl = link["action"].replace("/go-", "/frame/go-") -                links.append(self.handleFrame(frameUrl)) -            if re.search("#hasName", url) or ((self.getConfig("changeNameSJ") == "Packagename") and -                                              (self.getConfig("changeNameDJ") == "Packagename")): -                self.core.files.addLinks(links, self.pyfile.package().id) -            else: -                if h1.text[2] == "_": -                    eName = h1.text[3:] -                else: -                    eName = h1.text -                self.packages.append((eName, links, eName)) - -    def handleOldStyleLink(self, url): -        sj = self.req.load(str(url)) -        soup = BeautifulSoup(sj) -        form = soup.find("form", attrs={"action": re.compile("^http://serienjunkies.org")}) -        captchaTag = form.find(attrs={"src": re.compile("^/safe/secure/")}) -        captchaUrl = "http://serienjunkies.org" + captchaTag["src"] -        result = self.decryptCaptcha(str(captchaUrl)) -        url = form["action"] -        sinp = form.find(attrs={"name": "s"}) - -        self.req.load(str(url), post={'s': sinp["value"], 'c': result, 'dl.start': "Download"}, cookies=False, -                      just_header=True) -        decrypted = self.req.lastEffectiveURL -        if decrypted == str(url): -            self.retry() -        self.core.files.addLinks([decrypted], self.pyfile.package().id) - -    def handleFrame(self, url): -        self.req.load(str(url)) -        return self.req.lastEffectiveURL - -    def handleShowDJ(self, url): -        src = self.getSJSrc(url) -        soup = BeautifulSoup(src) -        post = soup.find("div", attrs={"id": "page_post"}) -        ps = post.findAll("p") -        found = unescape(soup.find("h2").find("a").string.split(' –')[0]) -        if found: -            seasonName = found - -        groups = {} -        gid = -1 -        for p in ps: -            if re.search("<strong>Sprache|<strong>Format", str(p)): -                var = p.findAll("strong") -                opts = {"Sprache": "", "Format": ""} -                for v in var: -                    n = unescape(v.string).strip() -                    n = re.sub(r"^([:]?)(.*?)([:]?)$", r'\2', n) -                    if n.strip() not in opts: -                        continue -                    val = v.nextSibling -                    if not val: -                        continue -                    val = val.replace("|", "").strip() -                    val = re.sub(r"^([:]?)(.*?)([:]?)$", r'\2', val) -                    opts[n.strip()] = val.strip() -                gid += 1 -                groups[gid] = {} -                groups[gid]["ep"] = {} -                groups[gid]["opts"] = opts -            elif re.search("<strong>Download:", str(p)): -                parts = str(p).split("<br />") -                if re.search("<strong>", parts[0]): -                    ename = re.search('<strong>(.*?)</strong>', parts[0]).group(1).strip().decode("utf-8").replace( -                        "–", "-") -                    groups[gid]["ep"][ename] = {} -                    parts.remove(parts[0]) -                    for part in parts: -                        hostername = re.search(r" \| ([-a-zA-Z0-9]+\.\w+)", part) -                        if hostername: -                            hostername = hostername.group(1) -                            groups[gid]["ep"][ename][hostername] = [] -                            links = re.findall('href="(.*?)"', part) -                            for link in links: -                                groups[gid]["ep"][ename][hostername].append(link + "#hasName") - -        links = [] -        for g in groups.values(): -            for ename in g["ep"]: -                links.extend(self.getpreferred(g["ep"][ename])) -                if self.getConfig("changeNameDJ") == "Episode": -                    self.packages.append((ename, links, ename)) -                    links = [] -            package = "%s (%s, %s)" % (seasonName, g["opts"]["Format"], g["opts"]["Sprache"]) -            if self.getConfig("changeNameDJ") == "Format": -                self.packages.append((package, links, package)) -                links = [] -        if (self.getConfig("changeNameDJ") == "Packagename") or re.search("#hasName", url): -            self.core.files.addLinks(links, self.pyfile.package().id) -        elif (self.getConfig("changeNameDJ") == "Show") or not re.search("#hasName", url): -            self.packages.append((seasonName, links, seasonName)) - -    def handleCategoryDJ(self, url): -        package_links = [] -        src = self.getSJSrc(url) -        soup = BeautifulSoup(src) -        content = soup.find("div", attrs={"id": "content"}) -        for a in content.findAll("a", attrs={"rel": "bookmark"}): -            package_links.append(a["href"]) -        self.core.files.addLinks(package_links, self.pyfile.package().id) - -    def decrypt(self, pyfile): -        showPattern = re.compile("^http://serienjunkies.org/serie/(.*)/$") -        seasonPattern = re.compile("^http://serienjunkies.org/.*?/(.*)/$") -        episodePattern = re.compile("^http://download.serienjunkies.org/f-.*?.html(#hasName)?$") -        oldStyleLink = re.compile("^http://serienjunkies.org/safe/(.*)$") -        categoryPatternDJ = re.compile("^http://dokujunkies.org/.*?(.*)$") -        showPatternDJ = re.compile(r"^http://dokujunkies.org/.*?/(.*)\.html(#hasName)?$") -        framePattern = re.compile("^http://download.(serienjunkies.org|dokujunkies.org)/frame/go-.*?/$") -        url = pyfile.url -        if framePattern.match(url): -            self.packages.append((pyfile.package().name, [self.handleFrame(url)], pyfile.package().name)) -        elif episodePattern.match(url): -            self.handleEpisode(url) -        elif oldStyleLink.match(url): -            self.handleOldStyleLink(url) -        elif showPattern.match(url): -            self.handleShow(url) -        elif showPatternDJ.match(url): -            self.handleShowDJ(url) -        elif seasonPattern.match(url): -            self.handleSeason(url) -        elif categoryPatternDJ.match(url): -            self.handleCategoryDJ(url) - -    #selects the preferred hoster, after that selects any hoster (ignoring the one to ignore) -    def getpreferred(self, hosterlist): - -        result = [] -        preferredList = self.getConfig("hosterList").strip().lower().replace( -            '|', ',').replace('.', '').replace(';', ',').split(',') -        if (self.getConfig("randomPreferred") is True) and ( -                self.getConfig("hosterListMode") in ["OnlyOne", "OnlyPreferred(One)"]): -            random.shuffle(preferredList) -            # we don't want hosters be read two times -        hosterlist2 = hosterlist.copy() - -        for preferred in preferredList: -            for Hoster in hosterlist: -                if preferred == Hoster.lower().replace('.', ''): -                    for Part in hosterlist[Hoster]: -                        self.logDebug("selected " + Part) -                        result.append(str(Part)) -                        del (hosterlist2[Hoster]) -                    if self.getConfig("hosterListMode") in ["OnlyOne", "OnlyPreferred(One)"]: -                        return result - -        ignorelist = self.getConfig("ignoreList").strip().lower().replace( -            '|', ',').replace('.', '').replace(';', ',').split(',') -        if self.getConfig('hosterListMode') in ["OnlyOne", "All"]: -            for Hoster in hosterlist2: -                if Hoster.strip().lower().replace('.', '') not in ignorelist: -                    for Part in hosterlist2[Hoster]: -                        self.logDebug("selected2 " + Part) -                        result.append(str(Part)) - -                    if self.getConfig('hosterListMode') == "OnlyOne": -                        return result -        return result diff --git a/module/plugins/crypter/SexuriaCom.py b/module/plugins/crypter/SexuriaCom.py new file mode 100644 index 000000000..3c952fd6b --- /dev/null +++ b/module/plugins/crypter/SexuriaCom.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- + +import re + +from module.plugins.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_package", "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', flags=re.I) +    PATTERN_SUPPORTED_CRYPT    = re.compile(r'http://(www\.)?sexuria\.com/(v1/)?dl_links_\d+_(?P<ID>\d+)\.html', flags=re.I) +    PATTERN_SUPPORTED_REDIRECT = re.compile(r'http://(www\.)?sexuria\.com/out\.php\?id=(?P<ID>\d+)\&part=\d+\&link=\d+', flags=re.I) +    PATTERN_TITLE              = re.compile(r'<title> - (?P<TITLE>.*) Sexuria - Kostenlose Pornos - Rapidshare XXX Porn</title>', flags=re.I) +    PATTERN_PASSWORD           = re.compile(r'<strong>Passwort: </strong></div></td>.*?bgcolor="#EFEFEF">(?P<PWD>.*?)</td>', flags=re.I | re.S) +    PATTERN_DL_LINK_PAGE       = re.compile(r'"(dl_links_\d+_\d+\.html)"', flags=re.I) +    PATTERN_REDIRECT_LINKS     = re.compile(r'value="(http://sexuria\.com/out\.php\?id=\d+\&part=\d+\&link=\d+)" readonly', flags=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/module/plugins/crypter/ShareLinksBiz.py b/module/plugins/crypter/ShareLinksBiz.py index 816842216..d2e8138f6 100644 --- a/module/plugins/crypter/ShareLinksBiz.py +++ b/module/plugins/crypter/ShareLinksBiz.py @@ -9,23 +9,27 @@ from module.plugins.Crypter import Crypter  class ShareLinksBiz(Crypter): -    __name__ = "ShareLinksBiz" -    __type__ = "crypter" -    __pattern__ = r'(?P<base>http://(?:www\.)?(share-links|s2l)\.biz)/(?P<id>_?[0-9a-z]+)(/.*)?' -    __version__ = "1.13" +    __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_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Share-Links.biz decrypter plugin""" -    __author_name__ = "fragonib" -    __author_mail__ = "fragonib[AT]yahoo[DOT]es" +    __license__     = "GPLv3" +    __authors__     = [("fragonib", "fragonib[AT]yahoo[DOT]es")] +      def setup(self):          self.baseUrl = None          self.fileId = None          self.package = None -        self.html = None          self.captcha = False -    def decrypt(self, pyfile): +    def decrypt(self, pyfile):          # Init          self.initFile(pyfile) @@ -59,37 +63,43 @@ class ShareLinksBiz(Crypter):          # 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 = re.match(self.__pattern__, url).group(1) -        self.fileId = re.match(self.__pattern__, url).group('id') +        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) @@ -97,6 +107,7 @@ class ShareLinksBiz(Crypter):          url = self.baseUrl + '/' + self.fileId          self.html = self.load(url, post=post, decode=True) +      def unlockCaptchaProtection(self):          # Get captcha map          captchaMap = self._getCaptchaMap() @@ -112,14 +123,12 @@ class ShareLinksBiz(Crypter):          # Resolve captcha          href = self._resolveCoords(coords, captchaMap)          if href is None: -            self.logDebug("Invalid captcha resolving, retrying")              self.invalidCaptcha() -            self.setWait(5, False) -            self.wait() -            self.retry() +            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): @@ -128,34 +137,34 @@ class ShareLinksBiz(Crypter):              mapp[rect] = href          return mapp +      def _resolveCoords(self, coords, captchaMap):          x, y = coords -        for rect, href in captchaMap.items(): +        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") +            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.logDebug("Invalid captcha, retrying")                  self.invalidCaptcha() -                self.setWait(5) -                self.wait() -                self.retry() +                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.DOTALL) +        m = re.search(title_re, self.html, re.S)          if m is not None:              title = m.group(1).strip()              if 'unnamed' not in title: @@ -171,38 +180,45 @@ class ShareLinksBiz(Crypter):          # 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+, ''\)" +        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 -                response = self.load(dwLink) -                code = re.search(r'frm/(\d+)', response).group(1) +                res = self.load(dwLink) + +                code = re.search(r'frm/(\d+)', res).group(1)                  fwLink = self.baseUrl + "/get/frm/" + code -                response = self.load(fwLink) -                jscode = re.search(r'<script language="javascript">\s*eval\((.*)\)\s*</script>', response, -                                   re.DOTALL).group(1) +                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)'\)" +        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: @@ -210,6 +226,7 @@ class ShareLinksBiz(Crypter):              package_links.append(link)          return package_links +      def handleCNL2(self):          package_links = []          self.logDebug("Handling CNL2 links") @@ -219,16 +236,16 @@ class ShareLinksBiz(Crypter):                  (crypted, jk) = self._getCipherParams()                  package_links.extend(self._getLinks(crypted, jk))              except: -                self.fail("Unable to decrypt CNL2 links") +                self.fail(_("Unable to decrypt CNL2 links"))          return package_links -    def _getCipherParams(self): +    def _getCipherParams(self):          # Request CNL2 -        code = re.search(r'ClicknLoad.swf\?code=(.*?)"', self.html).group(1) -        url = "%s/get/cnl2/%s" % (self.baseUrl, code) -        response = self.load(url) -        params = response.split(";;") +        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(base64.standard_b64decode(params[1])) @@ -243,8 +260,8 @@ class ShareLinksBiz(Crypter):          # Log and return          return crypted, jk -    def _getLinks(self, crypted, jk): +    def _getLinks(self, crypted, jk):          # Get key          jreturn = self.js.eval("%s f()" % jk)          self.logDebug("JsEngine returns value [%s]" % jreturn) diff --git a/module/plugins/crypter/ShareRapidComFolder.py b/module/plugins/crypter/ShareRapidComFolder.py deleted file mode 100644 index 15c8dccde..000000000 --- a/module/plugins/crypter/ShareRapidComFolder.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- - -from module.plugins.internal.SimpleCrypter import SimpleCrypter - - -class ShareRapidComFolder(SimpleCrypter): -    __name__ = "ShareRapidComFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?((share(-?rapid\.(biz|com|cz|info|eu|net|org|pl|sk)|-(central|credit|free|net)\.cz|-ms\.net)|(s-?rapid|rapids)\.(cz|sk))|(e-stahuj|mediatack|premium-rapidshare|rapidshare-premium|qiuck)\.cz|kadzet\.com|stahuj-zdarma\.eu|strelci\.net|universal-share\.com)/(slozka/.+)' -    __version__ = "0.01" -    __description__ = """Share-Rapid.com folder decrypter plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" - -    LINK_PATTERN = r'<td class="soubor"[^>]*><a href="([^"]+)">' diff --git a/module/plugins/crypter/SharingmatrixComFolder.py b/module/plugins/crypter/SharingmatrixComFolder.py new file mode 100644 index 000000000..e16bdf814 --- /dev/null +++ b/module/plugins/crypter/SharingmatrixComFolder.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo + + +class SharingmatrixComFolder(DeadCrypter): +    __name__    = "SharingmatrixComFolder" +    __type__    = "crypter" +    __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?sharingmatrix\.com/folder/\w+' + +    __description__ = """Sharingmatrix.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +getInfo = create_getInfo(SharingmatrixComFolder) diff --git a/module/plugins/crypter/SpeedLoadOrgFolder.py b/module/plugins/crypter/SpeedLoadOrgFolder.py index 4bb1562ee..ddde7dec2 100644 --- a/module/plugins/crypter/SpeedLoadOrgFolder.py +++ b/module/plugins/crypter/SpeedLoadOrgFolder.py @@ -1,27 +1,19 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ -from module.plugins.internal.DeadCrypter import DeadCrypter +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo  class SpeedLoadOrgFolder(DeadCrypter): -    __name__ = "SpeedLoadOrgFolder" -    __type__ = "crypter" +    __name__    = "SpeedLoadOrgFolder" +    __type__    = "crypter" +    __version__ = "0.30" +      __pattern__ = r'http://(?:www\.)?speedload\.org/(\d+~f$|folder/\d+/)' -    __version__ = "0.3" +    __config__  = [] +      __description__ = """Speedload decrypter plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +getInfo = create_getInfo(SpeedLoadOrgFolder) diff --git a/module/plugins/crypter/StealthTo.py b/module/plugins/crypter/StealthTo.py index e97a741e0..5173421f1 100644 --- a/module/plugins/crypter/StealthTo.py +++ b/module/plugins/crypter/StealthTo.py @@ -1,48 +1,19 @@  # -*- coding: utf-8 -*- -import re +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo -from module.plugins.Crypter import Crypter +class StealthTo(DeadCrypter): +    __name__    = "StealthTo" +    __type__    = "crypter" +    __version__ = "0.20" -class StealthTo(Crypter): -    __name__ = "StealthTo" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?stealth.to/folder/' -    __version__ = "0.1" -    __description__ = """Stealth.to decrypter plugin""" -    __author_name__ = "spoob" -    __author_mail__ = "spoob@pyload.org" - -    def __init__(self, parent): -        Crypter.__init__(self, parent) -        self.parent = parent -        self.html = None +    __pattern__ = r'http://(?:www\.)?stealth\.to/folder/.+' +    __config__  = [] -    def file_exists(self): -        """ returns True or False -        """ -        return True - -    def proceed(self, url, location): -        url = self.parent.url -        self.html = self.req.load(url, cookies=True) -        temp_links = [] -        ids = [] -        ats = []  # authenticity_token -        inputs = re.findall(r"(<(input|form)[^>]+)", self.html) -        for input in inputs: -            if re.search(r"name=\"authenticity_token\"", input[0]): -                ats.append(re.search(r"value=\"([^\"]+)", input[0]).group(1)) -            if re.search(r"name=\"id\"", input[0]): -                ids.append(re.search(r"value=\"([^\"]+)", input[0]).group(1)) +    __description__ = """Stealth.to decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org")] -        for i in xrange(0, len(ids)): -            self.req.load(url + "/web", -                          post={"authenticity_token": ats[i], "id": str(ids[i]), "link": ("download_" + str(ids[i]))}, -                          cookies=True) -            new_html = self.req.load(url + "/web", post={"authenticity_token": ats[i], "id": str(ids[i]), "link": "1"}, -                                     cookies=True) -            temp_links.append(re.search(r"iframe src=\"(.*)\" frameborder", new_html).group(1)) -        self.links = temp_links +getInfo = create_getInfo(StealthTo) diff --git a/module/plugins/crypter/TnyCz.py b/module/plugins/crypter/TnyCz.py index 6c56f7639..d36128550 100644 --- a/module/plugins/crypter/TnyCz.py +++ b/module/plugins/crypter/TnyCz.py @@ -1,38 +1,30 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: Walter Purcaro -""" - -from module.plugins.internal.SimpleCrypter import SimpleCrypter +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  import re  class TnyCz(SimpleCrypter): -    __name__ = "TnyCz" -    __type__ = "crypter" +    __name__    = "TnyCz" +    __type__    = "crypter" +    __version__ = "0.03" +      __pattern__ = r'http://(?:www\.)?tny\.cz/\w+' -    __version__ = "0.01" +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Tny.cz decrypter plugin""" -    __author_name__ = "Walter Purcaro" -    __author_mail__ = "vuolter@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    NAME_PATTERN = r'<title>(?P<N>.+) - .+</title>' -    TITLE_PATTERN = r'<title>(?P<title>.+) - .+</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 + + +getInfo = create_getInfo(TnyCz) diff --git a/module/plugins/crypter/TrailerzoneInfo.py b/module/plugins/crypter/TrailerzoneInfo.py index 84a476a38..10780dd45 100644 --- a/module/plugins/crypter/TrailerzoneInfo.py +++ b/module/plugins/crypter/TrailerzoneInfo.py @@ -1,13 +1,19 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.DeadCrypter import DeadCrypter +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo  class TrailerzoneInfo(DeadCrypter): -    __name__ = "TrailerzoneInfo" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?trailerzone.info/.*?' +    __name__    = "TrailerzoneInfo" +    __type__    = "crypter"      __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?trailerzone\.info/.+' +    __config__  = [] +      __description__ = """TrailerZone.info decrypter plugin""" -    __author_name__ = "godofdream" -    __author_mail__ = "soilfiction@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("godofdream", "soilfiction@gmail.com")] + + +getInfo = create_getInfo(TrailerzoneInfo) diff --git a/module/plugins/crypter/TurbobitNetFolder.py b/module/plugins/crypter/TurbobitNetFolder.py index d9e63b4ce..c6734c997 100644 --- a/module/plugins/crypter/TurbobitNetFolder.py +++ b/module/plugins/crypter/TurbobitNetFolder.py @@ -1,50 +1,47 @@  # -*- coding: utf-8 -*- -############################################################################### -#  This program is free software: you can redistribute it and/or modify -#  it under the terms of the GNU Affero General Public License as -#  published by the Free Software Foundation, either version 3 of the -#  License, or (at your option) any later version. -# -#  This program is distributed in the hope that it will be useful, -#  but WITHOUT ANY WARRANTY; without even the implied warranty of -#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -#  GNU Affero General Public License for more details. -# -#  You should have received a copy of the GNU Affero General Public License -#  along with this program.  If not, see <http://www.gnu.org/licenses/>. -###############################################################################  import re -from module.plugins.internal.SimpleCrypter import SimpleCrypter +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  from module.common.json_layer import json_loads  class TurbobitNetFolder(SimpleCrypter): -    __name__ = "TurbobitNetFolder" -    __type__ = "crypter" +    __name__    = "TurbobitNetFolder" +    __type__    = "crypter" +    __version__ = "0.05" +      __pattern__ = r'http://(?:www\.)?turbobit\.net/download/folder/(?P<ID>\w+)' -    __version__ = "0.03" +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Turbobit.net folder decrypter plugin""" -    __author_name__ = ("stickell", "Walter Purcaro") -    __author_mail__ = ("l.stickell@yahoo.it", "vuolter@gmail.com") +    __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>' -    TITLE_PATTERN = r"src='/js/lib/grid/icon/folder.png'> <span>(?P<title>.+?)</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"] +        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") +        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)) + + +getInfo = create_getInfo(TurbobitNetFolder) diff --git a/module/plugins/crypter/TusfilesNetFolder.py b/module/plugins/crypter/TusfilesNetFolder.py index 0bc770f99..0db3470cc 100644 --- a/module/plugins/crypter/TusfilesNetFolder.py +++ b/module/plugins/crypter/TusfilesNetFolder.py @@ -1,46 +1,38 @@  # -*- coding: utf-8 -*- -############################################################################### -#  This program is free software: you can redistribute it and/or modify -#  it under the terms of the GNU Affero General Public License as -#  published by the Free Software Foundation, either version 3 of the -#  License, or (at your option) any later version. -# -#  This program is distributed in the hope that it will be useful, -#  but WITHOUT ANY WARRANTY; without even the implied warranty of -#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -#  GNU Affero General Public License for more details. -# -#  You should have received a copy of the GNU Affero General Public License -#  along with this program.  If not, see <http://www.gnu.org/licenses/>. -# -#  @author: Walter Purcaro -###############################################################################  import math  import re  from urlparse import urljoin -from module.plugins.internal.SimpleCrypter import SimpleCrypter +from module.plugins.internal.XFSCrypter import XFSCrypter, create_getInfo -class TusfilesNetFolder(SimpleCrypter): -    __name__ = "TusfilesNetFolder" -    __type__ = "crypter" -    __pattern__ = r'https?://(?:www\.)?tusfiles\.net/go/(?P<ID>\w+)/?' -    __version__ = "0.01" +class TusfilesNetFolder(XFSCrypter): +    __name__    = "TusfilesNetFolder" +    __type__    = "crypter" +    __version__ = "0.07" + +    __pattern__ = r'https?://(?:www\.)?tusfiles\.net/go/(?P<ID>\w+)' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Tusfiles.net folder decrypter plugin""" -    __author_name__ = ("Walter Purcaro", "stickell") -    __author_mail__ = ("vuolter@gmail.com", "l.stickell@yahoo.it") +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    HOSTER_DOMAIN = "tusfiles.net" -    LINK_PATTERN = r'<TD align=left><a href="(.*?)">' -    TITLE_PATTERN = r'<Title>.*?\: (?P<title>.+) folder</Title>' -    PAGES_PATTERN = r'>\((?P<pages>\d+) \w+\)<' +    PAGES_PATTERN = r'>\((\d+) \w+\)<' + +    URL_REPLACEMENTS = [(__pattern__ + ".*", r'https://www.tusfiles.net/go/\g<ID>/')] -    FILE_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 handleMultiPages(self):          pages = re.search(self.PAGES_PATTERN, self.html)          if pages: @@ -50,4 +42,7 @@ class TusfilesNetFolder(SimpleCrypter):          for p in xrange(2, pages + 1):              self.html = self.loadPage(p) -            self.package_links += self.getLinks() +            self.links += self.getLinks() + + +getInfo = create_getInfo(TusfilesNetFolder) diff --git a/module/plugins/crypter/UlozToFolder.py b/module/plugins/crypter/UlozToFolder.py index eebb14497..a1f3ed5ea 100644 --- a/module/plugins/crypter/UlozToFolder.py +++ b/module/plugins/crypter/UlozToFolder.py @@ -5,38 +5,42 @@ from module.plugins.Crypter import Crypter  class UlozToFolder(Crypter): -    __name__ = "UlozToFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?(uloz\.to|ulozto\.(cz|sk|net)|bagruj.cz|zachowajto.pl)/(m|soubory)/.*' -    __version__ = "0.2" +    __name__    = "UlozToFolder" +    __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_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Uloz.to folder decrypter plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __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) -            found = re.search(self.FOLDER_PATTERN, html, re.DOTALL) -            if found is None: -                self.fail("Parse error (FOLDER)") - -            new_links.extend(re.findall(self.LINK_PATTERN, found.group(1))) -            found = re.search(self.NEXT_PAGE_PATTERN, html) -            if found: -                html = self.load("http://ulozto.net/" + found.group(1)) +            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") +            self.logInfo(_("Limit of 99 pages reached, aborting"))          if new_links: -            self.core.files.addLinks(map(lambda s: "http://ulozto.net/%s" % s, new_links), pyfile.package().id) -        else: -            self.fail('Could not extract any links') +            self.urls = [map(lambda s: "http://ulozto.net/%s" % s, new_links)] diff --git a/module/plugins/crypter/UploadableChFolder.py b/module/plugins/crypter/UploadableChFolder.py new file mode 100644 index 000000000..22f9ca2ed --- /dev/null +++ b/module/plugins/crypter/UploadableChFolder.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo + + +class UploadableChFolder(SimpleCrypter): +    __name__    = "UploadableChFolder" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?uploadable\.ch/list/\w+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "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">' + + +getInfo = create_getInfo(UploadableChFolder) diff --git a/module/plugins/crypter/UploadedToFolder.py b/module/plugins/crypter/UploadedToFolder.py index 97c77fdce..0a71add70 100644 --- a/module/plugins/crypter/UploadedToFolder.py +++ b/module/plugins/crypter/UploadedToFolder.py @@ -1,49 +1,37 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################  import re -from module.plugins.internal.SimpleCrypter import SimpleCrypter +from urlparse import urljoin + +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo  class UploadedToFolder(SimpleCrypter): -    __name__ = "UploadedToFolder" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?(uploaded|ul)\.(to|net)/(f|folder|list)/(?P<id>\w+)' -    __version__ = "0.3" +    __name__    = "UploadedToFolder" +    __type__    = "crypter" +    __version__ = "0.42" + +    __pattern__ = r'http://(?:www\.)?(uploaded|ul)\.(to|net)/(f|folder|list)/(?P<ID>\w+)' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """UploadedTo decrypter plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] -    PLAIN_PATTERN = r'<small class="date"><a href="(?P<plain>[\w/]+)" onclick=' -    TITLE_PATTERN = r'<title>(?P<title>[^<]+)</title>' -    def decrypt(self, pyfile): -        self.html = self.load(pyfile.url) +    PLAIN_PATTERN = r'<small class="date"><a href="([\w/]+)" onclick=' +    NAME_PATTERN = r'<title>(?P<N>.+?)<' -        package_name, folder_name = self.getPackageNameAndFolder() +    def getLinks(self):          m = re.search(self.PLAIN_PATTERN, self.html) -        if m: -            plain_link = 'http://uploaded.net/' + m.group('plain') -        else: -            self.fail('Parse error - Unable to find plain url list') +        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] -        self.html = self.load(plain_link) -        package_links = self.html.split('\n')[:-1] -        self.logDebug('Package has %d links' % len(package_links)) -        self.packages = [(package_name, package_links, folder_name)] +getInfo = create_getInfo(UploadedToFolder) diff --git a/module/plugins/crypter/WiiReloadedOrg.py b/module/plugins/crypter/WiiReloadedOrg.py index 0b983c705..c3c5b8222 100644 --- a/module/plugins/crypter/WiiReloadedOrg.py +++ b/module/plugins/crypter/WiiReloadedOrg.py @@ -1,13 +1,19 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.DeadCrypter import DeadCrypter +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo  class WiiReloadedOrg(DeadCrypter): -    __name__ = "WiiReloadedOrg" -    __type__ = "crypter" -    __pattern__ = r'http://(?:www\.)?wii-reloaded\.org/protect/get\.php\?i=.+' +    __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""" -    __author_name__ = "hzpz" -    __author_mail__ = "" +    __license__     = "GPLv3" +    __authors__     = [("hzpz", None)] + + +getInfo = create_getInfo(WiiReloadedOrg) diff --git a/module/plugins/crypter/WuploadComFolder.py b/module/plugins/crypter/WuploadComFolder.py new file mode 100644 index 000000000..873c71fad --- /dev/null +++ b/module/plugins/crypter/WuploadComFolder.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo + + +class WuploadComFolder(DeadCrypter): +    __name__    = "WuploadComFolder" +    __type__    = "crypter" +    __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?wupload\.com/folder/\w+' + +    __description__ = """Wupload.com folder decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +getInfo = create_getInfo(WuploadComFolder) diff --git a/module/plugins/crypter/XFileSharingProFolder.py b/module/plugins/crypter/XFileSharingProFolder.py new file mode 100644 index 000000000..10e4d8c83 --- /dev/null +++ b/module/plugins/crypter/XFileSharingProFolder.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- + +import re + +from module.plugins.internal.XFSCrypter import XFSCrypter, create_getInfo + + +class XFileSharingProFolder(XFSCrypter): +    __name__    = "XFileSharingProFolder" +    __type__    = "crypter" +    __version__ = "0.03" + +    __pattern__ = r'^unmatchable$' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "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(XFileSharingProFolder, self).init() + +        self.__pattern__ = self.core.pluginManager.crypterPlugins[self.__name__]['pattern'] + +        self.HOSTER_DOMAIN = re.match(self.__pattern__, self.pyfile.url).group(1).lower() +        self.HOSTER_NAME = "".join([str.capitalize() for str in self.HOSTER_DOMAIN.split('.')]) + +        account = self.core.accountManager.getAccountPlugin(self.HOSTER_NAME) + +        if account and account.canUse(): +            self.account = account +        elif self.account: +            self.account.HOSTER_DOMAIN = self.HOSTER_DOMAIN +        else: +            return + +        self.user, data = self.account.selectAccount() +        self.req = self.account.getAccountRequest(self.user) +        self.premium = self.account.isPremium(self.user) + + +getInfo = create_getInfo(XFileSharingProFolder) diff --git a/module/plugins/crypter/XupPl.py b/module/plugins/crypter/XupPl.py index c04fc69f8..f2ecbee5a 100644 --- a/module/plugins/crypter/XupPl.py +++ b/module/plugins/crypter/XupPl.py @@ -4,17 +4,22 @@ from module.plugins.Crypter import Crypter  class XupPl(Crypter): -    __name__ = "XupPl" -    __type__ = "crypter" -    __pattern__ = r'https?://(?:[^/]*\.)?xup\.pl/.*' -    __version__ = "0.1" +    __name__    = "XupPl" +    __type__    = "crypter" +    __version__ = "0.10" + +    __pattern__ = r'https?://(?:[^/]*\.)?xup\.pl/.+' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] +      __description__ = """Xup.pl decrypter plugin""" -    __author_name__ = "z00nx" -    __author_mail__ = "z00nx0@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("z00nx", "z00nx0@gmail.com")] +      def decrypt(self, pyfile):          header = self.load(pyfile.url, just_header=True)          if 'location' in header: -            self.core.files.addLinks([header['location']], pyfile.package().id) +            self.urls = [header['location']]          else: -            self.fail('Unable to find link') +            self.fail(_("Unable to find link")) diff --git a/module/plugins/crypter/YoutubeBatch.py b/module/plugins/crypter/YoutubeBatch.py index e6976471c..73ebf5fb3 100644 --- a/module/plugins/crypter/YoutubeBatch.py +++ b/module/plugins/crypter/YoutubeBatch.py @@ -1,67 +1,60 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: Walter Purcaro -""" +import re  from urlparse import urljoin -import re  from module.common.json_layer import json_loads  from module.plugins.Crypter import Crypter  from module.utils import save_join -API_KEY = "AIzaSyCKnWLNlkX-L4oD1aEzqqhRw1zczeD6_k0" -  class YoutubeBatch(Crypter): -    __name__ = "YoutubeBatch" -    __type__ = "crypter" -    __pattern__ = r'https?://(?:www\.)?(m\.)?youtube\.com/(?P<TYPE>user|playlist|view_play_list)(/|.*?[?&](?:list|p)=)(?P<ID>[\w-]+)' -    __version__ = "1.00" -    __description__ = """Youtube.com channel & playlist decrypter plugin""" -    __config__ = [("likes", "bool", "Grab user (channel) liked videos", False), +    __name__    = "YoutubeBatch" +    __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_package", "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)] -    __author_name__ = "Walter Purcaro" -    __author_mail__ = "vuolter@gmail.com" + +    __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": API_KEY}) +        req.update({"key": self.API_KEY})          url = urljoin("https://www.googleapis.com/youtube/v3/", ref)          page = self.load(url, get=req)          return json_loads(page) +      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"], +        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] +        if playlists['items']: +            playlist = playlists['items'][0]              return {"id": p_id, -                    "title": playlist["snippet"]["title"], -                    "channelId": playlist["snippet"]["channelId"], -                    "channelTitle": playlist["snippet"]["channelTitle"]} +                    "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} @@ -70,16 +63,18 @@ class YoutubeBatch(Crypter):          playlists = self.api_response("playlists", req) -        for playlist in playlists["items"]: -            yield playlist["id"] +        for playlist in playlists['items']: +            yield playlist['id']          if "nextPageToken" in playlists: -            for item in self._getPlaylists(id, playlists["nextPageToken"]): +            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: @@ -87,20 +82,22 @@ class YoutubeBatch(Crypter):          playlist = self.api_response("playlistItems", req) -        for item in playlist["items"]: -            yield item["contentDetails"]["videoId"] +        for item in playlist['items']: +            yield item['contentDetails']['videoId']          if "nextPageToken" in playlist: -            for item in self._getVideosId(id, playlist["nextPageToken"]): +            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): -        match = re.match(self.__pattern__, pyfile.url) -        m_id = match.group("ID") -        m_type = match.group("TYPE") +        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") @@ -108,18 +105,18 @@ class YoutubeBatch(Crypter):              channel = self.getChannel(user)              if channel: -                playlists = self.getPlaylists(channel["id"]) -                self.logDebug("%s playlist\s found on channel \"%s\"" % (len(playlists), channel["title"])) +                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()} +                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 +                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 +                        p_data['title'] += " of " + user                          playlists.append(p_data)              else:                  playlists = [] @@ -128,14 +125,14 @@ class YoutubeBatch(Crypter):              playlists = [self.getPlaylist(m_id)]          if not playlists: -            self.fail("No playlist available") +            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 = save_join(self.config['general']['download_folder'], p["channelTitle"], p_name) +            p_name = p['title'] +            p_videos = self.getVideosId(p['id']) +            p_folder = save_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: diff --git a/module/plugins/hooks/AlldebridCom.py b/module/plugins/hooks/AlldebridCom.py index 0f8d3bfbb..cd279e733 100644 --- a/module/plugins/hooks/AlldebridCom.py +++ b/module/plugins/hooks/AlldebridCom.py @@ -1,29 +1,26 @@  # -*- coding: utf-8 -*- -# should be working +from module.plugins.internal.MultiHook import MultiHook -from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster +class AlldebridCom(MultiHook): +    __name__    = "AlldebridCom" +    __type__    = "hook" +    __version__ = "0.15" -class AlldebridCom(MultiHoster): -    __name__ = "AlldebridCom" -    __version__ = "0.13" -    __type__ = "hook" - -    __config__ = [("activated", "bool", "Activated", False), -                  ("https", "bool", "Enable HTTPS", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", ""), -                  ("unloadFailing", "bool", "Revert to stanard download if download fails", False), +    __config__ = [("https", "bool", "Enable HTTPS", False), +                  ("mode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", ""), +                  ("revertfailed", "bool", "Revert to stanard download if download fails", False),                    ("interval", "int", "Reload interval in hours (0 to disable)", 24)]      __description__ = """Alldebrid.com hook plugin""" -    __author_name__ = "Andy Voigt" -    __author_mail__ = "spamsales@online.de" +    __license__     = "GPLv3" +    __authors__     = [("Andy Voigt", "spamsales@online.de")] + -    def getHoster(self): +    def getHosters(self):          https = "https" if self.getConfig("https") else "http" -        page = getURL(https + "://www.alldebrid.com/api.php?action=get_host").replace("\"", "").strip() +        page = self.getURL(https + "://www.alldebrid.com/api.php", get={'action': "get_host"}).replace("\"", "").strip()          return [x.strip() for x in page.split(",") if x.strip()] diff --git a/module/plugins/hooks/BypassCaptcha.py b/module/plugins/hooks/BypassCaptcha.py index 70e60f56c..a32de7f42 100644 --- a/module/plugins/hooks/BypassCaptcha.py +++ b/module/plugins/hooks/BypassCaptcha.py @@ -1,70 +1,69 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: RaNaN, Godofdream, zoidberg -""" - -from thread import start_new_thread  from pycurl import FORM_FILE, LOW_SPEED_TIME +from thread import start_new_thread -from module.network.RequestFactory import getURL, getRequest  from module.network.HTTPRequest import BadHeader - +from module.network.RequestFactory import getURL, getRequest  from module.plugins.Hook import Hook -PYLOAD_KEY = "4f771155b640970d5607f919a615bdefc67e7d32" -  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" -    __version__ = "0.04" -    __description__ = """Send captchas to BypassCaptcha.com""" -    __config__ = [("activated", "bool", "Activated", False), -                  ("force", "bool", "Force BC even if client is connected", False), +    __name__    = "BypassCaptcha" +    __type__    = "hook" +    __version__ = "0.05" + +    __config__ = [("force", "bool", "Force BC even if client is connected", False),                    ("passkey", "password", "Passkey", "")] -    __author_name__ = ("RaNaN", "Godofdream", "zoidberg") -    __author_mail__ = ("RaNaN@pyload.org", "soilfcition@gmail.com", "zoidberg@mujmail.cz") + +    __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" + +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass + +      def setup(self): -        self.info = {} +        self.info = {}  #@TODO: Remove in 0.4.10 +      def getCredits(self): -        response = getURL(self.GETCREDITS_URL, post={"key": self.getConfig("passkey")}) +        res = getURL(self.GETCREDITS_URL, post={"key": self.getConfig("passkey")}) -        data = dict([x.split(' ', 1) for x in response.splitlines()]) +        data = dict([x.split(' ', 1) for x in res.splitlines()])          return int(data['Left']) +      def submit(self, captcha, captchaType="file", match=None):          req = getRequest() @@ -72,31 +71,33 @@ class BypassCaptcha(Hook):          req.c.setopt(LOW_SPEED_TIME, 80)          try: -            response = req.load(self.SUBMIT_URL, -                                post={"vendor_key": PYLOAD_KEY, -                                      "key": self.getConfig("passkey"), -                                      "gen_task_id": "1", -                                      "file": (FORM_FILE, captcha)}, -                                multipart=True) +            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 response.splitlines()]) +        data = dict([x.split(' ', 1) for x in res.splitlines()])          if not data or "Value" not in data: -            raise BypassCaptchaException(response) +            raise BypassCaptchaException(res)          result = data['Value']          ticket = data['TaskId'] -        self.logDebug("result %s : %s" % (ticket, result)) +        self.logDebug("Result %s : %s" % (ticket, result))          return ticket, result +      def respond(self, ticket, success):          try: -            response = getURL(self.RESPOND_URL, post={"task_id": ticket, "key": self.getConfig("passkey"), +            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.", str(e)) +            self.logError(_("Could not send response"), e) +      def newCaptchaTask(self, task):          if "service" in task.data: @@ -118,15 +119,18 @@ class BypassCaptcha(Hook):              start_new_thread(self.processCaptcha, (task,))          else: -            self.logInfo("Your %s account has not enough credits" % self.__name__) +            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) +            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) +            self.respond(task.data['ticket'], False) +      def processCaptcha(self, task):          c = task.captchaFile @@ -136,5 +140,5 @@ class BypassCaptcha(Hook):              task.error = e.getCode()              return -        task.data["ticket"] = ticket +        task.data['ticket'] = ticket          task.setResult(result) diff --git a/module/plugins/hooks/Captcha9kw.py b/module/plugins/hooks/Captcha9kw.py index c86f92972..33ad00c49 100755 --- a/module/plugins/hooks/Captcha9kw.py +++ b/module/plugins/hooks/Captcha9kw.py @@ -1,168 +1,261 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay, RaNaN, zoidberg -"""  from __future__ import with_statement -from thread import start_new_thread +import re +  from base64 import b64encode -import time +from thread import start_new_thread +from time import sleep -from module.network.RequestFactory import getURL  from module.network.HTTPRequest import BadHeader +from module.network.RequestFactory import getURL  from module.plugins.Hook import Hook  class Captcha9kw(Hook): -    __name__ = "Captcha9kw" -    __version__ = "0.09" +    __name__    = "Captcha9kw" +    __type__    = "hook" +    __version__ = "0.26" + +    __config__ = [("activated"     , "bool"    , "Activated"                                                                       , True                                                               ), +                  ("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""" -    __config__ = [("activated", "bool", "Activated", False), -                  ("force", "bool", "Force CT even if client is connected", True), -                  ("https", "bool", "Enable HTTPS", False), -                  ("confirm", "bool", "Confirm Captcha (Cost +6)", False), -                  ("captchaperhour", "int", "Captcha per hour (max. 9999)", 9999), -                  ("prio", "int", "Prio 1-10 (Cost +1-10)", 0), -                  ("selfsolve", "bool", -                   "If enabled and you have a 9kw client active only you will get your captcha to solve it (Selfsolve)", -                   False), -                  ("timeout", "int", "Timeout (max. 300)", 300), -                  ("passkey", "password", "API key", "")] -    __author_name__ = "RaNaN" -    __author_mail__ = "RaNaN@pyload.org" - -    API_URL = "://www.9kw.eu/index.cgi" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    API_URL = "http://www.9kw.eu/index.cgi" + + +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass +      def setup(self): -        self.API_URL = "https" + self.API_URL if self.getConfig("https") else "http" + self.API_URL -        self.info = {} +        self.info = {}  #@TODO: Remove in 0.4.10 +        if self.getConfig("ssl"): +            self.API_URL = self.API_URL.replace("http://", "https://") -    def getCredits(self): -        response = getURL(self.API_URL, get={"apikey": self.getConfig("passkey"), "pyload": "1", "source": "pyload", -                                             "action": "usercaptchaguthaben"}) -        if response.isdigit(): -            self.logInfo(_("%s credits left") % response) -            self.info["credits"] = credits = int(response) +    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(response) +            self.logError(res)              return 0 -    def processCaptcha(self, task): -        result = None -        with open(task.captchaFile, 'rb') as f: -            data = f.read() +    def _processCaptcha(self, task): +        try: +            with open(task.captchaFile, 'rb') as f: +                data = f.read() + +        except IOError, e: +            self.logError(e) +            return +          data = b64encode(data) -        self.logDebug("%s : %s" % (task.captchaFile, data)) -        if task.isPositional(): -            mouse = 1 -        else: -            mouse = 0 - -        response = getURL(self.API_URL, post={ -            "apikey": self.getConfig("passkey"), -            "prio": self.getConfig("prio"), -            "confirm": self.getConfig("confirm"), -            "captchaperhour": self.getConfig("captchaperhour"), -            "maxtimeout": self.getConfig("timeout"), -            "selfsolve": self.getConfig("selfsolve"), -            "pyload": "1", -            "source": "pyload", -            "base64": "1", -            "mouse": mouse, -            "file-upload-01": data, -            "action": "usercaptchaupload"}) - -        if response.isdigit(): -            self.logInfo(_("New CaptchaID from upload: %s : %s") % (response, task.captchaFile)) - -            for _ in xrange(1, 100, 1): -                response2 = getURL(self.API_URL, get={"apikey": self.getConfig("passkey"), "id": response, -                                                      "pyload": "1", "source": "pyload", -                                                      "action": "usercaptchacorrectdata"}) - -                if response2 != "": +        mouse = 1 if task.isPositional() else 0 +        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'         : mouse, +                     'file-upload-01': data, +                     'action'        : "usercaptchaupload"} + +        for _i in xrange(5): +            try: +                res = getURL(self.API_URL, post=post_data) +            except BadHeader, e: +                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": +                sleep(5) +            else: +                break +        else: +            self.logDebug("Could not send request: %s" % res) +            result = None -                time.sleep(3) +        self.logInfo(_("Captcha result for ticket %s: %s") % (res, result)) + +        task.setResult(result) -            result = response2 -            task.data["ticket"] = response -            self.logInfo("result %s : %s" % (response, result)) -            task.setResult(result) -        else: -            self.logError("Bad upload: %s" % response) -            return False      def newCaptchaTask(self, task):          if not task.isTextual() and not task.isPositional(): -            return False +            return          if not self.getConfig("passkey"): -            return False +            return          if self.core.isClientConnected() and not self.getConfig("force"): -            return False +            return -        if self.getCredits() > 0: -            task.handler.append(self) -            task.setWaiting(self.getConfig("timeout")) -            start_new_thread(self.processCaptcha, (task,)) +        credits = self.getCredits() -        else: -            self.logError(_("Your Captcha 9kw.eu Account has not enough credits")) +        if not credits: +            self.logError(_("Your captcha 9kw.eu account has not enough credits")) +            return -    def captchaCorrect(self, task): -        if "ticket" in task.data: +        queue = min(self.getConfig("queue"), 999) +        timeout = min(max(self.getConfig("timeout"), 300), 3999) +        pluginname = re.search(r'_([^_]*)_\d+.\w+', task.captchaFile).group(1) -            try: -                response = getURL(self.API_URL, -                                  post={"action": "usercaptchacorrectback", -                                        "apikey": self.getConfig("passkey"), -                                        "api_key": self.getConfig("passkey"), -                                        "correct": "1", -                                        "pyload": "1", -                                        "source": "pyload", -                                        "id": task.data["ticket"]}) -                self.logInfo("Request correct: %s" % response) +        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 -            except BadHeader, e: -                self.logError("Could not send correct request.", str(e)) +            sleep(10)          else: -            self.logError("No CaptchaID for correct request (task %s) found." % task) +            self.fail(_("Too many captchas in queue")) -    def captchaInvalid(self, task): -        if "ticket" in task.data: +        for opt in str(self.getConfig("hoster_options").split('|')): +            details = map(str.strip, opt.split(':')) -            try: -                response = getURL(self.API_URL, -                                  post={"action": "usercaptchacorrectback", -                                        "apikey": self.getConfig("passkey"), -                                        "api_key": self.getConfig("passkey"), -                                        "correct": "2", -                                        "pyload": "1", -                                        "source": "pyload", -                                        "id": task.data["ticket"]}) -                self.logInfo("Request refund: %s" % response) +            if not details or details[0].lower() != pluginname.lower(): +                continue -            except BadHeader, e: -                self.logError("Could not send refund request.", str(e)) +            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 + +            sleep(5)          else: -            self.logError("No CaptchaID for not correct request (task %s) found." % task) +            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/module/plugins/hooks/CaptchaBrotherhood.py b/module/plugins/hooks/CaptchaBrotherhood.py index 23d71ff5f..b6e38d8bb 100644 --- a/module/plugins/hooks/CaptchaBrotherhood.py +++ b/module/plugins/hooks/CaptchaBrotherhood.py @@ -1,76 +1,80 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay, RaNaN, zoidberg -"""  from __future__ import with_statement -from thread import start_new_thread - -import pycurl  import StringIO -from urllib import urlencode +import pycurl + +try: +    from PIL import Image +except ImportError: +    import Image + +from thread import start_new_thread  from time import sleep -from PIL import Image +from urllib import urlencode  from module.network.RequestFactory import getURL, getRequest  from module.plugins.Hook import Hook  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" -    __version__ = "0.05" -    __description__ = """Send captchas to CaptchaBrotherhood.com""" -    __config__ = [("activated", "bool", "Activated", False), -                  ("username", "str", "Username", ""), +    __name__    = "CaptchaBrotherhood" +    __type__    = "hook" +    __version__ = "0.06" + +    __config__ = [("username", "str", "Username", ""),                    ("force", "bool", "Force CT even if client is connected", False),                    ("passkey", "password", "Password", "")] -    __author_name__ = ("RaNaN", "zoidberg") -    __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz") + +    __description__ = """Send captchas to CaptchaBrotherhood.com""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org"), +                       ("zoidberg", "zoidberg@mujmail.cz")] +      API_URL = "http://www.captchabrotherhood.com/" + +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass + +      def setup(self): -        self.info = {} +        self.info = {}  #@TODO: Remove in 0.4.10 +      def getCredits(self): -        response = getURL(self.API_URL + "askCredits.aspx", -                          get={"username": self.getConfig("username"), "password": self.getConfig("passkey")}) -        if not response.startswith("OK"): -            raise CaptchaBrotherhoodException(response) +        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(response[3:]) +            credits = int(res[3:])              self.logInfo(_("%d credits left") % credits) -            self.info["credits"] = credits +            self.info['credits'] = credits              return credits +      def submit(self, captcha, captchaType="file", match=None):          try:              img = Image.open(captcha) @@ -102,34 +106,36 @@ class CaptchaBrotherhood(Hook):          try:              req.c.perform() -            response = req.getResponse() +            res = req.getResponse()          except Exception, e:              raise CaptchaBrotherhoodException("Submit captcha image failed")          req.close() -        if not response.startswith("OK"): -            raise CaptchaBrotherhoodException(response[1]) +        if not res.startswith("OK"): +            raise CaptchaBrotherhoodException(res[1]) -        ticket = response[3:] +        ticket = res[3:] -        for _ in xrange(15): +        for _i in xrange(15):              sleep(5) -            response = self.get_api("askCaptchaResult", ticket) -            if response.startswith("OK-answered"): -                return ticket, response[12:] +            res = self.get_api("askCaptchaResult", ticket) +            if res.startswith("OK-answered"): +                return ticket, res[12:]          raise CaptchaBrotherhoodException("No solution received in time") +      def get_api(self, api, ticket): -        response = getURL("%s%s.aspx" % (self.API_URL, api), +        res = getURL("%s%s.aspx" % (self.API_URL, api),                            get={"username": self.getConfig("username"),                                 "password": self.getConfig("passkey"),                                 "captchaID": ticket}) -        if not response.startswith("OK"): -            raise CaptchaBrotherhoodException("Unknown response: %s" % response) +        if not res.startswith("OK"): +            raise CaptchaBrotherhoodException("Unknown response: %s" % res) + +        return res -        return response      def newCaptchaTask(self, task):          if "service" in task.data: @@ -150,11 +156,13 @@ class CaptchaBrotherhood(Hook):              task.setWaiting(100)              start_new_thread(self.processCaptcha, (task,))          else: -            self.logInfo("Your CaptchaBrotherhood Account has not enough credits") +            self.logInfo(_("Your CaptchaBrotherhood Account has not enough credits")) +      def captchaInvalid(self, task):          if task.data['service'] == self.__name__ and "ticket" in task.data: -            response = self.get_api("complainCaptcha", task.data['ticket']) +            res = self.get_api("complainCaptcha", task.data['ticket']) +      def processCaptcha(self, task):          c = task.captchaFile @@ -164,5 +172,5 @@ class CaptchaBrotherhood(Hook):              task.error = e.getCode()              return -        task.data["ticket"] = ticket +        task.data['ticket'] = ticket          task.setResult(result) diff --git a/module/plugins/hooks/CaptchaTrader.py b/module/plugins/hooks/CaptchaTrader.py deleted file mode 100644 index 051dc6c2b..000000000 --- a/module/plugins/hooks/CaptchaTrader.py +++ /dev/null @@ -1,157 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay, RaNaN -""" - -from thread import start_new_thread -from pycurl import FORM_FILE, LOW_SPEED_TIME - -from module.common.json_layer import json_loads -from module.network.RequestFactory import getURL, getRequest -from module.network.HTTPRequest import BadHeader -from module.plugins.Hook import Hook - -PYLOAD_KEY = "9f65e7f381c3af2b076ea680ae96b0b7" - - -class CaptchaTraderException(Exception): -    def __init__(self, err): -        self.err = err - -    def getCode(self): -        return self.err - -    def __str__(self): -        return "<CaptchaTraderException %s>" % self.err - -    def __repr__(self): -        return "<CaptchaTraderException %s>" % self.err - - -class CaptchaTrader(Hook): -    __name__ = "CaptchaTrader" -    __version__ = "0.16" -    __description__ = """Send captchas to captchatrader.com""" -    __config__ = [("activated", "bool", "Activated", False), -                  ("username", "str", "Username", ""), -                  ("force", "bool", "Force CT even if client is connected", False), -                  ("passkey", "password", "Password", "")] -    __author_name__ = "RaNaN" -    __author_mail__ = "RaNaN@pyload.org" - -    SUBMIT_URL = "http://api.captchatrader.com/submit" -    RESPOND_URL = "http://api.captchatrader.com/respond" -    GETCREDITS_URL = "http://api.captchatrader.com/get_credits/username:%(user)s/password:%(password)s/" - -    def setup(self): -        self.info = {} - -    def getCredits(self): -        json = getURL(CaptchaTrader.GETCREDITS_URL % {"user": self.getConfig("username"), -                                                      "password": self.getConfig("passkey")}) -        response = json_loads(json) -        if response[0] < 0: -            raise CaptchaTraderException(response[1]) -        else: -            self.logInfo(_("%s credits left") % response[1]) -            self.info["credits"] = response[1] -            return response[1] - -    def submit(self, captcha, captchaType="file", match=None): -        if not PYLOAD_KEY: -            raise CaptchaTraderException("No API Key Specified!") - -        #if type(captcha) == str and captchaType == "file": -        #    raise CaptchaTraderException("Invalid Type") -        assert captchaType in ("file", "url-jpg", "url-jpeg", "url-png", "url-bmp") - -        req = getRequest() - -        #raise timeout threshold -        req.c.setopt(LOW_SPEED_TIME, 80) - -        try: -            json = req.load(CaptchaTrader.SUBMIT_URL, post={"api_key": PYLOAD_KEY, -                                                            "username": self.getConfig("username"), -                                                            "password": self.getConfig("passkey"), -                                                            "value": (FORM_FILE, captcha), -                                                            "type": captchaType}, multipart=True) -        finally: -            req.close() - -        response = json_loads(json) -        if response[0] < 0: -            raise CaptchaTraderException(response[1]) - -        ticket = response[0] -        result = response[1] -        self.logDebug("result %s : %s" % (ticket, result)) - -        return ticket, result - -    def respond(self, ticket, success): -        try: -            json = getURL(CaptchaTrader.RESPOND_URL, post={"is_correct": 1 if success else 0, -                                                           "username": self.getConfig("username"), -                                                           "password": self.getConfig("passkey"), -                                                           "ticket": ticket}) - -            response = json_loads(json) -            if response[0] < 0: -                raise CaptchaTraderException(response[1]) - -        except BadHeader, e: -            self.logError(_("Could not send response."), str(e)) - -    def newCaptchaTask(self, task): -        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.setWaiting(100) -            start_new_thread(self.processCaptcha, (task,)) - -        else: -            self.logInfo(_("Your CaptchaTrader Account has not enough credits")) - -    def captchaCorrect(self, task): -        if "ticket" in task.data: -            ticket = task.data["ticket"] -            self.respond(ticket, True) - -    def captchaInvalid(self, task): -        if "ticket" in task.data: -            ticket = task.data["ticket"] -            self.respond(ticket, False) - -    def processCaptcha(self, task): -        c = task.captchaFile -        try: -            ticket, result = self.submit(c) -        except CaptchaTraderException, e: -            task.error = e.getCode() -            return - -        task.data["ticket"] = ticket -        task.setResult(result) diff --git a/module/plugins/hooks/Checksum.py b/module/plugins/hooks/Checksum.py index af37d69e6..8d9f8f981 100644 --- a/module/plugins/hooks/Checksum.py +++ b/module/plugins/hooks/Checksum.py @@ -1,31 +1,16 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  from __future__ import with_statement +  import hashlib +import re  import zlib +  from os import remove  from os.path import getsize, isfile, splitext -import re -from module.utils import save_join, fs_encode  from module.plugins.Hook import Hook +from module.utils import save_join, fs_encode  def computeChecksum(local_file, algorithm): @@ -33,7 +18,7 @@ def computeChecksum(local_file, algorithm):          h = getattr(hashlib, algorithm)()          with open(local_file, 'rb') as f: -            for chunk in iter(lambda: f.read(128 * h.block_size), b''): +            for chunk in iter(lambda: f.read(128 * h.block_size), ''):                  h.update(chunk)          return h.hexdigest() @@ -43,7 +28,7 @@ def computeChecksum(local_file, algorithm):          last = 0          with open(local_file, 'rb') as f: -            for chunk in iter(lambda: f.read(8192), b''): +            for chunk in iter(lambda: f.read(8192), ''):                  last = hf(chunk, last)          return "%x" % last @@ -53,32 +38,46 @@ def computeChecksum(local_file, algorithm):  class Checksum(Hook): -    __name__ = "Checksum" -    __version__ = "0.12" -    __description__ = """Verify downloaded file size and checksum""" -    __config__ = [("activated", "bool", "Activated", False), +    __name__    = "Checksum" +    __type__    = "hook" +    __version__ = "0.15" + +    __config__ = [("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)] -    __author_name__ = ("zoidberg", "Walter Purcaro") -    __author_mail__ = ("zoidberg@mujmail.cz", "vuolter@gmail.com") + +    __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>.+)$'} +    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>.+)$'} + + +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass +      def coreReady(self): -        if not self.config['general']['checksum']: -            self.logInfo("Checksum validation is disabled in general configuration") +        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'] +        self.formats = self.algorithms + ["sfv", "crc", "hash"] +      def downloadFinished(self, pyfile):          """ @@ -87,10 +86,15 @@ class Checksum(Hook):          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)): +        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)): + +        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() +          else:              return @@ -111,12 +115,12 @@ class Checksum(Hook):              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.logWarning(_("File %s has incorrect size: %d B (%d expected)") % (pyfile.name, file_size, api_size))                  self.checkFailed(pyfile, local_file, "Incorrect file size")              del data['size']          # validate checksum -        if data and self.config['general']['checksum']: +        if data and self.getConfig("check_checksum"):              if "checksum" in data:                  data['md5'] = data['checksum'] @@ -125,17 +129,18 @@ class Checksum(Hook):                      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).' % +                            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)" % +                            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: %s" % key.upper()) +                        self.logWarning(_("Unsupported hashing algorithm"), key.upper())              else: -                self.logWarning("Unable to validate checksum for file %s" % pyfile.name) +                self.logWarning(_("Unable to validate checksum for file: ") + pyfile.name) +      def checkFailed(self, pyfile, local_file, msg):          check_action = self.getConfig("check_action") @@ -145,26 +150,26 @@ class Checksum(Hook):              if pyfile.plugin.retries < max_tries:                  if local_file:                      remove(local_file) -                pyfile.plugin.retry(max_tries=max_tries, wait_time=self.getConfig("wait_time"), reason=msg) +                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 = save_join(self.config['general']['download_folder'], pypack.folder, "")          for link in pypack.getChildren().itervalues(): -            file_type = splitext(link["name"])[1][1:].lower() -            #self.logDebug(link, file_type) +            file_type = splitext(link['name'])[1][1:].lower()              if file_type not in self.formats:                  continue -            hash_file = fs_encode(save_join(download_folder, link["name"])) +            hash_file = fs_encode(save_join(download_folder, link['name']))              if not isfile(hash_file): -                self.logWarning("File not found: %s" % link["name"]) +                self.logWarning(_("File not found"), link['name'])                  continue              with open(hash_file) as f: @@ -172,14 +177,14 @@ class Checksum(Hook):              for m in re.finditer(self.regexps.get(file_type, self.regexps['default']), text):                  data = m.groupdict() -                self.logDebug(link["name"], data) +                self.logDebug(link['name'], data) -                local_file = fs_encode(save_join(download_folder, data["name"])) +                local_file = fs_encode(save_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)) +                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"])) +                    self.logWarning(_("%s checksum for file %s does not match (%s != %s)") % +                                   (algorithm, data['NAME'], checksum, data['HASH'])) diff --git a/module/plugins/hooks/ClickAndLoad.py b/module/plugins/hooks/ClickAndLoad.py index 002fd4cd7..04aac2f10 100644 --- a/module/plugins/hooks/ClickAndLoad.py +++ b/module/plugins/hooks/ClickAndLoad.py @@ -1,52 +1,11 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: RaNaN -    @interface-version: 0.2 -""" -  import socket  import thread  from module.plugins.Hook import Hook -class ClickAndLoad(Hook): -    __name__ = "ClickAndLoad" -    __version__ = "0.22" -    __description__ = """Gives abillity to use jd's click and load. depends on webinterface""" -    __config__ = [("activated", "bool", "Activated", True), -                  ("extern", "bool", "Allow external link adding", False)] -    __author_name__ = ("RaNaN", "mkaay") -    __author_mail__ = ("RaNaN@pyload.de", "mkaay@mkaay.de") - -    def coreReady(self): -        self.port = int(self.config['webinterface']['port']) -        if self.config['webinterface']['activated']: -            try: -                if self.getConfig("extern"): -                    ip = "0.0.0.0" -                else: -                    ip = "127.0.0.1" - -                thread.start_new_thread(proxy, (self, ip, self.port, 9666)) -            except: -                self.logError("ClickAndLoad port already in use.") - -  def proxy(self, *settings):      thread.start_new_thread(server, (self,) + settings)      lock = thread.allocate_lock() @@ -88,3 +47,31 @@ def forward(source, destination):          else:              #source.shutdown(socket.SHUT_RD)              destination.shutdown(socket.SHUT_WR) + + +class ClickAndLoad(Hook): +    __name__    = "ClickAndLoad" +    __type__    = "hook" +    __version__ = "0.24" + +    __config__ = [("activated", "bool", "Activated", True), +                  ("extern", "bool", "Allow external link adding", False)] + +    __description__ = """Click'N'Load hook plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.de"), +                       ("mkaay", "mkaay@mkaay.de")] + + +    def coreReady(self): +        self.port = int(self.config['webinterface']['port']) +        if self.config['webinterface']['activated']: +            try: +                if self.getConfig("extern"): +                    ip = "0.0.0.0" +                else: +                    ip = "127.0.0.1" + +                thread.start_new_thread(proxy, (self, ip, self.port, 9666)) +            except: +                self.logError(_("ClickAndLoad port already in use")) diff --git a/module/plugins/hooks/DeathByCaptcha.py b/module/plugins/hooks/DeathByCaptcha.py index f7bc1b90f..f03ac4567 100644 --- a/module/plugins/hooks/DeathByCaptcha.py +++ b/module/plugins/hooks/DeathByCaptcha.py @@ -1,33 +1,18 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay, RaNaN, zoidberg -"""  from __future__ import with_statement -from thread import start_new_thread +import re + +from base64 import b64encode  from pycurl import FORM_FILE, HTTPHEADER +from thread import start_new_thread  from time import sleep -from base64 import b64encode -import re -from module.network.RequestFactory import getRequest +from module.common.json_layer import json_loads  from module.network.HTTPRequest import BadHeader +from module.network.RequestFactory import getRequest  from module.plugins.Hook import Hook -from module.common.json_layer import json_loads  class DeathByCaptchaException(Exception): @@ -40,40 +25,56 @@ class DeathByCaptchaException(Exception):                    '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" -    __version__ = "0.03" -    __description__ = """Send captchas to DeathByCaptcha.com""" -    __config__ = [("activated", "bool", "Activated", False), -                  ("username", "str", "Username", ""), +    __name__    = "DeathByCaptcha" +    __type__    = "hook" +    __version__ = "0.04" + +    __config__ = [("username", "str", "Username", ""),                    ("passkey", "password", "Password", ""),                    ("force", "bool", "Force DBC even if client is connected", False)] -    __author_name__ = ("RaNaN", "zoidberg") -    __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz") + +    __description__ = """Send captchas to DeathByCaptcha.com""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org"), +                       ("zoidberg", "zoidberg@mujmail.cz")] +      API_URL = "http://api.dbcapi.me/api/" + +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass + +      def setup(self): -        self.info = {} +        self.info = {}  #@TODO: Remove in 0.4.10 +      def call_api(self, api="captcha", post=False, multipart=False):          req = getRequest() @@ -85,18 +86,18 @@ class DeathByCaptcha(Hook):              post.update({"username": self.getConfig("username"),                           "password": self.getConfig("passkey")}) -        response = None +        res = None          try:              json = req.load("%s%s" % (self.API_URL, api),                              post=post,                              multipart=multipart)              self.logDebug(json) -            response = json_loads(json) +            res = json_loads(json) -            if "error" in response: -                raise DeathByCaptchaException(response['error']) -            elif "status" not in response: -                raise DeathByCaptchaException(str(response)) +            if "error" in res: +                raise DeathByCaptchaException(res['error']) +            elif "status" not in res: +                raise DeathByCaptchaException(str(res))          except BadHeader, e:              if 403 == e.code: @@ -113,27 +114,30 @@ class DeathByCaptcha(Hook):          finally:              req.close() -        return response +        return res +      def getCredits(self): -        response = self.call_api("user", True) +        res = self.call_api("user", True) -        if 'is_banned' in response and response['is_banned']: +        if 'is_banned' in res and res['is_banned']:              raise DeathByCaptchaException('banned') -        elif 'balance' in response and 'rate' in response: -            self.info.update(response) +        elif 'balance' in res and 'rate' in res: +            self.info.update(res)          else: -            raise DeathByCaptchaException(response) +            raise DeathByCaptchaException(res) +      def getStatus(self): -        response = self.call_api("status", False) +        res = self.call_api("status", False) -        if 'is_service_overloaded' in response and response['is_service_overloaded']: +        if 'is_service_overloaded' in res and res['is_service_overloaded']:              raise DeathByCaptchaException('service-overload') +      def submit(self, captcha, captchaType="file", match=None): -        #workaround multipart-post bug in HTTPRequest.py  -        if re.match("^[A-Za-z0-9]*$", self.getConfig("passkey")): +        #workaround multipart-post bug in HTTPRequest.py +        if re.match("^\w*$", self.getConfig("passkey")):              multipart = True              data = (FORM_FILE, captcha)          else: @@ -142,25 +146,26 @@ class DeathByCaptcha(Hook):                  data = f.read()              data = "base64:" + b64encode(data) -        response = self.call_api("captcha", {"captchafile": data}, multipart) +        res = self.call_api("captcha", {"captchafile": data}, multipart) -        if "captcha" not in response: -            raise DeathByCaptchaException(response) -        ticket = response['captcha'] +        if "captcha" not in res: +            raise DeathByCaptchaException(res) +        ticket = res['captcha'] -        for _ in xrange(24): +        for _i in xrange(24):              sleep(5) -            response = self.call_api("captcha/%d" % ticket, False) -            if response['text'] and response['is_correct']: +            res = self.call_api("captcha/%d" % ticket, False) +            if res['text'] and res['is_correct']:                  break          else:              raise DeathByCaptchaException('timed-out') -        result = response['text'] -        self.logDebug("result %s : %s" % (ticket, result)) +        result = res['text'] +        self.logDebug("Result %s : %s" % (ticket, result))          return ticket, result +      def newCaptchaTask(self, task):          if "service" in task.data:              return False @@ -181,9 +186,10 @@ class DeathByCaptcha(Hook):              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)) +        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) @@ -191,15 +197,19 @@ class DeathByCaptcha(Hook):              task.setWaiting(180)              start_new_thread(self.processCaptcha, (task,)) +      def captchaInvalid(self, task):          if task.data['service'] == self.__name__ and "ticket" in task.data:              try: -                response = self.call_api("captcha/%d/report" % task.data["ticket"], True) +                res = self.call_api("captcha/%d/report" % task.data['ticket'], True) +              except DeathByCaptchaException, e:                  self.logError(e.getDesc()) +              except Exception, e:                  self.logError(e) +      def processCaptcha(self, task):          c = task.captchaFile          try: @@ -209,5 +219,5 @@ class DeathByCaptcha(Hook):              self.logError(e.getDesc())              return -        task.data["ticket"] = ticket +        task.data['ticket'] = ticket          task.setResult(result) diff --git a/module/plugins/hooks/DebridItaliaCom.py b/module/plugins/hooks/DebridItaliaCom.py index fb6be674f..d18be5384 100644 --- a/module/plugins/hooks/DebridItaliaCom.py +++ b/module/plugins/hooks/DebridItaliaCom.py @@ -1,41 +1,25 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -from module.plugins.internal.MultiHoster import MultiHoster - - -class DebridItaliaCom(MultiHoster): -    __name__ = "DebridItaliaCom" -    __version__ = "0.07" -    __type__ = "hook" -    __config__ = [("activated", "bool", "Activated", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", ""), -                  ("unloadFailing", "bool", "Revert to standard download if download fails", False), + +import re + +from module.plugins.internal.MultiHook import MultiHook + + +class DebridItaliaCom(MultiHook): +    __name__    = "DebridItaliaCom" +    __type__    = "hook" +    __version__ = "0.11" + +    __config__ = [("mode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", ""), +                  ("revertfailed", "bool", "Revert to standard download if download fails", False),                    ("interval", "int", "Reload interval in hours (0 to disable)", 24)]      __description__ = """Debriditalia.com hook plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" - -    def getHoster(self): -        return ["netload.in", "hotfile.com", "rapidshare.com", "multiupload.com", -                "uploading.com", "megashares.com", "crocko.com", "filepost.com", -                "bitshare.com", "share-links.biz", "putlocker.com", "uploaded.to", -                "speedload.org", "rapidgator.net", "likeupload.net", "cyberlocker.ch", -                "depositfiles.com", "extabit.com", "filefactory.com", "sharefiles.co", -                "ryushare.com", "tusfiles.net", "nowvideo.co", "cloudzer.net", "letitbit.net", -                "easybytez.com", "uptobox.com", "ddlstorage.com"] +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    def getHosters(self): +        return self.getURL("http://debriditalia.com/api.php", get={'hosts': ""}).replace('"', '').split(',') diff --git a/module/plugins/hooks/DeleteFinished.py b/module/plugins/hooks/DeleteFinished.py index 3bc98a7b3..5d2b78d50 100644 --- a/module/plugins/hooks/DeleteFinished.py +++ b/module/plugins/hooks/DeleteFinished.py @@ -1,84 +1,79 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. +from module.database import style +from module.plugins.Hook import Hook -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. +class DeleteFinished(Hook): +    __name__    = "DeleteFinished" +    __type__    = "hook" +    __version__ = "1.11" -    @author: Walter Purcaro -""" +    __config__ = [('activated', 'bool', 'Activated', 'False'), +                  ('interval', 'int', 'Delete every (hours)', '72'), +                  ('deloffline', 'bool', 'Delete packages with offline links', 'False')] -from module.database import style -from module.plugins.Hook import Hook +    __description__ = """Automatically delete all finished packages from queue""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] -class DeleteFinished(Hook): -    __name__ = 'DeleteFinished' -    __version__ = '1.09' -    __description__ = 'Automatically delete all finished packages from queue' -    __config__ = [ -        ('activated', 'bool', 'Activated', 'False'), -        ('interval', 'int', 'Delete every (hours)', '72'), -        ('deloffline', 'bool', 'Delete packages with offline links', 'False') -    ] -    __author_name__ = ('Walter Purcaro') -    __author_mail__ = ('vuolter@gmail.com') +    # event_list = ["pluginConfigChanged"] +      ## overwritten methods ##      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')) +            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: +        if name == "interval" and value != self.interval:              self.interval = value * 3600              self.initPeriodical() +      def unload(self):          self.removeEvent('packageFinished', self.wakeup) +      def coreReady(self):          self.info = {'sleep': True}          interval = self.getConfig('interval') -        self.pluginConfigChanged('DeleteFinished', 'interval', interval) +        self.pluginConfigChanged(self.__name__, 'interval', interval)          self.addEvent('packageFinished', self.wakeup) +      ## 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.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.m.events:              if func in self.m.events[event]: -                self.logDebug('Function already registered %s' % func) +                self.logDebug("Function already registered", func)              else:                  self.m.events[event].append(func)          else:              self.m.events[event] = [func] +      def setup(self):          self.m = self.manager          self.removeEvent = self.m.removeEvent diff --git a/module/plugins/hooks/DownloadScheduler.py b/module/plugins/hooks/DownloadScheduler.py index 41a20e5d1..4996e212d 100644 --- a/module/plugins/hooks/DownloadScheduler.py +++ b/module/plugins/hooks/DownloadScheduler.py @@ -1,44 +1,40 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -    Original idea by new.cze -""" -  import re +  from time import localtime  from module.plugins.Hook import Hook  class DownloadScheduler(Hook): -    __name__ = "DownloadScheduler" -    __version__ = "0.21" -    __description__ = """Download Scheduler""" -    __config__ = [("activated", "bool", "Activated", False), -                  ("timetable", "str", "List time periods as hh:mm full or number(kB/s)", +    __name__    = "DownloadScheduler" +    __type__    = "hook" +    __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)] -    __author_name__ = ("zoidberg", "stickell") -    __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it") + +    __description__ = """Download Scheduler""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass +      def setup(self):          self.cb = None  # callback to scheduler job; will be by removed hookmanager when hook unloaded +      def coreReady(self):          self.updateSchedule() +      def updateSchedule(self, schedule=None):          if schedule is None:              schedule = self.getConfig("timetable") @@ -46,7 +42,7 @@ class DownloadScheduler(Hook):          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") +            self.logError(_("Invalid schedule"))              return          t0 = localtime() @@ -66,10 +62,11 @@ class DownloadScheduler(Hook):                  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.logInfo(_("Stopping download server. (Running downloads will %sbe aborted.)") % '' if abort else _('not '))              self.core.api.pauseServer()              if abort:                  self.core.api.stopAllDownloads() @@ -77,10 +74,10 @@ class DownloadScheduler(Hook):              self.core.api.unpauseServer()              if speed > 0: -                self.logInfo("Setting download speed to %d kB/s" % speed) +                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.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/module/plugins/hooks/EasybytezCom.py b/module/plugins/hooks/EasybytezCom.py index a3a2dcb92..16149580c 100644 --- a/module/plugins/hooks/EasybytezCom.py +++ b/module/plugins/hooks/EasybytezCom.py @@ -2,33 +2,38 @@  import re -from module.plugins.internal.MultiHoster import MultiHoster +from module.plugins.internal.MultiHook import MultiHook -class EasybytezCom(MultiHoster): -    __name__ = "EasybytezCom" -    __version__ = "0.03" -    __type__ = "hook" -    __config__ = [("activated", "bool", "Activated", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", "")] +class EasybytezCom(MultiHook): +    __name__    = "EasybytezCom" +    __type__    = "hook" +    __version__ = "0.05" + +    __config__ = [("mode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", "")] +      __description__ = """EasyBytez.com hook plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    def getHoster(self): +    def getHosters(self):          self.account = self.core.accountManager.getAccountPlugin(self.__name__)          user = self.account.selectAccount()[0]          try: -            req = self.account.getAccountRequest(user) +            req  = self.account.getAccountRequest(user)              page = req.load("http://www.easybytez.com") -            found = re.search(r'</textarea>\s*Supported sites:(.*)', page) -            return found.group(1).split(',') +            hosters = re.search(r'</textarea>\s*Supported sites:(.*)', page).group(1).split(',') +          except Exception, e: +            self.logWarning(_("Unable to load supported hoster list, using last known"))              self.logDebug(e) -            self.logWarning("Unable to load supported hoster list, using last known") -            return ['bitshare.com', 'crocko.com', 'ddlstorage.com', 'depositfiles.com', 'extabit.com', 'hotfile.com', -                    'mediafire.com', 'netload.in', 'rapidgator.net', 'rapidshare.com', 'uploading.com', 'uload.to', -                    'uploaded.to'] + +            hosters = ["bitshare.com", "crocko.com", "ddlstorage.com", "depositfiles.com", "extabit.com", "hotfile.com", +                       "mediafire.com", "netload.in", "rapidgator.net", "rapidshare.com", "uploading.com", "uload.to", +                       "uploaded.to"] +        finally: +            return hosters diff --git a/module/plugins/hooks/Ev0InFetcher.py b/module/plugins/hooks/Ev0InFetcher.py deleted file mode 100644 index 1e2b62062..000000000 --- a/module/plugins/hooks/Ev0InFetcher.py +++ /dev/null @@ -1,90 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay -""" -from time import mktime, time - -from module.lib import feedparser -from module.plugins.Hook import Hook - - -class Ev0InFetcher(Hook): -    __name__ = "Ev0InFetcher" -    __version__ = "0.21" -    __description__ = """Checks rss feeds for Ev0.in""" -    __config__ = [("activated", "bool", "Activated", False), -                  ("interval", "int", "Check interval in minutes", 10), -                  ("queue", "bool", "Move new shows directly to Queue", False), -                  ("shows", "str", "Shows to check for (comma seperated)", ""), -                  ("quality", "xvid;x264;rmvb", "Video Format", "xvid"), -                  ("hoster", "str", "Hoster to use (comma seperated)", -                   "NetloadIn,RapidshareCom,MegauploadCom,HotfileCom")] -    __author_name__ = "mkaay" -    __author_mail__ = "mkaay@mkaay.de" - -    def setup(self): -        self.interval = self.getConfig("interval") * 60 - -    def filterLinks(self, links): -        results = self.core.pluginManager.parseUrls(links) -        sortedLinks = {} - -        for url, hoster in results: -            if hoster not in sortedLinks: -                sortedLinks[hoster] = [] -            sortedLinks[hoster].append(url) - -        for h in self.getConfig("hoster").split(","): -            try: -                return sortedLinks[h.strip()] -            except: -                continue -        return [] - -    def periodical(self): -        def normalizefiletitle(filename): -            filename = filename.replace('.', ' ') -            filename = filename.replace('_', ' ') -            filename = filename.lower() -            return filename - -        shows = [s.strip() for s in self.getConfig("shows").split(",")] - -        feed = feedparser.parse("http://feeds.feedburner.com/ev0in/%s?format=xml" % self.getConfig("quality")) - -        showStorage = {} -        for show in shows: -            showStorage[show] = int(self.getStorage("show_%s_lastfound" % show, 0)) - -        found = False -        for item in feed['items']: -            for show, lastfound in showStorage.iteritems(): -                if show.lower() in normalizefiletitle(item['title']) and lastfound < int(mktime(item.date_parsed)): -                    links = self.filterLinks(item['description'].split("<br />")) -                    packagename = item['title'].encode("utf-8") -                    self.logInfo("Ev0InFetcher: new episode '%s' (matched '%s')" % (packagename, show)) -                    self.core.api.addPackage(packagename, links, 1 if self.getConfig("queue") else 0) -                    self.setStorage("show_%s_lastfound" % show, int(mktime(item.date_parsed))) -                    found = True -        if not found: -            #self.logDebug("Ev0InFetcher: no new episodes found") -            pass - -        for show, lastfound in self.getStorage().iteritems(): -            if int(lastfound) > 0 and int(lastfound) + (3600 * 24 * 30) < int(time()): -                self.delStorage("show_%s_lastfound" % show) -                self.logDebug("Ev0InFetcher: cleaned '%s' record" % show) diff --git a/module/plugins/hooks/ExpertDecoders.py b/module/plugins/hooks/ExpertDecoders.py index 7be30f86e..54de8eb53 100644 --- a/module/plugins/hooks/ExpertDecoders.py +++ b/module/plugins/hooks/ExpertDecoders.py @@ -1,82 +1,77 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay, RaNaN, zoidberg -"""  from __future__ import with_statement -from thread import start_new_thread +from base64 import b64encode  from pycurl import LOW_SPEED_TIME +from thread import start_new_thread  from uuid import uuid4 -from base64 import b64encode -from module.network.RequestFactory import getURL, getRequest  from module.network.HTTPRequest import BadHeader - +from module.network.RequestFactory import getURL, getRequest  from module.plugins.Hook import Hook  class ExpertDecoders(Hook): -    __name__ = "ExpertDecoders" -    __version__ = "0.01" -    __description__ = """Send captchas to expertdecoders.com""" -    __config__ = [("activated", "bool", "Activated", False), -                  ("force", "bool", "Force CT even if client is connected", False), +    __name__    = "ExpertDecoders" +    __type__    = "hook" +    __version__ = "0.03" + +    __config__ = [("force", "bool", "Force CT even if client is connected", False),                    ("passkey", "password", "Access key", "")] -    __author_name__ = ("RaNaN", "zoidberg") -    __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz") + +    __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" + +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass + +      def setup(self): -        self.info = {} +        self.info = {}  #@TODO: Remove in 0.4.10 +      def getCredits(self): -        response = getURL(self.API_URL, post={"key": self.getConfig("passkey"), "action": "balance"}) +        res = getURL(self.API_URL, post={"key": self.getConfig("passkey"), "action": "balance"}) -        if response.isdigit(): -            self.logInfo(_("%s credits left") % response) -            self.info["credits"] = credits = int(response) +        if res.isdigit(): +            self.logInfo(_("%s credits left") % res) +            self.info['credits'] = credits = int(res)              return credits          else: -            self.logError(response) +            self.logError(res)              return 0 +      def processCaptcha(self, task): -        task.data["ticket"] = ticket = uuid4() +        task.data['ticket'] = ticket = uuid4()          result = None          with open(task.captchaFile, 'rb') as f:              data = f.read()          data = b64encode(data) -        #self.logDebug("%s: %s : %s" % (ticket, task.captchaFile, data))          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"), +            result = req.load(self.API_URL, post={"action": "upload", "key": self.getConfig("passkey"),                                                     "file": data, "gen_task_id": ticket})          finally:              req.close() -        self.logDebug("result %s : %s" % (ticket, result)) +        self.logDebug("Result %s : %s" % (ticket, result))          task.setResult(result) +      def newCaptchaTask(self, task):          if not task.isTextual():              return False @@ -95,13 +90,14 @@ class ExpertDecoders(Hook):          else:              self.logInfo(_("Your ExpertDecoders Account has not enough credits")) +      def captchaInvalid(self, task):          if "ticket" in task.data:              try: -                response = getURL(self.API_URL, post={"action": "refund", "key": self.getConfig("passkey"), -                                                      "gen_task_id": task.data["ticket"]}) -                self.logInfo("Request refund: %s" % response) +                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.", str(e)) +                self.logError(_("Could not send refund request"), e) diff --git a/module/plugins/hooks/ExternalScripts.py b/module/plugins/hooks/ExternalScripts.py index 3d84dcc3d..a35e47c03 100644 --- a/module/plugins/hooks/ExternalScripts.py +++ b/module/plugins/hooks/ExternalScripts.py @@ -1,24 +1,8 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay -    @interface-version: 0.1 -""" -  import subprocess + +from itertools import chain  from os import listdir, access, X_OK, makedirs  from os.path import join, exists, basename, abspath @@ -27,21 +11,38 @@ from module.utils import save_join  class ExternalScripts(Hook): -    __name__ = "ExternalScripts" -    __version__ = "0.23" -    __description__ = """Run external scripts""" +    __name__    = "ExternalScripts" +    __type__    = "hook" +    __version__ = "0.25" +      __config__ = [("activated", "bool", "Activated", True)] -    __author_name__ = ("mkaay", "RaNaN", "spoob") -    __author_mail__ = ("mkaay@mkaay.de", "ranan@pyload.org", "spoob@pyload.org") -    event_list = ["unrarFinished", "allDownloadsFinished", "allDownloadsProcessed"] +    __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_extracted", "package_extracted", "all_archives_extracted", "all_archives_processed", +                  "allDownloadsFinished", "allDownloadsProcessed"] + + +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass +      def setup(self):          self.scripts = {} -        folders = ['download_preparing', 'download_finished', 'package_finished', -                   'before_reconnect', 'after_reconnect', 'unrar_finished', -                   'all_dls_finished', 'all_dls_processed'] +        folders = ["download_preparing", "download_finished", "all_downloads_finished", "all_downloads_processed", +                   "before_reconnect", "after_reconnect", +                   "package_finished", "package_extracted", +                   "archive_extracted", "all_archives_extracted", "all_archives_processed", +                   # deprecated folders +                   "unrar_finished", "all_dls_finished", "all_dls_processed"]          for folder in folders:              self.scripts[folder] = [] @@ -51,7 +52,8 @@ class ExternalScripts(Hook):          for script_type, names in self.scripts.iteritems():              if names: -                self.logInfo((_("Installed scripts for %s: ") % script_type) + ", ".join([basename(x) for x in names])) +                self.logInfo(_("Installed scripts for"), script_type, ", ".join([basename(x) for x in names])) +      def initPluginType(self, folder, path):          if not exists(path): @@ -70,48 +72,75 @@ class ExternalScripts(Hook):              self.scripts[folder].append(join(path, f)) +      def callScript(self, script, *args):          try:              cmd = [script] + [str(x) if not isinstance(x, basestring) else x for x in args] -            self.logDebug("Executing %(script)s: %(cmd)s" % {"script": abspath(script), "cmd": " ".join(cmd)}) +            self.logDebug("Executing", abspath(script), " ".join(cmd))              #output goes to pyload              subprocess.Popen(cmd, bufsize=-1)          except Exception, e: -            self.logError(_("Error in %(script)s: %(error)s") % {"script": basename(script), "error": str(e)}) +            self.logError(_("Error in %(script)s: %(error)s") % {"script": basename(script), "error": e}) +      def downloadPreparing(self, pyfile):          for script in self.scripts['download_preparing']:              self.callScript(script, pyfile.pluginname, pyfile.url, pyfile.id) +      def downloadFinished(self, pyfile): +        download_folder = self.config['general']['download_folder']          for script in self.scripts['download_finished']: -            self.callScript(script, pyfile.pluginname, pyfile.url, pyfile.name, -                            save_join(self.config['general']['download_folder'], -                                      pyfile.package().folder, pyfile.name), pyfile.id) +            filename = save_join(download_folder, pyfile.package().folder, pyfile.name) +            self.callScript(script, pyfile.pluginname, pyfile.url, pyfile.name, filename, pyfile.id) +      def packageFinished(self, pypack): +        download_folder = self.config['general']['download_folder']          for script in self.scripts['package_finished']: -            folder = self.config['general']['download_folder'] -            folder = save_join(folder, pypack.folder) - +            folder = save_join(download_folder, pypack.folder)              self.callScript(script, pypack.name, folder, pypack.password, pypack.id) +      def beforeReconnecting(self, ip):          for script in self.scripts['before_reconnect']:              self.callScript(script, ip) +      def afterReconnecting(self, ip):          for script in self.scripts['after_reconnect']:              self.callScript(script, ip) -    def unrarFinished(self, folder, fname): -        for script in self.scripts["unrar_finished"]: -            self.callScript(script, folder, fname) + +    def archive_extracted(self, pyfile, folder, filename, files): +        for script in self.scripts['archive_extracted']: +            self.callScript(script, folder, filename, files) +        for script in self.scripts['unrar_finished']:  #: deprecated +            self.callScript(script, folder, filename) + + +    def package_extracted(self, pypack): +        download_folder = self.config['general']['download_folder'] +        for script in self.scripts['package_extracted']: +            folder = save_join(download_folder, pypack.folder) +            self.callScript(script, pypack.name, folder, pypack.password, pypack.id) + + +    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) +      def allDownloadsFinished(self): -        for script in self.scripts["all_dls_finished"]: +        for script in chain(self.scripts['all_downloads_finished'], self.scripts['all_dls_finished']):              self.callScript(script) +      def allDownloadsProcessed(self): -        for script in self.scripts["all_dls_processed"]: +        for script in chain(self.scripts['all_downloads_processed'], self.scripts['all_dls_processed']):              self.callScript(script) diff --git a/module/plugins/hooks/ExtractArchive.py b/module/plugins/hooks/ExtractArchive.py index 12e53fe50..af78ffc93 100644 --- a/module/plugins/hooks/ExtractArchive.py +++ b/module/plugins/hooks/ExtractArchive.py @@ -1,19 +1,20 @@  # -*- coding: utf-8 -*- -import sys +from __future__ import with_statement +  import os +import sys + +from copy import copy  from os import remove, chmod, makedirs -from os.path import exists, basename, isfile, isdir, join +from os.path import exists, basename, isfile, isdir  from traceback import print_exc -from copy import copy  # monkey patch bug in python 2.6 and lower -# see http://bugs.python.org/issue6122 -# http://bugs.python.org/issue1236 -# http://bugs.python.org/issue1731717 +# 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": -    from subprocess import Popen      import errno +    from subprocess import Popen      def _eintr_retry_call(func, *args):          while True: @@ -24,6 +25,7 @@ if sys.version_info < (2, 7) and os.name != "nt":                      continue                  raise +      # unsued timeout option for older python version      def wait(self, timeout=0):          """Wait for child process to terminate.  Returns returncode @@ -44,44 +46,54 @@ if sys.version_info < (2, 7) and os.name != "nt":      Popen.wait = wait  if os.name != "nt": +    from grp import getgrnam      from os import chown      from pwd import getpwnam -    from grp import getgrnam -from module.utils import save_join, fs_encode  from module.plugins.Hook import Hook, threaded, Expose -from module.plugins.internal.AbstractExtractor import ArchiveError, CRCError, WrongPassword +from module.plugins.internal.Extractor import ArchiveError, CRCError, PasswordError +from module.utils import save_join, uniqify  class ExtractArchive(Hook): -    """ -    Provides: unrarFinished (folder, filename) -    """ -    __name__ = "ExtractArchive" -    __version__ = "0.16" +    __name__    = "ExtractArchive" +    __type__    = "hook" +    __version__ = "1.02" + +    __config__ = [("activated"    , "bool"  , "Activated"                                 , True                                                                     ), +                  ("fullpath"     , "bool"  , "Extract full path"                         , True                                                                     ), +                  ("overwrite"    , "bool"  , "Overwrite files"                           , False                                                                    ), +                  ("keepbroken"   , "bool"  , "Extract broken archives"                   , False                                                                    ), +                  ("repair"       , "bool"  , "Repair broken archives"                    , True                                                                     ), +                  ("passwordfile" , "file"  , "Store passwords in file"                   , "archive_password.txt"                                                   ), +                  ("delete"       , "bool"  , "Delete archive when successfully extracted", False                                                                    ), +                  ("subfolder"    , "bool"  , "Create subfolder for each package"         , False                                                                    ), +                  ("destination"  , "folder", "Extract files to"                          , ""                                                                       ), +                  ("extensions"   , "str"   , "Extract the following extensions"          , "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                                                                     ), +                  ("queue"        , "bool"  , "Wait for all downloads to be finished"     , True                                                                     ), +                  ("renice"       , "int"   , "CPU Priority"                              , 0                                                                        )] +      __description__ = """Extract different kind of archives""" -    __config__ = [("activated", "bool", "Activated", True), -                  ("fullpath", "bool", "Extract full path", True), -                  ("overwrite", "bool", "Overwrite files", True), -                  ("passwordfile", "file", "password file", "unrar_passwords.txt"), -                  ("deletearchive", "bool", "Delete archives when done", False), -                  ("subfolder", "bool", "Create subfolder for each package", False), -                  ("destination", "folder", "Extract files to", ""), -                  ("excludefiles", "str", "Exclude files from unpacking (seperated by ;)", ""), -                  ("recursive", "bool", "Extract archives in archvies", True), -                  ("queue", "bool", "Wait for all downloads to be finished", True), -                  ("renice", "int", "CPU Priority", 0)] -    __author_name__ = ("pyload Team", "AndroKev") -    __author_mail__ = ("admin<at>pyload.org", "@pyloadforum") +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] +      event_list = ["allDownloadsProcessed"] + +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass + +      def setup(self): -        self.plugins = [] +        self.plugins   = []          self.passwords = []          names = [] -        for p in ("UnRar", "UnZip"): +        for p in ("UnRar", "SevenZip", "UnZip"):              try:                  module = self.core.pluginManager.loadModule("internal", p)                  klass = getattr(module, p) @@ -93,12 +105,12 @@ class ExtractArchive(Hook):                  if e.errno == 2:                      self.logInfo(_("No %s installed") % p)                  else: -                    self.logWarning(_("Could not activate %s") % p, str(e)) +                    self.logWarning(_("Could not activate %s") % p, e)                      if self.core.debug:                          print_exc()              except Exception, e: -                self.logWarning(_("Could not activate %s") % p, str(e)) +                self.logWarning(_("Could not activate %s") % p, e)                  if self.core.debug:                      print_exc() @@ -110,138 +122,230 @@ class ExtractArchive(Hook):          # queue with package ids          self.queue = [] +      @Expose      def extractPackage(self, id):          """ Extract package with given id"""          self.manager.startThread(self.extract, [id]) +      def packageFinished(self, pypack): +        pid = pypack.id          if self.getConfig("queue"):              self.logInfo(_("Package %s queued for later extracting") % pypack.name) -            self.queue.append(pypack.id) +            self.queue.append(pid)          else: -            self.manager.startThread(self.extract, [pypack.id]) +            self.extractPackage(pid) +      @threaded      def allDownloadsProcessed(self, thread):          local = copy(self.queue) +          del self.queue[:] -        self.extract(local, thread) + +        if self.extract(local, thread):  #: check only if all gone fine, no failed reporting for now +            self.manager.dispatchEvent("all_archives_extracted") + +        self.manager.dispatchEvent("all_archives_processed") +      def extract(self, ids, thread=None): +        processed = [] +        extracted = [] +        failed    = [] + +        clearlist = lambda string: [x.lstrip('.') for x in string.replace(' ', '').replace(',', '|').replace(';', '|').split('|')] + +        destination  = self.getConfig("destination") +        subfolder    = self.getConfig("subfolder") +        fullpath     = self.getConfig("fullpath") +        overwrite    = self.getConfig("overwrite") +        extensions   = clearlist(self.getConfig("extensions")) +        excludefiles = clearlist(self.getConfig("excludefiles")) +        renice       = self.getConfig("renice") +        recursive    = self.getConfig("recursive") +        delete       = self.getConfig("delete") +        keepbroken   = self.getConfig("keepbroken") + +        if extensions: +            self.logDebug("Extensions allowed: %s" % "|.".join(extensions)) +          # reload from txt file          self.reloadPasswords()          # dl folder          dl = self.config['general']['download_folder'] -        extracted = [] -          #iterate packages -> plugins -> targets          for pid in ids:              p = self.core.files.getPackage(pid) -            self.logInfo(_("Check package %s") % p.name) +            self.logInfo(_("Check package: %s") % p.name)              if not p:                  continue              # determine output folder -            out = save_join(dl, p.folder, "") -            # force trailing slash +            out = save_join(dl, p.folder, destination, "")  #: force trailing slash -            if self.getConfig("destination") and self.getConfig("destination").lower() != "none": +            if subfolder: +                out = save_join(out, p.folder) -                out = save_join(dl, p.folder, self.getConfig("destination"), "") -                #relative to package folder if destination is relative, otherwise absolute path overwrites them +            if not exists(out): +                makedirs(out) -                if self.getConfig("subfolder"): -                    out = join(out, fs_encode(p.folder)) - -                if not exists(out): -                    makedirs(out) - -            files_ids = [(save_join(dl, p.folder, x["name"]), x["id"]) for x in p.getChildren().itervalues()] -            matched = False +            files_ids = [(save_join(dl, p.folder, x['name']), x['id']) for x in p.getChildren().itervalues()] +            matched   = False +            success   = True              # check as long there are unseen files              while files_ids:                  new_files_ids = [] +                if extensions: +                    files_ids = [(file, id) for file, id in files_ids if filter(lambda ext: file.endswith(ext), extensions)] +                  for plugin in self.plugins:                      targets = plugin.getTargets(files_ids) +                      if targets:                          self.logDebug("Targets for %s: %s" % (plugin.__name__, targets))                          matched = True +                      for target, fid in targets: -                        if target in extracted: +                        if target in processed:                              self.logDebug(basename(target), "skipped")                              continue -                        extracted.append(target)  # prevent extracting same file twice -                        klass = plugin(self, target, out, self.getConfig("fullpath"), self.getConfig("overwrite"), self.getConfig("excludefiles"), -                                       self.getConfig("renice")) -                        klass.init() +                        processed.append(target)  # prevent extracting same file twice + +                        self.logInfo(basename(target), _("Extract to: %s") % out) +                        try: +                            klass = plugin(self, +                                           target, +                                           out, +                                           p.password, +                                           fullpath, +                                           overwrite, +                                           excludefiles, +                                           renice, +                                           delete, +                                           keepbroken) +                            klass.init() + +                            new_files = self._extract(klass, fid, thread) + +                        except Exception, e: +                            self.logError(basename(target), e) +                            new_files = None + +                        if new_files is None: +                            self.logWarning(basename(target), _("No files extracted")) +                            success = False +                            continue -                        self.logInfo(basename(target), _("Extract to %s") % out) -                        new_files = self.startExtracting(klass, fid, p.password.strip().splitlines(), thread) -                        self.logDebug("Extracted: %s" % new_files) +                        self.logDebug("Extracted files: %s" % new_files)                          self.setPermissions(new_files)                          for file in new_files:                              if not exists(file): -                                self.logDebug("new file %s does not exists" % file) +                                self.logDebug("New file %s does not exists" % file)                                  continue -                            if self.getConfig("recursive") and isfile(file): +                            if recursive and isfile(file):                                  new_files_ids.append((file, fid))  # append as new target                  files_ids = new_files_ids  # also check extracted files -            if not matched: +            if matched: +                if success: +                    extracted.append(pid) +                    self.manager.dispatchEvent("package_extracted", p) +                else: +                    failed.append(pid) +                    self.manager.dispatchEvent("package_extract_failed", p) +            else:                  self.logInfo(_("No files found to extract")) -    def startExtracting(self, plugin, fid, passwords, thread): +            if not matched or not success and subfolder: +                try: +                    os.rmdir(out) +                except OSError: +                    pass + +        return True if not failed else False + + +    def _extract(self, plugin, fid, thread):          pyfile = self.core.files.getFile(fid) -        if not pyfile: -            return []          pyfile.setCustomStatus(_("extracting"))          thread.addActive(pyfile)  # keep this file until everything is done          try: -            progress = lambda x: pyfile.setProgress(x) -            success = False +            progress  = lambda x: pyfile.setProgress(x) +            encrypted = False +            passwords = self.getPasswords() + +            try: +                self.logInfo(basename(plugin.file), "Verifying...") + +                tmp_password    = plugin.password +                plugin.password = ""  #: Force verifying without password + +                plugin.verify() + +            except PasswordError: +                encrypted = True + +            except CRCError: +                self.logWarning(basename(plugin.file), _("Archive damaged")) + +                if not self.getConfig("repair"): +                    raise CRCError + +                elif plugin.repair(): +                    self.logInfo(basename(plugin.file), _("Successfully repaired")) + +                elif not self.getConfig("keepbroken"): +                    raise ArchiveError(_("Broken archive")) -            if not plugin.checkArchive(): +                else: +                    self.logInfo(basename(plugin.file), _("All OK")) + +            plugin.password = tmp_password + +            if not encrypted:                  plugin.extract(progress) -                success = True +              else:                  self.logInfo(basename(plugin.file), _("Password protected")) -                self.logDebug("Passwords: %s" % str(passwords)) -                pwlist = copy(self.getPasswords()) -                #remove already supplied pws from list (only local) -                for pw in passwords: -                    if pw in pwlist: -                        pwlist.remove(pw) +                if plugin.password: +                    passwords.insert(0, plugin.password) +                    passwords = uniqify(self.passwords) +                    self.logDebug("Password: %s" % plugin.password) +                else: +                    self.logDebug("No package password provided") -                for pw in passwords + pwlist: +                for pw in passwords:                      try:                          self.logDebug("Try password: %s" % pw) -                        if plugin.checkPassword(pw): -                            plugin.extract(progress, pw) + +                        if plugin.setPassword(pw): +                            plugin.extract(progress)                              self.addPassword(pw) -                            success = True                              break -                    except WrongPassword: -                        self.logDebug("Password was wrong") +                        else: +                            raise PasswordError -            if not success: -                self.logError(basename(plugin.file), _("Wrong password")) -                return [] +                    except PasswordError: +                        self.logDebug("Password was wrong") +                else: +                    raise PasswordError              if self.core.debug:                  self.logDebug("Would delete: %s" % ", ".join(plugin.getDeleteFiles())) -            if self.getConfig("deletearchive"): +            if self.getConfig("delete"):                  files = plugin.getDeleteFiles()                  self.logInfo(_("Deleting %s files") % len(files))                  for f in files: @@ -251,67 +355,87 @@ class ExtractArchive(Hook):                          self.logDebug("%s does not exists" % f)              self.logInfo(basename(plugin.file), _("Extracting finished")) -            self.manager.dispatchEvent("unrarFinished", plugin.out, plugin.file) -            return plugin.getExtractedFiles() +            extracted_files = plugin.getExtractedFiles() +            self.manager.dispatchEvent("archive_extracted", pyfile, plugin.out, plugin.file, extracted_files) + +            return extracted_files + +        except PasswordError: +            self.logError(basename(plugin.file), _("Wrong password" if passwords else "No password found")) +            plugin.password = "" -        except ArchiveError, e: -            self.logError(basename(plugin.file), _("Archive Error"), str(e))          except CRCError:              self.logError(basename(plugin.file), _("CRC Mismatch")) + +        except ArchiveError, e: +            self.logError(basename(plugin.file), _("Archive Error"), e) +          except Exception, e:              if self.core.debug:                  print_exc() -            self.logError(basename(plugin.file), _("Unknown Error"), str(e)) +            self.logError(basename(plugin.file), _("Unknown Error"), e) + +        self.manager.dispatchEvent("archive_extract_failed", pyfile) + +        self.logError(basename(plugin.file), _("Extract failed")) -        return []      @Expose      def getPasswords(self):          """ List of saved passwords """          return self.passwords +      def reloadPasswords(self): -        pwfile = self.getConfig("passwordfile") -        if not exists(pwfile): -            open(pwfile, "wb").close() +        passwordfile = self.getConfig("passwordfile") -        passwords = [] -        f = open(pwfile, "rb") -        for pw in f.read().splitlines(): -            passwords.append(pw) -        f.close() +        try: +            passwords = [] +            with open(passwordfile, "a+") as f: +                for pw in f.read().splitlines(): +                    passwords.append(pw) + +        except IOError, e: +            self.logError(e) + +        else: +            self.passwords = passwords -        self.passwords = passwords      @Expose      def addPassword(self, pw):          """  Adds a password to saved list""" -        pwfile = self.getConfig("passwordfile") +        passwordfile = self.getConfig("passwordfile") -        if pw in self.passwords: -            self.passwords.remove(pw)          self.passwords.insert(0, pw) +        self.passwords = uniqify(self.passwords) + +        try: +            with open(passwordfile, "wb") as f: +                for pw in self.passwords: +                    f.write(pw + '\n') + +        except IOError, e: +            self.logError(e) -        f = open(pwfile, "wb") -        for pw in self.passwords: -            f.write(pw + "\n") -        f.close()      def setPermissions(self, files):          for f in files:              if not exists(f):                  continue +              try: -                if self.config["permission"]["change_file"]: +                if self.config['permission']['change_file']:                      if isfile(f): -                        chmod(f, int(self.config["permission"]["file"], 8)) +                        chmod(f, int(self.config['permission']['file'], 8))                      elif isdir(f): -                        chmod(f, int(self.config["permission"]["folder"], 8)) +                        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] +                if self.config['permission']['change_dl'] and os.name != "nt": +                    uid = getpwnam(self.config['permission']['user'])[2] +                    gid = getgrnam(self.config['permission']['group'])[2]                      chown(f, uid, gid) +              except Exception, e:                  self.logWarning(_("Setting User and Group failed"), e) diff --git a/module/plugins/hooks/FastixRu.py b/module/plugins/hooks/FastixRu.py index 558da1b86..a4b423fb7 100644 --- a/module/plugins/hooks/FastixRu.py +++ b/module/plugins/hooks/FastixRu.py @@ -1,27 +1,27 @@  # -*- coding: utf-8 -*- -# should be working - -from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster  from module.common.json_layer import json_loads +from module.plugins.internal.MultiHook import MultiHook + +class FastixRu(MultiHook): +    __name__    = "FastixRu" +    __type__    = "hook" +    __version__ = "0.04" -class FastixRu(MultiHoster): -    __name__ = "FastixRu" -    __version__ = "0.02" -    __type__ = "hook" -    __config__ = [("activated", "bool", "Activated", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), -                  ("unloadFailing", "bool", "Revert to standard download if download fails", False), +    __config__ = [("mode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("revertfailed", "bool", "Revert to standard download if download fails", False),                    ("interval", "int", "Reload interval in hours (0 to disable)", 24)] +      __description__ = """Fastix.ru hook plugin""" -    __author_name__ = "Massimo Rosamilia" -    __author_mail__ = "max@spiritix.eu" +    __license__     = "GPLv3" +    __authors__     = [("Massimo Rosamilia", "max@spiritix.eu")] + -    def getHoster(self): -        page = getURL( -            "http://fastix.ru/api_v2/?apikey=5182964c3f8f9a7f0b00000a_kelmFB4n1IrnCDYuIFn2y&sub=allowed_sources") +    def getHosters(self): +        page = self.getURL("http://fastix.ru/api_v2", +                      get={'apikey': "5182964c3f8f9a7f0b00000a_kelmFB4n1IrnCDYuIFn2y", +                           'sub'   : "allowed_sources"})          host_list = json_loads(page)          host_list = host_list['allow']          return host_list diff --git a/module/plugins/hooks/FreeWayMe.py b/module/plugins/hooks/FreeWayMe.py index 7d4bcc852..f2a4f2d34 100644 --- a/module/plugins/hooks/FreeWayMe.py +++ b/module/plugins/hooks/FreeWayMe.py @@ -1,40 +1,24 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. +from module.plugins.internal.MultiHook import MultiHook -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: Nicolas Giese -""" - -from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster +class FreeWayMe(MultiHook): +    __name__    = "FreeWayMe" +    __type__    = "hook" +    __version__ = "0.13" +    __config__ = [("mode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", ""), +                  ("revertfailed", "bool", "Revert to stanard download if download fails", False), +                  ("interval", "int", "Reload interval in hours (0 to disable)", 24)] -class FreeWayMe(MultiHoster): -    __name__ = "FreeWayMe" -    __version__ = "0.11" -    __type__ = "hook"      __description__ = """FreeWay.me hook plugin""" -    __config__ = [("activated", "bool", "Activated", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", ""), -                  ("unloadFailing", "bool", "Revert to stanard download if download fails", False), -                  ("interval", "int", "Reload interval in hours (0 to disable)", 24)] -    __author_name__ = "Nicolas Giese" -    __author_mail__ = "james@free-way.me" +    __license__     = "GPLv3" +    __authors__     = [("Nicolas Giese", "james@free-way.me")] + -    def getHoster(self): -        hostis = getURL("https://www.free-way.me/ajax/jd.php", get={"id": 3}).replace("\"", "").strip() -        self.logDebug("hosters: %s" % hostis) +    def getHosters(self): +        hostis = self.getURL("https://www.free-way.me/ajax/jd.php", get={'id': 3}).replace("\"", "").strip() +        self.logDebug("Hosters", hostis)          return [x.strip() for x in hostis.split(",") if x.strip()] diff --git a/module/plugins/hooks/HotFolder.py b/module/plugins/hooks/HotFolder.py index 38bcaf440..b0b59e2ba 100644 --- a/module/plugins/hooks/HotFolder.py +++ b/module/plugins/hooks/HotFolder.py @@ -1,83 +1,66 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: RaNaN -    @interface-version: 0.2 -""" - -from os import makedirs -from os import listdir -from os.path import exists -from os.path import join -from os.path import isfile -from shutil import move +from __future__ import with_statement +  import time +from os import listdir, makedirs +from os.path import exists, isfile, join +from shutil import move +  from module.plugins.Hook import Hook +from module.utils import fs_encode, save_join  class HotFolder(Hook): -    __name__ = "HotFolder" -    __version__ = "0.11" -    __description__ = """Observe folder and file for changes and add container and links""" -    __config__ = [("activated", "bool", "Activated", False), -                  ("folder", "str", "Folder to observe", "container"), +    __name__    = "HotFolder" +    __type__    = "hook" +    __version__ = "0.12" + +    __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")] -    __threaded__ = [] -    __author_name__ = "RaNaN" -    __author_mail__ = "RaNaN@pyload.de" + +    __description__ = """Observe folder and file for changes and add container and links""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.de")] +      def setup(self):          self.interval = 10 +      def periodical(self): +        folder = fs_encode(self.getConfig("folder")) -        if not exists(join(self.getConfig("folder"), "finished")): -            makedirs(join(self.getConfig("folder"), "finished")) +        try: +            if not exists(join(folder, "finished")): +                makedirs(join(folder, "finished")) -        if self.getConfig("watch_file"): +            if self.getConfig("watch_file"): +                with open(fs_encode(self.getConfig("file")), "a+") as f: +                    content = f.read().strip() -            if not exists(self.getConfig("file")): -                f = open(self.getConfig("file"), "wb") -                f.close() +                if content: +                    name = "%s_%s.txt" % (self.getConfig("file"), time.strftime("%H-%M-%S_%d%b%Y")) -            f = open(self.getConfig("file"), "rb") -            content = f.read().strip() -            f.close() -            f = open(self.getConfig("file"), "wb") -            f.close() -            if content: -                name = "%s_%s.txt" % (self.getConfig("file"), time.strftime("%H-%M-%S_%d%b%Y")) +                    with open(save_join(folder, "finished", name), "wb") as f: +                        f.write(content) -                f = open(join(self.getConfig("folder"), "finished", name), "wb") -                f.write(content) -                f.close() +                    self.core.api.addPackage(f.name, [f.name], 1) -                self.core.api.addPackage(f.name, [f.name], 1) +            for f in listdir(folder): +                path = join(folder, f) -        for f in listdir(self.getConfig("folder")): -            path = join(self.getConfig("folder"), f) +                if not isfile(path) or f.endswith("~") or f.startswith("#") or f.startswith("."): +                    continue -            if not isfile(path) or f.endswith("~") or f.startswith("#") or f.startswith("."): -                continue +                newpath = join(folder, "finished", f if self.getConfig("keep") else "tmp_" + f) +                move(path, newpath) -            newpath = join(self.getConfig("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) -            self.logInfo(_("Added %s from HotFolder") % f) -            self.core.api.addPackage(f, [newpath], 1) +        except IOError, e: +            self.logError(e) diff --git a/module/plugins/hooks/IRCInterface.py b/module/plugins/hooks/IRCInterface.py index 760c1a4df..efd4e411d 100644 --- a/module/plugins/hooks/IRCInterface.py +++ b/module/plugins/hooks/IRCInterface.py @@ -1,73 +1,62 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: RaNaN -    @author: jeix -    @interface-version: 0.2 -""" +import re +import socket +import ssl +import time +from pycurl import FORM_FILE  from select import select -import socket  from threading import Thread -import time  from time import sleep  from traceback import print_exc -import re -from pycurl import FORM_FILE -from module.plugins.Hook import Hook +from module.Api import PackageDoesNotExists, FileDoesNotExists  from module.network.RequestFactory import getURL +from module.plugins.Hook import Hook  from module.utils import formatSize -from module.Api import PackageDoesNotExists, FileDoesNotExists  class IRCInterface(Thread, Hook): -    __name__ = "IRCInterface" -    __version__ = "0.11" -    __description__ = """Connect to irc and let owner perform different tasks""" -    __config__ = [("activated", "bool", "Activated", False), -                  ("host", "str", "IRC-Server Address", "Enter your server here!"), +    __name__    = "IRCInterface" +    __type__    = "hook" +    __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)] -    __author_name__ = "Jeix" -    __author_mail__ = "Jeix@hasnomail.com" + +    __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)          Hook.__init__(self, core, manager)          self.setDaemon(True) -        #   self.sm = core.server_methods -        self.api = core.api  # todo, only use api -    def coreReady(self): -        self.new_package = {} -        self.abort = False +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass -        self.links_added = 0 + +    def coreReady(self): +        self.abort = False          self.more = [] +        self.new_package = {}          self.start() +      def packageFinished(self, pypack):          try:              if self.getConfig("info_pack"): @@ -75,6 +64,7 @@ class IRCInterface(Thread, Hook):          except:              pass +      def downloadFinished(self, pyfile):          try:              if self.getConfig("info_file"): @@ -83,6 +73,7 @@ class IRCInterface(Thread, Hook):          except:              pass +      def newCaptchaTask(self, task):          if self.getConfig("captcha") and task.isTextual():              task.handler.append(self) @@ -95,19 +86,24 @@ class IRCInterface(Thread, Hook):              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("pyLoad IRC: Connected to %s!" % host) -        self.logInfo("pyLoad IRC: Switching to listening mode!") +        self.logInfo(_("Connected to"), host) +        self.logInfo(_("Switching to listening mode!"))          try:              self.main_loop() @@ -116,6 +112,7 @@ class IRCInterface(Thread, Hook):              print_exc()              self.sock.close() +      def main_loop(self):          readbuffer = ""          while True: @@ -154,34 +151,35 @@ class IRCInterface(Thread, Hook):                  self.handle_events(msg) +      def handle_events(self, msg): -        if not msg["origin"].split("!", 1)[0] in self.getConfig("owner").split(): +        if not msg['origin'].split("!", 1)[0] in self.getConfig("owner").split():              return -        if msg["target"].split("!", 1)[0] != self.getConfig("nick"): +        if msg['target'].split("!", 1)[0] != self.getConfig("nick"):              return -        if msg["action"] != "PRIVMSG": +        if msg['action'] != "PRIVMSG":              return          # HANDLE CTCP ANTI FLOOD/BOT PROTECTION -        if msg["text"] == "\x01VERSION\x01": -            self.logDebug("Sending CTCP VERSION.") +        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.") +        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 +        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() +            temp = msg['text'].split()              trigger = temp[0]              if len(temp) > 1:                  args = temp[1:] @@ -192,9 +190,10 @@ class IRCInterface(Thread, Hook):          try:              res = handler(args)              for line in res: -                self.response(line, msg["origin"]) +                self.response(line, msg['origin'])          except Exception, e: -            self.logError("pyLoad IRC: " + repr(e)) +            self.logError(e) +      def response(self, msg, origin=""):          if origin == "": @@ -203,13 +202,15 @@ class IRCInterface(Thread, Hook):          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.api.statusDownloads() +        downloads = self.core.api.statusDownloads()          if not downloads:              return ["INFO: There are no active downloads currently."] @@ -233,8 +234,9 @@ class IRCInterface(Thread, Hook):                           ))          return lines +      def event_queue(self, args): -        ps = self.api.getQueueData() +        ps = self.core.api.getQueueData()          if not ps:              return ["INFO: There are no packages in queue."] @@ -245,8 +247,9 @@ class IRCInterface(Thread, Hook):          return lines +      def event_collector(self, args): -        ps = self.api.getCollectorData() +        ps = self.core.api.getCollectorData()          if not ps:              return ["INFO: No packages in collector!"] @@ -256,27 +259,29 @@ class IRCInterface(Thread, Hook):          return lines +      def event_info(self, args):          if not args: -            return ['ERROR: Use info like this: info <id>'] +            return ["ERROR: Use info like this: info <id>"]          info = None          try: -            info = self.api.getFileData(int(args[0])) +            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>'] +            return ["ERROR: Use packinfo like this: packinfo <id>"]          lines = []          pack = None          try: -            pack = self.api.getPackageData(int(args[0])) +            pack = self.core.api.getPackageData(int(args[0]))          except PackageDoesNotExists:              return ["ERROR: Package doesn't exists."] @@ -300,6 +305,7 @@ class IRCInterface(Thread, Hook):          return lines +      def event_more(self, args):          if not self.more:              return ["No more information to display."] @@ -310,20 +316,21 @@ class IRCInterface(Thread, Hook):          return lines -    def event_start(self, args): -        self.api.unpauseServer() +    def event_start(self, args): +        self.core.api.unpauseServer()          return ["INFO: Starting downloads."] -    def event_stop(self, args): -        self.api.pauseServer() +    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>!'] +                    "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:]] @@ -332,58 +339,62 @@ class IRCInterface(Thread, Hook):          count_failed = 0          try:              id = int(pack) -            pack = self.api.getPackageData(id) +            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)] +            return ["INFO: Added %d links to Package %s [#%d]" % (len(links), pack['name'], id)]          except:              # create new package -            id = self.api.addPackage(pack, links, 1) +            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.api.deletePackages(map(int, args[1:])) +            ret = self.core.api.deletePackages(map(int, args[1:]))              return ["INFO: Deleted %d packages!" % len(args[1:])]          elif args[0] == "-l": -            ret = self.api.delLinks(map(int, args[1:])) +            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.api.getPackageInfo(id) +            info = self.core.api.getPackageInfo(id)          except PackageDoesNotExists:              return ["ERROR: Package #%d does not exist." % id] -        self.api.pushToQueue(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.api.getPackageData(id): +        if not self.core.api.getPackageData(id):              return ["ERROR: Package #%d does not exist." % id] -        self.api.pullFromQueue(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: @@ -396,6 +407,7 @@ class IRCInterface(Thread, Hook):          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)", @@ -415,8 +427,10 @@ class IRCInterface(Thread, Hook):  class IRCError(Exception): +      def __init__(self, value):          self.value = value +      def __str__(self):          return repr(self.value) diff --git a/module/plugins/hooks/ImageTyperz.py b/module/plugins/hooks/ImageTyperz.py index e2e9d93d5..f89d64c37 100644 --- a/module/plugins/hooks/ImageTyperz.py +++ b/module/plugins/hooks/ImageTyperz.py @@ -1,86 +1,90 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay, RaNaN, zoidberg -"""  from __future__ import with_statement -from thread import start_new_thread -from pycurl import FORM_FILE, LOW_SPEED_TIME +  import re +  from base64 import b64encode +from pycurl import FORM_FILE, LOW_SPEED_TIME +from thread import start_new_thread  from module.network.RequestFactory import getURL, getRequest  from module.plugins.Hook import Hook  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" -    __version__ = "0.04" -    __description__ = """Send captchas to ImageTyperz.com""" -    __config__ = [("activated", "bool", "Activated", False), -                  ("username", "str", "Username", ""), +    __name__    = "ImageTyperz" +    __type__    = "hook" +    __version__ = "0.05" + +    __config__ = [("username", "str", "Username", ""),                    ("passkey", "password", "Password", ""),                    ("force", "bool", "Force IT even if client is connected", False)] -    __author_name__ = ("RaNaN", "zoidberg") -    __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz") + +    __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" + +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass + +      def setup(self): -        self.info = {} +        self.info = {}  #@TODO: Remove in 0.4.10 +      def getCredits(self): -        response = getURL(self.GETCREDITS_URL, post={"action": "REQUESTBALANCE", "username": self.getConfig("username"), -                                                     "password": self.getConfig("passkey")}) +        res = getURL(self.GETCREDITS_URL, +                     post={'action': "REQUESTBALANCE", +                           'username': self.getConfig("username"), +                           'password': self.getConfig("passkey")}) -        if response.startswith('ERROR'): -            raise ImageTyperzException(response) +        if res.startswith('ERROR'): +            raise ImageTyperzException(res)          try: -            balance = float(response) +            balance = float(res)          except: -            raise ImageTyperzException("invalid response") +            raise ImageTyperzException("Invalid response") -        self.logInfo("Account balance: $%s left" % 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: -            #workaround multipart-post bug in HTTPRequest.py  -            if re.match("^[A-Za-z0-9]*$", self.getConfig("passkey")): +            #workaround multipart-post bug in HTTPRequest.py +            if re.match("^\w*$", self.getConfig("passkey")):                  multipart = True                  data = (FORM_FILE, captcha)              else: @@ -89,24 +93,26 @@ class ImageTyperz(Hook):                      data = f.read()                  data = b64encode(data) -            response = req.load(self.SUBMIT_URL, post={"action": "UPLOADCAPTCHA", -                                                       "username": self.getConfig("username"), -                                                       "password": self.getConfig("passkey"), "file": data}, -                                                       multipart=multipart) +            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 response.startswith("ERROR"): -            raise ImageTyperzException(response) +        if res.startswith("ERROR"): +            raise ImageTyperzException(res)          else: -            data = response.split('|') +            data = res.split('|')              if len(data) == 2:                  ticket, result = data              else: -                raise ImageTyperzException("Unknown response %s" % response) +                raise ImageTyperzException("Unknown response: %s" % res)          return ticket, result +      def newCaptchaTask(self, task):          if "service" in task.data:              return False @@ -127,18 +133,22 @@ class ImageTyperz(Hook):              start_new_thread(self.processCaptcha, (task,))          else: -            self.logInfo("Your %s account has not enough credits" % self.__name__) +            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: -            response = getURL(self.RESPOND_URL, post={"action": "SETBADIMAGE", "username": self.getConfig("username"), -                                                      "password": self.getConfig("passkey"), -                                                      "imageid": task.data["ticket"]}) - -            if response == "SUCCESS": -                self.logInfo("Bad captcha solution received, requested refund") +            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", response) +                self.logError(_("Bad captcha solution received, refund request failed"), res) +      def processCaptcha(self, task):          c = task.captchaFile @@ -148,5 +158,5 @@ class ImageTyperz(Hook):              task.error = e.getCode()              return -        task.data["ticket"] = ticket +        task.data['ticket'] = ticket          task.setResult(result) diff --git a/module/plugins/hooks/LinkdecrypterCom.py b/module/plugins/hooks/LinkdecrypterCom.py index dd9cd79f2..8592efd3d 100644 --- a/module/plugins/hooks/LinkdecrypterCom.py +++ b/module/plugins/hooks/LinkdecrypterCom.py @@ -1,68 +1,27 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re -from module.plugins.Hook import Hook -from module.network.RequestFactory import getURL -from module.utils import remove_chars - - -class LinkdecrypterCom(Hook): -    __name__ = "LinkdecrypterCom" -    __version__ = "0.19" -    __description__ = """Linkdecrypter.com hook plugin""" -    __config__ = [("activated", "bool", "Activated", False)] -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" - -    def coreReady(self): -        try: -            self.loadPatterns() -        except Exception, e: -            self.logError(e) +from module.plugins.internal.MultiHook import MultiHook -    def loadPatterns(self): -        page = getURL("http://linkdecrypter.com/") -        m = re.search(r'<b>Supported\(\d+\)</b>: <i>([^+<]*)', page) -        if not m: -            self.logError(_("Crypter list not found")) -            return -        builtin = [name.lower() for name in self.core.pluginManager.crypterPlugins.keys()] -        builtin.extend(["downloadserienjunkiesorg"]) +class LinkdecrypterCom(MultiHook): +    __name__    = "LinkdecrypterCom" +    __type__    = "hook" +    __version__ = "1.00" -        crypter_pattern = re.compile("(\w[\w.-]+)") -        online = [] -        for crypter in m.group(1).split(', '): -            m = re.match(crypter_pattern, crypter) -            if m and remove_chars(m.group(1), "-.") not in builtin: -                online.append(m.group(1).replace(".", "\\.")) +    __config__ = [("mode"        , "all;listed;unlisted", "Use for crypters (if supported)"              , "all"), +                  ("pluginlist"  , "str"                , "Crypter list (comma separated)"               , ""   ), +                  ("interval"    , "int"                , "Reload interval in hours (0 to disable)"      , 12   )] -        if not online: -            self.logError(_("Crypter list is empty")) -            return - -        regexp = r"https?://([^.]+\.)*?(%s)/.*" % "|".join(online) +    __description__ = """Linkdecrypter.com hook plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] -        dict = self.core.pluginManager.crypterPlugins[self.__name__] -        dict["pattern"] = regexp -        dict["re"] = re.compile(regexp) -        self.logDebug("REGEXP: " + regexp) +    def getCrypters(self): +        try: +            html = self.getURL("http://linkdecrypter.com/") +            return re.search(r'>Supported\(\d+\)</b>: <i>(.+?) \+ RSDF', html).group(1).split(', ') +        except Exception: +            return list() diff --git a/module/plugins/hooks/LinksnappyCom.py b/module/plugins/hooks/LinksnappyCom.py index 110731228..a1c4b90f7 100644 --- a/module/plugins/hooks/LinksnappyCom.py +++ b/module/plugins/hooks/LinksnappyCom.py @@ -1,26 +1,26 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.MultiHoster import MultiHoster -from module.network.RequestFactory import getURL  from module.common.json_layer import json_loads +from module.plugins.internal.MultiHook import MultiHook -class LinksnappyCom(MultiHoster): -    __name__ = "LinksnappyCom" -    __version__ = "0.01" -    __type__ = "hook" -    __config__ = [("activated", "bool", "Activated", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", ""), -                  ("unloadFailing", "bool", "Revert to standard download if download fails", False), +class LinksnappyCom(MultiHook): +    __name__    = "LinksnappyCom" +    __type__    = "hook" +    __version__ = "0.03" + +    __config__ = [("mode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", ""), +                  ("revertfailed", "bool", "Revert to standard download if download fails", False),                    ("interval", "int", "Reload interval in hours (0 to disable)", 24)]      __description__ = """Linksnappy.com hook plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + -    def getHoster(self): -        json_data = getURL('http://gen.linksnappy.com/lseAPI.php?act=FILEHOSTS') +    def getHosters(self): +        json_data = self.getURL("http://gen.linksnappy.com/lseAPI.php", get={'act': "FILEHOSTS"})          json_data = json_loads(json_data)          return json_data['return'].keys() diff --git a/module/plugins/hooks/MegaDebridEu.py b/module/plugins/hooks/MegaDebridEu.py index 0c3bb99f6..5fb7e1ea6 100644 --- a/module/plugins/hooks/MegaDebridEu.py +++ b/module/plugins/hooks/MegaDebridEu.py @@ -1,42 +1,29 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -from module.plugins.internal.MultiHoster import MultiHoster -from module.network.RequestFactory import getURL +  from module.common.json_layer import json_loads +from module.plugins.internal.MultiHook import MultiHook + +class MegaDebridEu(MultiHook): +    __name__    = "MegaDebridEu" +    __type__    = "hook" +    __version__ = "0.04" + +    __config__ = [("revertfailed", "bool", "Revert to standard download if download fails", False)] -class MegaDebridEu(MultiHoster): -    __name__ = "MegaDebridEu" -    __version__ = "0.02" -    __type__ = "hook" -    __config__ = [("activated", "bool", "Activated", False), -                  ("unloadFailing", "bool", "Revert to standard download if download fails", False)]      __description__ = """mega-debrid.eu hook plugin""" -    __author_name__ = "D.Ducatel" -    __author_mail__ = "dducatel@je-geek.fr" +    __license__     = "GPLv3" +    __authors__     = [("D.Ducatel", "dducatel@je-geek.fr")] + -    def getHoster(self): -        reponse = getURL('http://www.mega-debrid.eu/api.php?action=getHosters') +    def getHosters(self): +        reponse   = self.getURL("http://www.mega-debrid.eu/api.php", get={'action': "getHosters"})          json_data = json_loads(reponse) -        if json_data["response_code"] == "ok": +        if json_data['response_code'] == "ok":              host_list = [element[0] for element in json_data['hosters']]          else: -            self.logError("Unable to retrieve hoster list") +            self.logError(_("Unable to retrieve hoster list"))              host_list = list()          return host_list diff --git a/module/plugins/hooks/MergeFiles.py b/module/plugins/hooks/MergeFiles.py index 99b0aafc6..4de45f958 100644 --- a/module/plugins/hooks/MergeFiles.py +++ b/module/plugins/hooks/MergeFiles.py @@ -1,56 +1,52 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: and9000 -""" +from __future__ import with_statement  import os  import re -import traceback -from os.path import join -from module.utils import save_join, fs_encode -from module.plugins.Hook import Hook +from traceback import print_exc -BUFFER_SIZE = 4096 +from module.plugins.Hook import Hook, threaded +from module.utils import save_join, fs_encode  class MergeFiles(Hook): -    __name__ = "MergeFiles" -    __version__ = "0.12" +    __name__    = "MergeFiles" +    __type__    = "hook" +    __version__ = "0.13" + +    __config__ = [("activated", "bool", "Activated", True)] +      __description__ = """Merges parts splitted with hjsplit""" -    __config__ = [("activated", "bool", "Activated", False)] -    __threaded__ = ["packageFinished"] -    __author_name__ = "and9000" -    __author_mail__ = "me@has-no-mail.com" +    __license__     = "GPLv3" +    __authors__     = [("and9000", "me@has-no-mail.com")] + + +    BUFFER_SIZE = 4096 + + +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass +      def setup(self):          # nothing to do          pass + +    @threaded      def packageFinished(self, pack):          files = {}          fid_dict = {}          for fid, data in pack.getChildren().iteritems(): -            if re.search("\.[0-9]{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 +            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'] @@ -58,33 +54,36 @@ class MergeFiles(Hook):              download_folder = save_join(download_folder, pack.folder)          for name, file_list in files.iteritems(): -            self.logInfo("Starting merging of %s" % name) -            final_file = open(join(download_folder, fs_encode(name)), "wb") - -            for splitted_file in file_list: -                self.logDebug("Merging part %s" % splitted_file) -                pyfile = self.core.files.getFile(fid_dict[splitted_file]) -                pyfile.setStatus("processing") -                try: -                    s_file = open(os.path.join(download_folder, splitted_file), "rb") -                    size_written = 0 -                    s_file_size = int(os.path.getsize(os.path.join(download_folder, splitted_file))) -                    while True: -                        f_buffer = s_file.read(BUFFER_SIZE) -                        if f_buffer: -                            final_file.write(f_buffer) -                            size_written += BUFFER_SIZE -                            pyfile.setProgress((size_written * 100) / s_file_size) -                        else: -                            break -                    s_file.close() -                    self.logDebug("Finished merging part %s" % splitted_file) -                except Exception, e: -                    print traceback.print_exc() -                finally: -                    pyfile.setProgress(100) -                    pyfile.setStatus("finished") -                    pyfile.release() - -            final_file.close() -            self.logInfo("Finished merging of %s" % name) +            self.logInfo(_("Starting merging of"), name) + +            with open(save_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(os.path.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: +                        print_exc() + +                    finally: +                        pyfile.setProgress(100) +                        pyfile.setStatus("finished") +                        pyfile.release() + +            self.logInfo(_("Finished merging of"), name) diff --git a/module/plugins/hooks/MultiDebridCom.py b/module/plugins/hooks/MultiDebridCom.py deleted file mode 100644 index f2dfb18ca..000000000 --- a/module/plugins/hooks/MultiDebridCom.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -from module.plugins.internal.MultiHoster import MultiHoster -from module.network.RequestFactory import getURL -from module.common.json_layer import json_loads - - -class MultiDebridCom(MultiHoster): -    __name__ = "MultiDebridCom" -    __version__ = "0.01" -    __type__ = "hook" -    __config__ = [("activated", "bool", "Activated", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", ""), -                  ("unloadFailing", "bool", "Revert to standard download if download fails", False), -                  ("interval", "int", "Reload interval in hours (0 to disable)", 24)] - -    __description__ = """Multi-debrid.com hook plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" - -    def getHoster(self): -        json_data = getURL('http://multi-debrid.com/api.php?hosts', decode=True) -        self.logDebug('JSON data: ' + json_data) -        json_data = json_loads(json_data) - -        return json_data['hosts'] diff --git a/module/plugins/hooks/MultiHome.py b/module/plugins/hooks/MultiHome.py index b1635a588..105a42abd 100644 --- a/module/plugins/hooks/MultiHome.py +++ b/module/plugins/hooks/MultiHome.py @@ -1,67 +1,63 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: mkaay -""" -  from time import time  from module.plugins.Hook import Hook  class MultiHome(Hook): -    __name__ = "MultiHome" -    __version__ = "0.11" +    __name__    = "MultiHome" +    __type__    = "hook" +    __version__ = "0.12" + +    __config__ = [("interfaces", "str", "Interfaces", "None")] +      __description__ = """Ip address changer""" -    __config__ = [("activated", "bool", "Activated", False), -                  ("interfaces", "str", "Interfaces", "None")] -    __author_name__ = "mkaay" -    __author_mail__ = "mkaay@mkaay.de" +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de")] + + +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass +      def setup(self):          self.register = {}          self.interfaces = []          self.parseInterfaces(self.getConfig("interfaces").split(";"))          if not self.interfaces: -            self.parseInterfaces([self.config["download"]["interface"]]) +            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 coreReady(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("Multihome: using address: " + 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: @@ -71,17 +67,21 @@ class MultiHome(Hook):  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() +      def __repr__(self):          return "<Interface - %s>" % self.adress diff --git a/module/plugins/hooks/MultihostersCom.py b/module/plugins/hooks/MultihostersCom.py new file mode 100644 index 000000000..5ada3aa56 --- /dev/null +++ b/module/plugins/hooks/MultihostersCom.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +from module.network.RequestFactory import getURL +from module.plugins.internal.MultiHoster import MultiHoster + +class MultihostersCom(MultiHoster): +    __name__ = "MultihostersCom" +    __version__ = "0.01" +    __type__ = "hook" +    __config__ = [("activated", "bool", "Activated", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", ""), +                  ("unloadFailing", "bool", "Revert to standard download if download fails", False), +                  ("interval", "int", "Reload interval in hours (0 to disable)", 24)] + +    __description__ = """Multihosters.com hook plugin""" +    __author_name__ = "tjeh" +    __author_mail__ = "tjeh@gmx.net" + +    def getHoster(self): +        page = getURL("http://www.multihosters.com/jDownloader.ashx?cmd=gethosters") +        return [x.strip() for x in page.split(",")]
\ No newline at end of file diff --git a/module/plugins/hooks/MultishareCz.py b/module/plugins/hooks/MultishareCz.py index 0291738f5..2dadf5dc1 100644 --- a/module/plugins/hooks/MultishareCz.py +++ b/module/plugins/hooks/MultishareCz.py @@ -2,23 +2,25 @@  import re -from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster +from module.plugins.internal.MultiHook import MultiHook -class MultishareCz(MultiHoster): -    __name__ = "MultishareCz" -    __version__ = "0.04" -    __type__ = "hook" -    __config__ = [("activated", "bool", "Activated", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", "uloz.to")] +class MultishareCz(MultiHook): +    __name__    = "MultishareCz" +    __type__    = "hook" +    __version__ = "0.06" + +    __config__ = [("mode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", "uloz.to")] +      __description__ = """MultiShare.cz hook plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] +      HOSTER_PATTERN = r'<img class="logo-shareserveru"[^>]*?alt="([^"]+)"></td>\s*<td class="stav">[^>]*?alt="OK"' -    def getHoster(self): -        page = getURL("http://www.multishare.cz/monitoring/") + +    def getHosters(self): +        page = self.getURL("http://www.multishare.cz/monitoring/")          return re.findall(self.HOSTER_PATTERN, page) diff --git a/module/plugins/hooks/MyfastfileCom.py b/module/plugins/hooks/MyfastfileCom.py new file mode 100644 index 000000000..2fda0d3bf --- /dev/null +++ b/module/plugins/hooks/MyfastfileCom.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- + +from module.common.json_layer import json_loads +from module.plugins.internal.MultiHook import MultiHook + + +class MyfastfileCom(MultiHook): +    __name__    = "MyfastfileCom" +    __type__    = "hook" +    __version__ = "0.04" + +    __config__ = [("mode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", ""), +                  ("revertfailed", "bool", "Revert to standard download if download fails", False), +                  ("interval", "int", "Reload interval in hours (0 to disable)", 24)] + +    __description__ = """Myfastfile.com hook plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    def getHosters(self): +        json_data = self.getURL("http://myfastfile.com/api.php", get={'hosts': ""}, decode=True) +        self.logDebug("JSON data", json_data) +        json_data = json_loads(json_data) + +        return json_data['hosts'] diff --git a/module/plugins/hooks/NoPremiumPl.py b/module/plugins/hooks/NoPremiumPl.py new file mode 100644 index 000000000..84a019b8d --- /dev/null +++ b/module/plugins/hooks/NoPremiumPl.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.MultiHoster import MultiHoster +from module.network.RequestFactory import getURL +from module.common.json_layer import json_loads as loads + + +class NoPremiumPl(MultiHoster): +    __name__ = "NoPremiumPl" +    __version__ = "0.01" +    __type__ = "hook" + +    __config__ = [("activated", "bool", "Activated", "False"), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", ""), +                  ("unloadFailing", "bool", "Try standard download if download fails", "False"), +                  ("interval", "int", "Reload supported hosts interval in hours (0 to disable)", "24")] + +    __description__ = "NoPremium.pl hook" +    __license__ = "GPLv3" +    __authors__ = [("goddie", "dev@nopremium.pl")] + +    def getHoster(self): +        hostings = loads(getURL("https://www.nopremium.pl/clipboard.php?json=3").strip()) + +        return [domain for row in hostings for domain in row["domains"] if row["sdownload"] == "0"] + +    def getHosterCached(self): +        return self.getHoster() + + diff --git a/module/plugins/hooks/OverLoadMe.py b/module/plugins/hooks/OverLoadMe.py index e15d0b05f..eb3da319a 100644 --- a/module/plugins/hooks/OverLoadMe.py +++ b/module/plugins/hooks/OverLoadMe.py @@ -1,28 +1,28 @@  # -*- coding: utf-8 -*- -from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster - - -class OverLoadMe(MultiHoster): -    __name__ = "OverLoadMe" -    __version__ = "0.01" -    __type__ = "hook" -    __config__ = [("activated", "bool", "Activated", False), -                  ("https", "bool", "Enable HTTPS", True), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", ""), -                  ("unloadFailing", "bool", "Revert to standard download if download fails", False), +from module.plugins.internal.MultiHook import MultiHook + + +class OverLoadMe(MultiHook): +    __name__    = "OverLoadMe" +    __type__    = "hook" +    __version__ = "0.03" + +    __config__ = [("https", "bool", "Enable HTTPS", True), +                  ("mode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", ""), +                  ("revertfailed", "bool", "Revert to standard download if download fails", False),                    ("interval", "int", "Reload interval in hours (0 to disable)", 12)] +      __description__ = """Over-Load.me hook plugin""" -    __author_name__ = "marley" -    __author_mail__ = "marley@over-load.me" +    __license__     = "GPLv3" +    __authors__     = [("marley", "marley@over-load.me")] + -    def getHoster(self): +    def getHosters(self):          https = "https" if self.getConfig("https") else "http" -        page = getURL(https + "://api.over-load.me/hoster.php", -                      get={"auth": "0001-cb1f24dadb3aa487bda5afd3b76298935329be7700cd7-5329be77-00cf-1ca0135f"} -                      ).replace("\"", "").strip() -        self.logDebug("Hosterlist: %s" % page) +        page = self.getURL(https + "://api.over-load.me/hoster.php", +                      get={'auth': "0001-cb1f24dadb3aa487bda5afd3b76298935329be7700cd7-5329be77-00cf-1ca0135f"}).replace("\"", "").strip() +        self.logDebug("Hosterlist", page)          return [x.strip() for x in page.split(",") if x.strip()] diff --git a/module/plugins/hooks/Premium4Me.py b/module/plugins/hooks/Premium4Me.py deleted file mode 100644 index 57b188bb9..000000000 --- a/module/plugins/hooks/Premium4Me.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- - -from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster - - -class Premium4Me(MultiHoster): -    __name__ = "Premium4Me" -    __version__ = "0.03" -    __type__ = "hook" - -    __config__ = [("activated", "bool", "Activated", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for downloads from supported hosters:", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", "")] -    __description__ = """Premium.to hook plugin""" -    __author_name__ = ("RaNaN", "zoidberg", "stickell") -    __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz", "l.stickell@yahoo.it") - -    def getHoster(self): -        page = getURL("http://premium.to/api/hosters.php?authcode=%s" % self.account.authcode) -        return [x.strip() for x in page.replace("\"", "").split(";")] - -    def coreReady(self): -        self.account = self.core.accountManager.getAccountPlugin("Premium4Me") - -        user = self.account.selectAccount()[0] - -        if not user: -            self.logError(_("Please add your premium.to account first and restart pyLoad")) -            return - -        return MultiHoster.coreReady(self) diff --git a/module/plugins/hooks/PremiumTo.py b/module/plugins/hooks/PremiumTo.py new file mode 100644 index 000000000..348bb6789 --- /dev/null +++ b/module/plugins/hooks/PremiumTo.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.MultiHook import MultiHook + + +class PremiumTo(MultiHook): +    __name__    = "PremiumTo" +    __type__    = "hook" +    __version__ = "0.06" + +    __config__ = [("mode", "all;listed;unlisted", "Use for downloads from supported hosters:", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", "")] + +    __description__ = """Premium.to hook plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org"), +                       ("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    def getHosters(self): +        page = self.getURL("http://premium.to/api/hosters.php", +                      get={'username': self.account.username, 'password': self.account.password}) +        return [x.strip() for x in page.replace("\"", "").split(";")] + + +    def coreReady(self): +        self.account = self.core.accountManager.getAccountPlugin("PremiumTo") + +        user = self.account.selectAccount()[0] + +        if not user: +            self.logError(_("Please add your premium.to account first and restart pyLoad")) +            return + +        return MultiHook.coreReady(self) diff --git a/module/plugins/hooks/PremiumizeMe.py b/module/plugins/hooks/PremiumizeMe.py index 9f1a70a70..e23f13895 100644 --- a/module/plugins/hooks/PremiumizeMe.py +++ b/module/plugins/hooks/PremiumizeMe.py @@ -1,27 +1,25 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.MultiHoster import MultiHoster -  from module.common.json_layer import json_loads -from module.network.RequestFactory import getURL +from module.plugins.internal.MultiHook import MultiHook -class PremiumizeMe(MultiHoster): -    __name__ = "PremiumizeMe" -    __version__ = "0.12" -    __type__ = "hook" -    __description__ = """Premiumize.me hook plugin""" +class PremiumizeMe(MultiHook): +    __name__    = "PremiumizeMe" +    __type__    = "hook" +    __version__ = "0.15" -    __config__ = [("activated", "bool", "Activated", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", ""), -                  ("unloadFailing", "bool", "Revert to stanard download if download fails", False), +    __config__ = [("mode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", ""), +                  ("revertfailed", "bool", "Revert to stanard download if download fails", False),                    ("interval", "int", "Reload interval in hours (0 to disable)", 24)] -    __author_name__ = "Florian Franzen" -    __author_mail__ = "FlorianFranzen@gmail.com" +    __description__ = """Premiumize.me hook plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Florian Franzen", "FlorianFranzen@gmail.com")] -    def getHoster(self): + +    def getHosters(self):          # If no accounts are available there will be no hosters available          if not self.account or not self.account.canUse():              return [] @@ -31,24 +29,25 @@ class PremiumizeMe(MultiHoster):          # Get supported hosters list from premiumize.me using the          # json API v1 (see https://secure.premiumize.me/?show=api) -        answer = getURL("https://api.premiumize.me/pm-api/v1.php?method=hosterlist¶ms[login]=%s¶ms[pass]=%s" % ( -                        user, data['password'])) +        answer = self.getURL("https://api.premiumize.me/pm-api/v1.php", +                        get={'method': "hosterlist", 'params[login]': user, 'params[pass]': data['password']})          data = json_loads(answer)          # If account is not valid thera are no hosters available          if data['status'] != 200:              return [] -        # Extract hosters from json file  +        # Extract hosters from json file          return data['result']['hosterlist'] +      def coreReady(self):          # Get account plugin and check if there is a valid account available          self.account = self.core.accountManager.getAccountPlugin("PremiumizeMe")          if not self.account.canUse():              self.account = None -            self.logError(_("Please add a valid premiumize.me account first and restart pyLoad.")) +            self.logError(_("Please add a valid premiumize.me account first and restart pyLoad"))              return -        # Run the overwriten core ready which actually enables the multihoster hook  -        return MultiHoster.coreReady(self) +        # Run the overwriten core ready which actually enables the multihoster hook +        return MultiHook.coreReady(self) diff --git a/module/plugins/hooks/RPNetBiz.py b/module/plugins/hooks/RPNetBiz.py index 54f814231..5af355fcc 100644 --- a/module/plugins/hooks/RPNetBiz.py +++ b/module/plugins/hooks/RPNetBiz.py @@ -1,24 +1,25 @@  # -*- coding: utf-8 -*- -from module.plugins.internal.MultiHoster import MultiHoster  from module.common.json_layer import json_loads -from module.network.RequestFactory import getURL +from module.plugins.internal.MultiHook import MultiHook -class RPNetBiz(MultiHoster): -    __name__ = "RPNetBiz" -    __version__ = "0.1" -    __type__ = "hook" -    __description__ = """RPNet.biz hook plugin""" -    __config__ = [("activated", "bool", "Activated", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", ""), -                  ("unloadFailing", "bool", "Revert to stanard download if download fails", False), +class RPNetBiz(MultiHook): +    __name__    = "RPNetBiz" +    __type__    = "hook" +    __version__ = "0.12" + +    __config__ = [("mode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", ""), +                  ("revertfailed", "bool", "Revert to stanard download if download fails", False),                    ("interval", "int", "Reload interval in hours (0 to disable)", 24)] -    __author_name__ = "Dman" -    __author_mail__ = "dmanugm@gmail.com" -    def getHoster(self): +    __description__ = """RPNet.biz hook plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Dman", "dmanugm@gmail.com")] + + +    def getHosters(self):          # No hosts supported if no account          if not self.account or not self.account.canUse():              return [] @@ -26,17 +27,18 @@ class RPNetBiz(MultiHoster):          # Get account data          (user, data) = self.account.selectAccount() -        response = getURL("https://premium.rpnet.biz/client_api.php", -                          get={"username": user, "password": data['password'], "action": "showHosterList"}) -        hoster_list = json_loads(response) +        res = self.getURL("https://premium.rpnet.biz/client_api.php", +                     get={'username': user, 'password': data['password'], 'action': "showHosterList"}) +        hoster_list = json_loads(res)          # If account is not valid thera are no hosters available          if 'error' in hoster_list:              return [] -        # Extract hosters from json file  +        # Extract hosters from json file          return hoster_list['hosters'] +      def coreReady(self):          # Get account plugin and check if there is a valid account available          self.account = self.core.accountManager.getAccountPlugin("RPNetBiz") @@ -45,5 +47,5 @@ class RPNetBiz(MultiHoster):              self.logError(_("Please enter your %s account or deactivate this plugin") % "rpnet")              return -        # Run the overwriten core ready which actually enables the multihoster hook  -        return MultiHoster.coreReady(self) +        # Run the overwriten core ready which actually enables the multihoster hook +        return MultiHook.coreReady(self) diff --git a/module/plugins/hooks/RapideoPl.py b/module/plugins/hooks/RapideoPl.py new file mode 100644 index 000000000..a5d7a34a5 --- /dev/null +++ b/module/plugins/hooks/RapideoPl.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.MultiHoster import MultiHoster +from module.network.RequestFactory import getURL +from module.common.json_layer import json_loads as loads + + +class RapideoPl(MultiHoster): +    __name__ = "RapideoPl" +    __version__ = "0.01" +    __type__ = "hook" + +    __config__ = [("activated", "bool", "Activated", "False"), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", ""), +                  ("unloadFailing", "bool", "Try standard download if download fails", "False"), +                  ("interval", "int", "Reload supported hosts interval in hours (0 to disable)", "24")] + +    __description__ = "Rapideo.pl hook" +    __license__ = "GPLv3" +    __authors__ = [("goddie", "dev@rapideo.pl")] + +    def getHoster(self): +        hostings = loads(getURL("https://www.rapideo.pl/clipboard.php?json=3").strip()) + +        return [domain for row in hostings for domain in row["domains"] if row["sdownload"] == "0"] + +    def getHosterCached(self): +        return self.getHoster() + + diff --git a/module/plugins/hooks/RealdebridCom.py b/module/plugins/hooks/RealdebridCom.py index 566f9005f..05a6df1dd 100644 --- a/module/plugins/hooks/RealdebridCom.py +++ b/module/plugins/hooks/RealdebridCom.py @@ -1,26 +1,26 @@  # -*- coding: utf-8 -*- -from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster +from module.plugins.internal.MultiHook import MultiHook -class RealdebridCom(MultiHoster): -    __name__ = "RealdebridCom" -    __version__ = "0.43" -    __type__ = "hook" +class RealdebridCom(MultiHook): +    __name__    = "RealdebridCom" +    __type__    = "hook" +    __version__ = "0.45" -    __config__ = [("activated", "bool", "Activated", False), -                  ("https", "bool", "Enable HTTPS", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", ""), -                  ("unloadFailing", "bool", "Revert to stanard download if download fails", False), +    __config__ = [("https", "bool", "Enable HTTPS", False), +                  ("mode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", ""), +                  ("revertfailed", "bool", "Revert to stanard download if download fails", False),                    ("interval", "int", "Reload interval in hours (0 to disable)", 24)] +      __description__ = """Real-Debrid.com hook plugin""" -    __author_name__ = "Devirex Hazzard" -    __author_mail__ = "naibaf_11@yahoo.de" +    __license__     = "GPLv3" +    __authors__     = [("Devirex Hazzard", "naibaf_11@yahoo.de")] + -    def getHoster(self): +    def getHosters(self):          https = "https" if self.getConfig("https") else "http" -        page = getURL(https + "://real-debrid.com/api/hosters.php").replace("\"", "").strip() +        page = self.getURL(https + "://real-debrid.com/api/hosters.php").replace("\"", "").strip()          return [x.strip() for x in page.split(",") if x.strip()] diff --git a/module/plugins/hooks/RehostTo.py b/module/plugins/hooks/RehostTo.py index 6c3a77ca3..207449e6a 100644 --- a/module/plugins/hooks/RehostTo.py +++ b/module/plugins/hooks/RehostTo.py @@ -1,39 +1,40 @@  # -*- coding: utf-8 -*- -from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster +from module.plugins.internal.MultiHook import MultiHook -class RehostTo(MultiHoster): -    __name__ = "RehostTo" -    __version__ = "0.43" -    __type__ = "hook" +class RehostTo(MultiHook): +    __name__    = "RehostTo" +    __type__    = "hook" +    __version__ = "0.45" -    __config__ = [("activated", "bool", "Activated", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", ""), -                  ("unloadFailing", "bool", "Revert to stanard download if download fails", False), +    __config__ = [("mode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", ""), +                  ("revertfailed", "bool", "Revert to stanard download if download fails", False),                    ("interval", "int", "Reload interval in hours (0 to disable)", 24)]      __description__ = """Rehost.to hook plugin""" -    __author_name__ = "RaNaN" -    __author_mail__ = "RaNaN@pyload.org" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org")] -    def getHoster(self): -        page = getURL("http://rehost.to/api.php?cmd=get_supported_och_dl&long_ses=%s" % self.long_ses) + +    def getHosters(self): +        page = self.getURL("http://rehost.to/api.php", +                      get={'cmd': "get_supported_och_dl", 'long_ses': self.long_ses})          return [x.strip() for x in page.replace("\"", "").split(",")] +      def coreReady(self):          self.account = self.core.accountManager.getAccountPlugin("RehostTo")          user = self.account.selectAccount()[0]          if not user: -            self.logError("Rehost.to: " + _("Please add your rehost.to account first and restart pyLoad")) +            self.logError(_("Please add your rehost.to account first and restart pyLoad"))              return          data = self.account.getAccountInfo(user) -        self.ses = data["ses"] -        self.long_ses = data["long_ses"] +        self.ses = data['ses'] +        self.long_ses = data['long_ses'] -        return MultiHoster.coreReady(self) +        return MultiHook.coreReady(self) diff --git a/module/plugins/hooks/ReloadCc.py b/module/plugins/hooks/ReloadCc.py deleted file mode 100644 index 9960a2699..000000000 --- a/module/plugins/hooks/ReloadCc.py +++ /dev/null @@ -1,67 +0,0 @@ -# -*- coding: utf-8 -*- - -from module.plugins.internal.MultiHoster import MultiHoster - -from module.common.json_layer import json_loads -from module.network.RequestFactory import getURL - - -class ReloadCc(MultiHoster): -    __name__ = "ReloadCc" -    __version__ = "0.3" -    __type__ = "hook" -    __description__ = """Reload.cc hook plugin""" - -    __config__ = [("activated", "bool", "Activated", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", "")] - -    __author_name__ = "Reload Team" -    __author_mail__ = "hello@reload.cc" - -    interval = 0  # Disable periodic calls - -    def getHoster(self): -        # If no accounts are available there will be no hosters available -        if not self.account or not self.account.canUse(): -            print "ReloadCc: No accounts available" -            return [] - -        # Get account data -        (user, data) = self.account.selectAccount() - -        # Get supported hosters list from reload.cc using the json API v1 -        query_params = dict( -            via='pyload', -            v=1, -            get_supported='true', -            get_traffic='true', -            user=user -        ) - -        try: -            query_params.update(dict(hash=self.account.infos[user]['pwdhash'])) -        except Exception: -            query_params.update(dict(pwd=data['password'])) - -        answer = getURL("http://api.reload.cc/login", get=query_params) -        data = json_loads(answer) - -        # If account is not valid thera are no hosters available -        if data['status'] != "ok": -            print "ReloadCc: Status is not ok: %s" % data['status'] -            return [] - -        # Extract hosters from json file -        return data['msg']['supportedHosters'] - -    def coreReady(self): -        # Get account plugin and check if there is a valid account available -        self.account = self.core.accountManager.getAccountPlugin("ReloadCc") -        if not self.account.canUse(): -            self.account = None -            self.logError("Please add a valid reload.cc account first and restart pyLoad.") -            return - -        # Run the overwriten core ready which actually enables the multihoster hook -        return MultiHoster.coreReady(self) diff --git a/module/plugins/hooks/RestartFailed.py b/module/plugins/hooks/RestartFailed.py index 85553d738..07fb80967 100644 --- a/module/plugins/hooks/RestartFailed.py +++ b/module/plugins/hooks/RestartFailed.py @@ -1,57 +1,44 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: Walter Purcaro -""" -  from module.plugins.Hook import Hook  class RestartFailed(Hook): -    __name__ = "RestartFailed" -    __version__ = "1.53" +    __name__    = "RestartFailed" +    __type__    = "hook" +    __version__ = "1.57" + +    __config__ = [("interval", "int", "Check interval in minutes", 90)] +      __description__ = """Periodically restart all failed downloads in queue""" -    __config__ = [("activated", "bool", "Activated", False), -                  ("interval", "int", "Interval in minutes", 90)] -    __author_name__ = "Walter Purcaro" -    __author_mail__ = "vuolter@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] -    event_list = ["pluginConfigChanged"] -    MIN_INTERVAL = 15 * 60  # seconds +    # event_list = ["pluginConfigChanged"] -    def periodical(self): -        self.logDebug("Restart all failed downloads now") -        self.core.api.restartFailed() +    MIN_INTERVAL = 15 * 60  #: 15m minimum check interval (value is in seconds) -    def restartPeriodical(self, interval): -        self.logDebug("Set periodical interval to %s seconds" % interval) -        if self.cb: -            self.core.scheduler.removeJob(self.cb) -        self.interval = interval -        self.cb = self.core.scheduler.addJob(interval, self._periodical, threaded=False)      def pluginConfigChanged(self, plugin, name, value): -        value *= 60          if name == "interval": -            if self.interval != value > self.MIN_INTERVAL: -                self.restartPeriodical(value) +            interval = value * 60 +            if self.MIN_INTERVAL <= interval != self.interval: +                self.core.scheduler.removeJob(self.cb) +                self.interval = interval +                self.initPeriodical()              else: -                self.logWarning("Cannot change interval: given value is equal to the current or \ -                                 smaller than %s seconds" % self.MIN_INTERVAL) +                self.logDebug("Invalid interval value, kept current") + + +    def periodical(self): +        self.logDebug(_("Restart failed downloads")) +        self.core.api.restartFailed() + + +    def setup(self): +        self.interval = self.MIN_INTERVAL +      def coreReady(self): -        self.pluginConfigChanged(plugin="RestartFailed", name="interval", value=self.getConfig("interval")) +        self.pluginConfigChanged(self.__name__, "interval", self.getConfig("interval")) diff --git a/module/plugins/hooks/RestartSlow.py b/module/plugins/hooks/RestartSlow.py new file mode 100644 index 000000000..c2fdf6f95 --- /dev/null +++ b/module/plugins/hooks/RestartSlow.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +import pycurl + +from module.plugins.Hook import Hook + + +class RestartSlow(Hook): +    __name__    = "RestartSlow" +    __type__    = "hook" +    __version__ = "0.03" + +    __config__ = [("free_limit"   , "int" ,  "Transfer speed threshold in kilobytes"                     , 100 ), +                  ("free_time"    , "int" ,  "Sample interval in minutes"                                , 5   ), +                  ("premium_limit", "int" ,  "Transfer speed threshold for premium download in kilobytes", 300 ), +                  ("premium_time" , "int" ,  "Sample interval for premium download in minutes"           , 2   ), +                  ("safe_mode"    , "bool",  "Don't restart if download is not resumable"                , True)] + +    __description__ = """Restart slow downloads""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    event_list = ["downloadStarts"] + + +    def setup(self): +        self.info = {'chunk': {}} + + +    def initPeriodical(self): +        pass + + +    def periodical(self): +        if not self.pyfile.req.dl: +            return + +        if self.getConfig("safe_mode") and not self.pyfile.plugin.resumeDownload: +            time  = 30 +            limit = 5 +        else: +            type  = "premium" if self.pyfile.plugin.premium else "free" +            time  = max(30, self.getConfig("%s_time" % type) * 60) +            limit = max(5, self.getConfig("%s_limit" % type) * 1024) + +        chunks = [chunk for chunk in self.pyfile.req.dl.chunks \ +                  if chunk.id not in self.info['chunk'] or self.info['chunk'][chunk.id] is not (time, limit)] + +        for chunk in chunks: +            chunk.c.setopt(pycurl.LOW_SPEED_TIME , time) +            chunk.c.setopt(pycurl.LOW_SPEED_LIMIT, limit) + +            self.info['chunk'][chunk.id] = (time, limit) + + +    def downloadStarts(self, pyfile, url, filename): +        if self.cb or (self.getConfig("safe_mode") and not pyfile.plugin.resumeDownload): +            return + +        super(RestartSlow, self).initPeriodical() diff --git a/module/plugins/hooks/SimplyPremiumCom.py b/module/plugins/hooks/SimplyPremiumCom.py index ca1122e45..5f2d2a42c 100644 --- a/module/plugins/hooks/SimplyPremiumCom.py +++ b/module/plugins/hooks/SimplyPremiumCom.py @@ -1,42 +1,29 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -from module.plugins.internal.MultiHoster import MultiHoster -from module.network.RequestFactory import getURL  from module.common.json_layer import json_loads +from module.plugins.internal.MultiHook import MultiHook -class SimplyPremiumCom(MultiHoster): -    __name__ = "SimplyPremiumCom" -    __version__ = "0.01" -    __type__ = "hook" +class SimplyPremiumCom(MultiHook): +    __name__    = "SimplyPremiumCom" +    __type__    = "hook" +    __version__ = "0.04" +      __config__ = [("activated", "bool", "Activated", "False"), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", ""), -                  ("unloadFailing", "bool", "Revert to standard download if download fails", "False"), +                  ("mode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", ""), +                  ("revertfailed", "bool", "Revert to standard download if download fails", "False"),                    ("interval", "int", "Reload interval in hours (0 to disable)", "24")] -    __description__ = """Simply-Premium.Com hook plugin""" -    __author_name__ = ("EvolutionClip") -    __author_mail__ = ("evolutionclip@live.de") -    def getHoster(self): -        json_data = getURL('http://www.simply-premium.com/api/hosts.php?format=json&online=1') +    __description__ = """Simply-Premium.com hook plugin""" +    __license__     = "GPLv3" +    __authors__     = [("EvolutionClip", "evolutionclip@live.de")] + + +    def getHosters(self): +        json_data = self.getURL("http://www.simply-premium.com/api/hosts.php", get={'format': "json", 'online': 1})          json_data = json_loads(json_data) -        host_list = [element['host'] for element in json_data['result']] +        host_list = [element['regex'] for element in json_data['result']]          return host_list diff --git a/module/plugins/hooks/SimplydebridCom.py b/module/plugins/hooks/SimplydebridCom.py index a523d2404..13c957294 100644 --- a/module/plugins/hooks/SimplydebridCom.py +++ b/module/plugins/hooks/SimplydebridCom.py @@ -1,20 +1,21 @@  # -*- coding: utf-8 -*- -from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster +from module.plugins.internal.MultiHook import MultiHook -class SimplydebridCom(MultiHoster): -    __name__ = "SimplydebridCom" -    __version__ = "0.01" -    __type__ = "hook" -    __config__ = [("activated", "bool", "Activated", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", "")] +class SimplydebridCom(MultiHook): +    __name__    = "SimplydebridCom" +    __type__    = "hook" +    __version__ = "0.03" + +    __config__ = [("mode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", "")] +      __description__ = """Simply-Debrid.com hook plugin""" -    __author_name__ = "Kagenoshin" -    __author_mail__ = "kagenoshin@gmx.ch" +    __license__     = "GPLv3" +    __authors__     = [("Kagenoshin", "kagenoshin@gmx.ch")] + -    def getHoster(self): -        page = getURL("http://simply-debrid.com/api.php?list=1") +    def getHosters(self): +        page = self.getURL("http://simply-debrid.com/api.php", get={'list': 1})          return [x.strip() for x in page.rstrip(';').replace("\"", "").split(";")] diff --git a/module/plugins/hooks/SkipRev.py b/module/plugins/hooks/SkipRev.py new file mode 100644 index 000000000..cc32c365e --- /dev/null +++ b/module/plugins/hooks/SkipRev.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- + +from types import MethodType +from urllib import unquote +from urlparse import urlparse + +from module.PyFile import PyFile +from module.plugins.Hook import Hook +from module.plugins.Plugin import SkipDownload + + +def _setup(self): +    self.pyfile.plugin._setup() +    if self.pyfile.hasStatus("skipped"): +        raise SkipDownload(self.pyfile.statusname or self.pyfile.pluginname) + + +class SkipRev(Hook): +    __name__    = "SkipRev" +    __type__    = "hook" +    __version__ = "0.21" + +    __config__ = [("tokeep", "int", "Number of rev files to keep for package (-1 to auto)", -1)] + +    __description__ = """Skip files ending with extension rev""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass + + +    def _pyname(self, pyfile): +        url    = pyfile.url +        plugin = pyfile.plugin + +        if hasattr(plugin, "info") and 'name' in plugin.info and plugin.info['name']: +            name = plugin.info['name'] + +        elif hasattr(plugin, "parseInfos"): +            name = next(plugin.parseInfos([url]))['name'] + +        elif hasattr(plugin, "getInfo"):  #@NOTE: if parseInfos was not found, getInfo should be missing too +            name = plugin.getInfo(url)['name'] + +        else: +            self.logWarning("Unable to grab file name") +            name = urlparse(unquote(url)).path.split('/')[-1] + +        return name + + +    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): +        if pyfile.statusname is "unskipped" or not self._pyname(pyfile).endswith(".rev"): +            return + +        tokeep = self.getConfig("tokeep") + +        if tokeep: +            saved = [True for link in self.core.api.getPackageData(pyfile.package().id).links \ +                     if link.name.endswith(".rev") and link.status in (0, 12)].count(True) + +            if not saved or saved < tokeep:  #: keep one rev at least in auto mode +                return + +        pyfile.setCustomStatus("SkipRev", "skipped") +        pyfile.plugin._setup = pyfile.plugin.setup +        pyfile.plugin.setup  = MethodType(_setup, pyfile.plugin)  #: work-around: inject status checker inside the preprocessing routine of the plugin + + +    def downloadFailed(self, pyfile): +        #: Check if pyfile is still "failed", +        #  maybe might has been restarted in meantime +        if pyfile.status != 8: +            return + +        tokeep = self.getConfig("tokeep") + +        if not tokeep: +            return + +        for link in self.core.api.getPackageData(pyfile.package().id).links: +            if link.status is 4 and link.name.endswith(".rev"): +                pylink = self._pyfile(link) + +                if tokeep > -1 or pyfile.name.endswith(".rev"): +                    pylink.setStatus("queued") +                else: +                    pylink.setCustomStatus("unskipped", "queued") + +                self.core.files.save() +                pylink.release() +                return diff --git a/module/plugins/hooks/UnSkipOnFail.py b/module/plugins/hooks/UnSkipOnFail.py index af6039ecd..1becb937a 100644 --- a/module/plugins/hooks/UnSkipOnFail.py +++ b/module/plugins/hooks/UnSkipOnFail.py @@ -1,97 +1,95 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. +from module.PyFile import PyFile +from module.plugins.Hook import Hook -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. +class UnSkipOnFail(Hook): +    __name__    = "UnSkipOnFail" +    __type__    = "hook" +    __version__ = "0.05" -    @author: hgg -""" -from os.path import basename +    __config__ = [("activated", "bool", "Activated", True)] -from module.utils import fs_encode -from module.plugins.Hook import Hook -from module.PyFile import PyFile +    __description__ = """Queue skipped duplicates when download fails""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] -class UnSkipOnFail(Hook): -    __name__ = 'UnSkipOnFail' -    __version__ = '0.01' -    __description__ = """When a download fails, restart skipped duplicates""" -    __config__ = [("activated", "bool", "Activated", True)] -    __author_name__ = "hagg" -    __author_mail__ = "" +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass +      def downloadFailed(self, pyfile): -        pyfile_name = basename(pyfile.name) -        pid = pyfile.package().id -        msg = 'look for skipped duplicates for %s (pid:%s)...' -        self.logInfo(msg % (pyfile_name, pid)) -        dups = self.findDuplicates(pyfile) -        for link in dups: -            # check if link is "skipped"(=4) -            if link.status == 4: -                lpid = link.packageID -                self.logInfo('restart "%s" (pid:%s)...' % (pyfile_name, lpid)) -                self.setLinkStatus(link, "queued") - -    def findDuplicates(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("UnSkipOnFail", "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 (basename(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.          """ -        dups = [] -        pyfile_name = fs_encode(basename(pyfile.name)) -        # get packages (w/o files, as most file data is useless here) -        queue = self.core.api.getQueue() +        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 fs_encode(package.folder) == fs_encode(pyfile.package().folder): -                # now get packaged data w/ files/links -                pdata = self.core.api.getPackageData(package.pid) -                if pdata.links: -                    for link in pdata.links: -                        link_name = fs_encode(basename(link.name)) -                        # check if link name collides with pdata's name -                        if link_name == pyfile_name: -                            # at last check if it is not pyfile itself -                            if link.fid != pyfile.id: -                                dups.append(link) -        return dups - -    def setLinkStatus(self, link, new_status): -        """ 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. -        """ -        pyfile = PyFile(self.core.files, -                        link.fid, -                        link.url, -                        link.name, -                        link.size, -                        link.status, -                        link.error, -                        link.plugin, -                        link.packageID, -                        link.order) -        pyfile.setStatus(new_status) -        self.core.files.save() -        pyfile.release() +            #: 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/module/plugins/hooks/UnrestrictLi.py b/module/plugins/hooks/UnrestrictLi.py index 4f8f11625..e481e8449 100644 --- a/module/plugins/hooks/UnrestrictLi.py +++ b/module/plugins/hooks/UnrestrictLi.py @@ -1,41 +1,27 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -from module.plugins.internal.MultiHoster import MultiHoster -from module.network.RequestFactory import getURL +  from module.common.json_layer import json_loads +from module.plugins.internal.MultiHook import MultiHook + +class UnrestrictLi(MultiHook): +    __name__    = "UnrestrictLi" +    __type__    = "hook" +    __version__ = "0.04" -class UnrestrictLi(MultiHoster): -    __name__ = "UnrestrictLi" -    __version__ = "0.02" -    __type__ = "hook" -    __config__ = [("activated", "bool", "Activated", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", ""), -                  ("unloadFailing", "bool", "Revert to standard download if download fails", False), +    __config__ = [("mode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", ""), +                  ("revertfailed", "bool", "Revert to standard download if download fails", False),                    ("interval", "int", "Reload interval in hours (0 to disable)", 24),                    ("history", "bool", "Delete History", False)]      __description__ = """Unrestrict.li hook plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + -    def getHoster(self): -        json_data = getURL('http://unrestrict.li/api/jdownloader/hosts.php?format=json') +    def getHosters(self): +        json_data = self.getURL("http://unrestrict.li/api/jdownloader/hosts.php", get={'format': "json"})          json_data = json_loads(json_data)          host_list = [element['host'] for element in json_data['result']] diff --git a/module/plugins/hooks/UpdateManager.py b/module/plugins/hooks/UpdateManager.py index 5f88ea6d0..c72699228 100644 --- a/module/plugins/hooks/UpdateManager.py +++ b/module/plugins/hooks/UpdateManager.py @@ -1,121 +1,223 @@  # -*- coding: utf-8 -*- -import sys +from __future__ import with_statement +  import re -from os import remove, stat -from os.path import join, isfile -from time import time +import sys + +from operator import itemgetter +from os import path, remove, stat -from module.ConfigParser import IGNORE  from module.network.RequestFactory import getURL -from module.plugins.Hook import threaded, Expose, Hook +from module.plugins.Hook import Expose, Hook, threaded +from module.utils import save_join  class UpdateManager(Hook): -    __name__ = "UpdateManager" -    __version__ = "0.16" -    __description__ = """Checks for updates""" -    __config__ = [("activated", "bool", "Activated", True), -                  ("interval", "int", "Check interval in minutes", 480), -                  ("debug", "bool", "Check for plugin changes when in debug mode", False)] -    __author_name__ = ("RaNaN", "stickell") -    __author_mail__ = ("ranan@pyload.org", "l.stickell@yahoo.it") - -    URL = "http://updatemanager.pyload.org" -    MIN_TIME = 3 * 60 * 60  # 3h minimum check interval - -    @property -    def debug(self): -        return self.core.debug and self.getConfig("debug") +    __name__    = "UpdateManager" +    __type__    = "hook" +    __version__ = "0.42" + +    __config__ = [("activated"    , "bool"                         , "Activated"                                     , True              ), +                  ("mode"         , "pyLoad + plugins;plugins only", "Check updates for"                             , "pyLoad + plugins"), +                  ("interval"     , "int"                          , "Check interval in hours"                       , 8                 ), +                  ("autorestart"  , "bool"                         , "Automatically restart pyLoad when required"    , True              ), +                  ("reloadplugins", "bool"                         , "Monitor plugins for code changes in debug mode", True              ), +                  ("nodebugupdate", "bool"                         , "Don't check for updates in debug mode"         , True              )] + +    __description__ = """ Check for updates """ +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    # event_list = ["pluginConfigChanged"] + +    SERVER_URL   = "http://updatemanager.pyload.org" +    VERSION      = re.compile(r'__version__.*=.*("|\')([\d.]+)') +    MIN_INTERVAL = 3 * 60 * 60  #: 3h minimum check interval (value is in seconds) + + +    def pluginConfigChanged(self, plugin, name, value): +        if name == "interval": +            interval = value * 60 * 60 +            if self.MIN_INTERVAL <= interval != self.interval: +                self.core.scheduler.removeJob(self.cb) +                self.interval = interval +                self.initPeriodical() +            else: +                self.logDebug("Invalid interval value, kept current") + +        elif name == "reloadplugins": +            if self.cb2: +                self.core.scheduler.removeJob(self.cb2) +            if value is True and self.core.debug: +                self.periodical2() + + +    def coreReady(self): +        self.pluginConfigChanged(self.__name__, "interval", self.getConfig("interval")) +        x = lambda: self.pluginConfigChanged(self.__name__, "reloadplugins", self.getConfig("reloadplugins")) +        self.core.scheduler.addJob(10, x, threaded=False) + + +    def unload(self): +        self.pluginConfigChanged(self.__name__, "reloadplugins", False) +      def setup(self): -        if self.debug: -            self.logDebug("Monitoring file changes") -            self.interval = 4 -            self.last_check = 0  # timestamp of updatecheck -            self.old_periodical = self.periodical -            self.periodical = self.checkChanges -            self.mtimes = {}  # recordes times -        else: -            self.interval = max(self.getConfig("interval") * 60, self.MIN_TIME) +        self.cb2      = None +        self.interval = self.MIN_INTERVAL +        self.updating = False +        self.info     = {'pyload': False, 'version': None, 'plugins': False} +        self.mtimes   = {}  #: store modification time for each plugin -        self.updated = False -        self.reloaded = True -        self.version = "None" -        self.info = {"pyload": False, "plugins": False} +    def periodical2(self): +        if not self.updating: +            self.autoreloadPlugins() + +        self.cb2 = self.core.scheduler.addJob(4, self.periodical2, threaded=False) -    @threaded -    def periodical(self): -        updates = self.checkForUpdate() -        if updates: -            self.checkPlugins(updates) - -        if self.updated and not self.reloaded: -            self.info["plugins"] = True -            self.logInfo(_("*** Plugins have been updated, please restart pyLoad ***")) -        elif self.updated and self.reloaded: -            self.logInfo(_("Plugins updated and reloaded")) -            self.updated = False -        elif self.version == "None": -            self.logInfo(_("No plugin updates available"))      @Expose -    def recheckForUpdates(self): -        """recheck if updates are available""" -        self.periodical() +    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 = [] -    def checkForUpdate(self): -        """checks if an update is available, return result""" +        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 path.isfile(f): +                    continue + +                mtime = 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 periodical(self): +        if not self.info['pyload'] and not (self.getConfig("nodebugupdate") and self.core.debug): +            self.updateThread() + + +    def server_request(self):          try: -            if self.version == "None":  # No updated known -                version_check = getURL(self.URL, get={'v': self.core.api.getServerVersion()}).splitlines() -                self.version = version_check[0] - -                # Still no updates, plugins will be checked -                if self.version == "None": -                    self.logInfo(_("No Updates for pyLoad")) -                    return version_check[1:] - -            self.info["pyload"] = True -            self.logInfo(_("***  New pyLoad Version %s available  ***") % self.version) -            self.logInfo(_("***  Get it here: http://pyload.org/download  ***")) +            return getURL(self.SERVER_URL, get={'v': self.core.api.getServerVersion()}).splitlines()          except: -            self.logWarning(_("Not able to connect server for updates")) +            self.logWarning(_("Unable to contact server to get updates")) -        return None  # Nothing will be done -    def checkPlugins(self, updates): -        """ checks for plugins updates""" +    @threaded +    def updateThread(self): +        self.updating = True -        # plugins were already updated -        if self.info["plugins"]: -            return +        status = self.update(onlyplugin=self.getConfig("mode") == "plugins only") + +        if status is 2 and self.getConfig("autorestart"): +            self.core.api.restart() +        else: +            self.updating = False -        reloads = [] -        vre = re.compile(r'__version__.*=.*("|\')([0-9.]+)') -        url = updates[0] -        schema = updates[1].split("|") -        if 'BLACKLIST' in updates: +    @Expose +    def updatePlugins(self): +        """ simple wrapper for calling plugin update quickly """ +        return self.update(onlyplugin=True) + + +    @Expose +    def update(self, onlyplugin=False): +        """ check for updates """ +        data = self.server_request() + +        if not data: +            exitcode = 0 + +        elif data[0] == "None": +            self.logInfo(_("No new pyLoad version available")) +            updates = data[1:] +            exitcode = self._updatePlugins(updates) + +        elif onlyplugin: +            exitcode = 0 + +        else: +            newversion = data[0] +            self.logInfo(_("***  New pyLoad Version %s available  ***") % newversion) +            self.logInfo(_("***  Get it here: https://github.com/pyload/pyload/releases  ***")) +            exitcode = 3 +            self.info['pyload'] = True +            self.info['version'] = newversion + +        return exitcode  #: 0 = No plugins updated; 1 = Plugins updated; 2 = Plugins updated, but restart required; 3 = No plugins updated, new pyLoad version available + + +    def _updatePlugins(self, updates): +        """ check for plugin updates """ + +        if self.info['plugins']: +            return False  #: plugins were already updated + +        exitcode = 0 +        updated  = [] + +        url    = updates[0] +        schema = updates[1].split('|') + +        if "BLACKLIST" in updates:              blacklist = updates[updates.index('BLACKLIST') + 1:] -            updates = updates[2:updates.index('BLACKLIST')] +            updates   = updates[2:updates.index('BLACKLIST')]          else:              blacklist = None -            updates = updates[2:] +            updates   = updates[2:] + +        upgradable  = [dict(zip(schema, x.split('|'))) for x in updates] +        blacklisted = [(x.split('|')[0], x.split('|')[1].rsplit('.', 1)[0]) for x in blacklist] if blacklist else [] + +        if blacklist: +            # Protect UpdateManager from self-removing +            try: +                blacklisted.remove(("hook", "UpdateManager")) +            except: +                pass + +            for t, n in blacklisted: +                for idx, plugin in enumerate(upgradable): +                    if n == plugin['name'] and t == plugin['type']: +                        upgradable.pop(idx) +                        break + +            for t, n in self.removePlugins(sorted(blacklisted)): +                self.logInfo(_("Removed blacklisted plugin [%(type)s] %(name)s") % { +                    'type': t, +                    'name': n, +                }) -        for plugin in updates: -            info = dict(zip(schema, plugin.split("|"))) -            filename = info["name"] -            prefix = info["type"] -            version = info["version"] +        for plugin in sorted(upgradable, 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: obsolete in 0.5.0 +            #@TODO: obsolete after 0.4.10              if prefix.endswith("s"):                  type = prefix[:-1]              else: @@ -123,79 +225,86 @@ class UpdateManager(Hook):              plugins = getattr(self.core.pluginManager, "%sPlugins" % type) -            if name in plugins: -                if float(plugins[name]["v"]) >= float(version): -                    continue +            oldver = float(plugins[name]['v']) if name in plugins else None +            newver = float(version) -            if name in IGNORE or (type, name) in IGNORE: +            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(_("New version of %(type)s|%(name)s : %(version).2f") % { -                "type": type, -                "name": name, -                "version": float(version) -            }) - +            self.logInfo(_(msg) % {'type'  : type, +                                   'name'  : name, +                                   'oldver': oldver, +                                   'newver': newver})              try: -                content = getURL(url % info) +                content = getURL(url % plugin) +                m = self.VERSION.search(content) + +                if m and m.group(2) == version: +                    with open(save_join("userplugins", prefix, filename), "wb") as f: +                        f.write(content) + +                    updated.append((prefix, name)) +                else: +                    raise Exception, _("Version mismatch") +              except Exception, e: -                self.logWarning(_("Error when updating %s") % filename, str(e)) -                continue +                self.logError(_("Error updating plugin: %s") % filename, str(e)) -            m = vre.search(content) -            if not m or m.group(2) != version: -                self.logWarning(_("Error when updating %s") % name, _("Version mismatch")) -                continue +        if updated: +            reloaded = self.core.pluginManager.reloadPlugins(updated) +            if reloaded: +                self.logInfo(_("Plugins updated and reloaded")) +                exitcode = 1 +            else: +                self.logInfo(_("*** Plugins have been updated, but need a pyLoad restart to be reloaded ***")) +                self.info['plugins'] = True +                exitcode = 2 +        else: +            self.logInfo(_("No plugin updates available")) -            f = open(join("userplugins", prefix, filename), "wb") -            f.write(content) -            f.close() -            self.updated = True +        return exitcode  #: 0 = No plugins updated; 1 = Plugins updated; 2 = Plugins updated, but restart required -            reloads.append((prefix, name)) -        if blacklist: -            self.executeBlacklist(blacklist) +    @Expose +    def removePlugins(self, type_plugins): +        """ delete plugins from disk """ -        self.reloaded = self.core.pluginManager.reloadPlugins(reloads) +        if not type_plugins: +            return -    def executeBlacklist(self, blacklist): -        for b in blacklist: -            type, name = b.split('|') -            if isfile(join("userplugins", type, name)): -                self.logInfo(_("Removing blacklisted plugin %(type)s|%(name)s") % { -                    "type": type, -                    "name": name -                }) -                remove(join("userplugins", type, name)) -            if isfile(join("userplugins", type, name.replace('.py', '.pyc'))): -                remove(join("userplugins", type, name.replace('.py', '.pyc'))) +        self.logDebug("Requested deletion of plugins: %s" % type_plugins) -    def checkChanges(self): -        if self.last_check + max(self.getConfig("interval") * 60, self.MIN_TIME) < time(): -            self.old_periodical() -            self.last_check = time() +        removed = [] -        modules = filter( -            lambda m: m and (m.__name__.startswith("module.plugins.") or m.__name__.startswith( -                "userplugins.")) and m.__name__.count(".") >= 2, sys.modules.itervalues()) +        for type, name in type_plugins: +            err = False +            file = name + ".py" -        reloads = [] +            for root in ("userplugins", path.join(pypath, "module", "plugins")): -        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 isfile(f): -                    continue +                filename = save_join(root, type, file) +                try: +                    remove(filename) +                except Exception, e: +                    self.logDebug("Error removing: %s" % path.basename(filename), str(e)) +                    err = True -                mtime = stat(f).st_mtime +                filename += "c" +                if path.isfile(filename): +                    try: +                        if type == "hook": +                            self.manager.deactivateHook(name) +                        remove(filename) +                    except Exception, e: +                        self.logDebug("Error removing: %s" % path.basename(filename), str(e)) +                        err = True -                if id not in self.mtimes: -                    self.mtimes[id] = mtime -                elif self.mtimes[id] < mtime: -                    reloads.append(id) -                    self.mtimes[id] = mtime +            if not err: +                id = (type, name) +                removed.append(id) -        self.core.pluginManager.reloadPlugins(reloads) +        return removed  #: return a list of the plugins successfully removed diff --git a/module/plugins/hooks/Vipleech4uCom.py b/module/plugins/hooks/Vipleech4uCom.py deleted file mode 100644 index b2156b017..000000000 --- a/module/plugins/hooks/Vipleech4uCom.py +++ /dev/null @@ -1,80 +0,0 @@ -# -*- coding: utf-8 -*- -import re - -from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster - - -class Vipleech4uCom(MultiHoster): -    __name__ = "Vipleech4uCom" -    __version__ = "0.01" -    __type__ = "hook" -    __config__ = [("activated", "bool", "Activated", "False"), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", "")] -    __description__ = """Vipleech4u.com hook plugin""" -    __author_name__ = ("Kagenoshin") -    __author_mail__ = ("kagenoshin@gmx.ch") - -    HOSTER_PATTERN = re.compile(r'align\s*?=\s*?["\']*?left.*?<\s*?strong\s*?>([^<]*?)<', re.I) - -    def getHoster(self): -        hosters = { -            'depositfiles': ['depositfiles.com', 'dfiles.eu'], -            'uploaded': ['uploaded.to', 'uploaded.net', 'ul.to'], -            'rapidggator': ['rapidgator.net'],  # they have a typo it's called rapidgator -            'freakshare': ['freakshare.net', 'freakshare.com'], -            'filefactory': ['filefactory.com'], -            'bitshare': ['bitshare.com'], -            'share-online': ['share-online.biz', 'egoshare.com'], -            'youtube': ['youtube.com'], -            'turbobit': ['turbobit.net', 'unextfiles.com'], -            'firedrive': ['firedrive.com', 'putlocker.com'], -            'filepost': ['filepost.com', 'fp.io'], -            'netload': ['netload.in'], -            'uploadhero': ['uploadhero.com'], -            'ryushare': ['ryushare.com'], -        } - -        #check if the list is still valid -        self.check_for_new_or_removed_hosters(hosters) - -        #build list -        hoster_list = [] - -        for item in hosters.itervalues(): -            hoster_list.extend(item) - -        return hoster_list - -    def check_for_new_or_removed_hosters(self, hosters): -        #get the old hosters -        old_hosters = hosters.keys() - -        #load the current hosters from vipleech4u.com -        page = getURL('http://vipleech4u.com/hosts.php') -        current_hosters = self.HOSTER_PATTERN.findall(page) -        current_hosters = [x.lower() for x in current_hosters] - -        #let's look for new hosters -        new_hosters = [] - -        for hoster in current_hosters: -            if not hoster in old_hosters: -                new_hosters.append(hoster) - -        #let's look for removed hosters -        removed_hosters = [] - -        for hoster in old_hosters: -            if not hoster in current_hosters: -                removed_hosters.append(hoster) - -        if new_hosters: -            self.logDebug('The following new hosters were found on vipleech4u.com: %s' % str(new_hosters)) - -        if removed_hosters: -            self.logDebug('The following hosters were removed from vipleech4u.com: %s' % str(removed_hosters)) - -        if not (new_hosters and removed_hosters): -            self.logDebug('The hoster list is still valid.') diff --git a/module/plugins/hooks/WindowsPhoneToastNotify.py b/module/plugins/hooks/WindowsPhoneToastNotify.py index 7c005fcbb..ed305778c 100644 --- a/module/plugins/hooks/WindowsPhoneToastNotify.py +++ b/module/plugins/hooks/WindowsPhoneToastNotify.py @@ -1,40 +1,34 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: RaNaN, Godofdream, zoidberg -""" -import time  import httplib +import time +  from module.plugins.Hook import Hook  class WindowsPhoneToastNotify(Hook): -    __name__ = "WindowsPhoneToastNotify" -    __version__ = "0.02" -    __description__ = """Send push notifications to Windows Phone""" -    __author_name__ = "Andy Voigt" -    __author_mail__ = "phone-support@hotmail.de" -    __config__ = [("activated", "bool", "Activated", False), -                  ("force", "bool", "Force even if client is connected", False), +    __name__    = "WindowsPhoneToastNotify" +    __type__    = "hook" +    __version__ = "0.03" + +    __config__ = [("force", "bool", "Force even if client is connected", False),                    ("pushId", "str", "pushId", ""),                    ("pushUrl", "str", "pushUrl", ""),                    ("pushTimeout", "int", "Timeout between notifications in seconds", 0)] +    __description__ = """Send push notifications to Windows Phone""" +    __license__     = "GPLv3" +    __authors__     = [("Andy Voigt", "phone-support@hotmail.de")] + + +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass + +      def setup(self): -        self.info = {} +        self.info = {}  #@TODO: Remove in 0.4.10 +      def getXmlData(self):          myxml = ("<?xml version='1.0' encoding='utf-8'?> <wp:Notification xmlns:wp='WPNotification'> " @@ -42,6 +36,7 @@ class WindowsPhoneToastNotify(Hook):                   "</wp:Toast> </wp:Notification>")          return myxml +      def doRequest(self):          URL = self.getConfig("pushUrl")          request = self.getXmlData() @@ -57,6 +52,7 @@ class WindowsPhoneToastNotify(Hook):          webservice.close()          self.setStorage("LAST_NOTIFY", time.time()) +      def newCaptchaTask(self, task):          if not self.getConfig("pushId") or not self.getConfig("pushUrl"):              return False diff --git a/module/plugins/hooks/XFileSharingPro.py b/module/plugins/hooks/XFileSharingPro.py index 19ecc08b6..79e373ad3 100644 --- a/module/plugins/hooks/XFileSharingPro.py +++ b/module/plugins/hooks/XFileSharingPro.py @@ -6,70 +6,126 @@ from module.plugins.Hook import Hook  class XFileSharingPro(Hook): -    __name__ = "XFileSharingPro" -    __version__ = "0.11" -    __type__ = "hook" -    __config__ = [("activated", "bool", "Activated", True), -                  ("loadDefault", "bool", "Include default (built-in) hoster list", True), -                  ("includeList", "str", "Include hosters (comma separated)", ""), -                  ("excludeList", "str", "Exclude hosters (comma separated)", "")] -    __description__ = """XFileSharingPro hook plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __name__    = "XFileSharingPro" +    __type__    = "hook" +    __version__ = "0.28" + +    __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\.)?([\w.^_]+(?:\.[a-zA-Z]{2,})(?:\:\d+)?)/(?:embed-)?\w{12}(?:\W|$)', +                          r'https?://(?:[^/]+\.)?(%s)/(?:embed-)?\w+'), +              'crypter': (r'https?://(?:www\.)?([\w.^_]+(?:\.[a-zA-Z]{2,})(?:\:\d+)?)/(?:user|folder)s?/\w+', +                          r'https?://(?:[^/]+\.)?(%s)/(?:user|folder)s?/\w+')} + +    HOSTER_BUILTIN  = [#WORKING HOSTERS: +                       "eyesfile.ca", "file4safe.com", "fileband.com", "filedwon.com", "filevice.com", "hostingbulk.com", +                       "ravishare.com", "salefiles.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 = [] + + +    # def pluginConfigChanged(self.__name__, plugin, name, value): +        # self.loadPattern() + + +    #@TODO: Remove in 0.4.10 +    def initPeriodical(self): +        pass +      def coreReady(self):          self.loadPattern() +      def loadPattern(self): -        hosterList = self.getConfigSet('includeList') -        excludeList = self.getConfigSet('excludeList') - -        if self.getConfig('loadDefault'): -            hosterList |= set(( -                #WORKING HOSTERS: -                "aieshare.com", "asixfiles.com", "banashare.com", "cyberlocker.ch", "eyesfile.co", "eyesfile.com", -                "fileband.com", "filedwon.com", "filedownloads.org", "hipfile.com", "kingsupload.com", "mlfat4arab.com", -                "netuploaded.com", "odsiebie.pl", "q4share.com", "ravishare.com", "uptobox.com", "verzend.be", -                "xvidstage.com", "thefile.me", "sharesix.com", "hostingbulk.com", -                #NOT TESTED: -                "bebasupload.com", "boosterking.com", "divxme.com", "filevelocity.com", "glumbouploads.com", -                "grupload.com", "heftyfile.com", "host4desi.com", "laoupload.com", "linkzhost.com", "movreel.com", -                "rockdizfile.com", "limfile.com", "share76.com", "sharebeast.com", "sharehut.com", "sharerun.com", -                "shareswift.com", "sharingonline.com", "6ybh-upload.com", "skipfile.com", "spaadyshare.com", -                "space4file.com", "uploadbaz.com", "uploadc.com", "uploaddot.com", "uploadfloor.com", "uploadic.com", -                "uploadville.com", "vidbull.com", "zalaa.com", "zomgupload.com", "kupload.org", "movbay.org", -                "multishare.org", "omegave.org", "toucansharing.org", "uflinq.org", "banicrazy.info", "flowhot.info", -                "upbrasil.info", "shareyourfilez.biz", "bzlink.us", "cloudcache.cc", "fileserver.cc", "farshare.to", -                "filemaze.ws", "filehost.ws", "filestock.ru", "moidisk.ru", "4up.im", "100shared.com", "sharesix.com", -                "thefile.me", "filenuke.com", "sharerepo.com", "mightyupload.com", -                #WRONG FILE NAME: -                "sendmyway.com", "upchi.co.il", -                #NOT WORKING: -                "amonshare.com", "imageporter.com", "file4safe.com", -                #DOWN OR BROKEN: -                "ddlanime.com", "fileforth.com", "loombo.com", "goldfile.eu", "putshare.com" -            )) - -        hosterList -= (excludeList) -        hosterList -= set(('', u'')) - -        if not hosterList: -            self.unload() -            return - -        regexp = r"http://(?:[^/]*\.)?(%s)/\w{12}" % ("|".join(sorted(hosterList)).replace('.', '\.')) -        #self.logDebug(regexp) - -        dict = self.core.pluginManager.hosterPlugins['XFileSharingPro'] -        dict["pattern"] = regexp -        dict["re"] = re.compile(regexp) -        self.logDebug("Pattern loaded - handling %d hosters" % len(hosterList)) - -    def getConfigSet(self, option): -        s = self.getConfig(option).lower().replace('|', ',').replace(';', ',') -        return set([x.strip() for x in s.split(',')]) +        use_builtin_list = self.getConfig('use_builtin_list') + +        for type, plugin in (("hoster",  "XFileSharingPro"), +                             ("crypter", "XFileSharingProFolder")): +            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: +                s = self.getConfig('%s_list' % type).replace('\\', '').replace('|', ',').replace(';', ',').lower() +                plugin_list = set([x.strip() for x in s.split(',')]) + +                if use_builtin_list: +                    plugin_list |= set([x.lower() for x in getattr(self, "%s_BUILTIN" % type.upper())]) + +                plugin_list -= set(('', u'')) + +                if not plugin_list: +                    self.logInfo(_("No %s to handle") % type) +                    self._unload(type, plugin) +                    return + +                match_list = '|'.join(sorted(plugin_list)) + +                len_match_list = len(plugin_list) +                self.logInfo(_("Handling %d %s%s: %s") % (len_match_list, +                                                          type, +                                                          "" if len_match_list is 1 else "s", +                                                          match_list.replace('|', ', '))) + +                pattern = self.regexp[type][1] % match_list.replace('.', '\.') + +            dict = self.core.pluginManager.plugins[type][plugin] +            dict['pattern'] = pattern +            dict['re'] = re.compile(pattern) + +            self.logDebug("Loaded %s pattern: %s" % (type, pattern)) + + +    def _unload(self, type, plugin): +        dict = self.core.pluginManager.plugins[type][plugin] +        dict['pattern'] = r'^unmatchable$' +        dict['re'] = re.compile(dict['pattern']) +      def unload(self): -        dict = self.core.pluginManager.hosterPlugins['XFileSharingPro'] -        dict["pattern"] = r"^unmatchable$" -        dict["re"] = re.compile(r"^unmatchable$") +        # self.unloadHoster("BasePlugin") +        for type, plugin in (("hoster",  "XFileSharingPro"), +                             ("crypter", "XFileSharingProFolder")): +            self._unload(type, plugin) + + +    def unloadHoster(self, hoster): +        hdict = self.core.pluginManager.hosterPlugins[hoster] +        if "new_name" in hdict and hdict['new_name'] is "XFileSharingPro": +            if "module" in hdict: +                del hdict['module'] + +            if "new_module" in hdict: +                del hdict['new_module'] +                del hdict['new_name'] + +            return True +        else: +            return False + + +    # def downloadFailed(self, pyfile): +        # if pyfile.pluginname is "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/module/plugins/hooks/XMPPInterface.py b/module/plugins/hooks/XMPPInterface.py index 5ecf4e153..bbeab4341 100644 --- a/module/plugins/hooks/XMPPInterface.py +++ b/module/plugins/hooks/XMPPInterface.py @@ -1,49 +1,35 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: RaNaN -    @interface-version: 0.2 -""" -  from pyxmpp import streamtls  from pyxmpp.all import JID, Message -from pyxmpp.jabber.client import JabberClient  from pyxmpp.interface import implements  from pyxmpp.interfaces import * +from pyxmpp.jabber.client import JabberClient  from module.plugins.hooks.IRCInterface import IRCInterface  class XMPPInterface(IRCInterface, JabberClient): -    __name__ = "XMPPInterface" +    __name__    = "XMPPInterface" +    __type__    = "hook"      __version__ = "0.11" -    __description__ = """Connect to jabber and let owner perform different tasks""" -    __config__ = [("activated", "bool", "Activated", False), -                  ("jid", "str", "Jabber ID", "user@exmaple-jabber-server.org"), + +    __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)] -    __author_name__ = "RaNaN" -    __author_mail__ = "RaNaN@pyload.org" + +    __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) @@ -72,11 +58,13 @@ class XMPPInterface(IRCInterface, JabberClient):              self,          ] +      def coreReady(self):          self.new_package = {}          self.start() +      def packageFinished(self, pypack):          try:              if self.getConfig("info_pack"): @@ -84,6 +72,7 @@ class XMPPInterface(IRCInterface, JabberClient):          except:              pass +      def downloadFinished(self, pyfile):          try:              if self.getConfig("info_file"): @@ -92,45 +81,50 @@ class XMPPInterface(IRCInterface, JabberClient):          except:              pass +      def run(self):          # connect to IRC etc.          self.connect()          try:              self.loop()          except Exception, ex: -            self.logError("pyLoad XMPP: %s" % str(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("pyLoad XMPP: *** State changed: %s %r ***" % (state, arg)) +        self.logDebug("*** State changed: %s %r ***" % (state, arg)) +      def disconnected(self): -        self.logDebug("pyLoad XMPP: Client was disconnected") +        self.logDebug("Client was disconnected") +      def stream_closed(self, stream): -        self.logDebug("pyLoad XMPP: Stream was closed | %s" % stream) +        self.logDebug("Stream was closed", stream) +      def stream_error(self, err): -        self.logDebug("pyLoad XMPP: Stream Error: %s" % 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), -        ] +        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(u'pyLoad XMPP: Message from %s received.' % (unicode(stanza.get_from(), ))) -        self.logDebug(u'pyLoad XMPP: Body: %s Subject: %s Type: %s' % (body, subject, t)) +        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 @@ -174,20 +168,22 @@ class XMPPInterface(IRCInterface, JabberClient):                      messages.append(m)              except Exception, e: -                self.logError("pyLoad XMPP: " + repr(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("pyLoad XMPP: Send message to %s" % user) +            self.logDebug("Send message to", user)              to_jid = JID(user) @@ -203,9 +199,11 @@ class XMPPInterface(IRCInterface, JabberClient):              stream.send(m) +      def beforeReconnecting(self, ip):          self.disconnect() +      def afterReconnecting(self, ip):          self.connect() @@ -218,26 +216,29 @@ class VersionHandler(object):      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), -        ] +        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. diff --git a/module/plugins/hooks/ZeveraCom.py b/module/plugins/hooks/ZeveraCom.py index 4dee83ccb..51f759b1c 100644 --- a/module/plugins/hooks/ZeveraCom.py +++ b/module/plugins/hooks/ZeveraCom.py @@ -1,20 +1,21 @@  # -*- coding: utf-8 -*- -from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster +from module.plugins.internal.MultiHook import MultiHook -class ZeveraCom(MultiHoster): -    __name__ = "ZeveraCom" -    __version__ = "0.02" -    __type__ = "hook" -    __config__ = [("activated", "bool", "Activated", False), -                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), -                  ("hosterList", "str", "Hoster list (comma separated)", "")] +class ZeveraCom(MultiHook): +    __name__    = "ZeveraCom" +    __type__    = "hook" +    __version__ = "0.04" + +    __config__ = [("mode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("pluginlist", "str", "Hoster list (comma separated)", "")] +      __description__ = """Real-Debrid.com hook plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    def getHoster(self): -        page = getURL("http://www.zevera.com/jDownloader.ashx?cmd=gethosters") +    def getHosters(self): +        page = self.getURL("http://www.zevera.com/jDownloader.ashx", get={'cmd': "gethosters"})          return [x.strip() for x in page.replace("\"", "").split(",")] diff --git a/module/plugins/hoster/AlldebridCom.py b/module/plugins/hoster/AlldebridCom.py index 11ee8b7d3..d7dbe31a6 100644 --- a/module/plugins/hoster/AlldebridCom.py +++ b/module/plugins/hoster/AlldebridCom.py @@ -1,83 +1,82 @@  # -*- coding: utf-8 -*-  import re -from urllib import unquote +  from random import randrange -from module.plugins.Hoster import Hoster +from urllib import unquote +  from module.common.json_layer import json_loads +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo  from module.utils import parseFileSize -class AlldebridCom(Hoster): -    __name__ = "AlldebridCom" -    __version__ = "0.34" -    __type__ = "hoster" +class AlldebridCom(MultiHoster): +    __name__    = "AlldebridCom" +    __type__    = "hoster" +    __version__ = "0.42" + +    __pattern__ = r'https?://(?:www\.|s\d+\.)?alldebrid\.com/dl/[\w^_]+' -    __pattern__ = r'https?://(?:[^/]*\.)?alldebrid\..*'      __description__ = """Alldebrid.com hoster plugin""" -    __author_name__ = "Andy Voigt" -    __author_mail__ = "spamsales@online.de" +    __license__     = "GPLv3" +    __authors__     = [("Andy Voigt", "spamsales@online.de")] +      def getFilename(self, url):          try:              name = unquote(url.rsplit("/", 1)[1])          except IndexError:              name = "Unknown_Filename..." +          if name.endswith("..."):  # incomplete filename, append random stuff              name += "%s.tmp" % randrange(100, 999) +          return name +      def setup(self): -        self.chunkLimit = 16 +        self.chunkLimit     = 16          self.resumeDownload = True -    def process(self, pyfile): -        if re.match(self.__pattern__, pyfile.url): -            new_url = pyfile.url -        elif not self.account: -            self.logError(_("Please enter your %s account or deactivate this plugin") % "AllDebrid") -            self.fail("No AllDebrid account provided") -        else: -            self.logDebug("Old URL: %s" % pyfile.url) -            password = self.getPassword().splitlines() -            password = "" if not password else password[0] - -            url = "http://www.alldebrid.com/service.php?link=%s&json=true&pw=%s" % (pyfile.url, password) -            page = self.load(url) -            data = json_loads(page) - -            self.logDebug("Json data: %s" % str(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() + +    def handlePremium(self): +        password = self.getPassword() + +        data = json_loads(self.load("http://www.alldebrid.com/service.php", +                                     get={'link': self.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: -                if pyfile.name and not pyfile.name.endswith('.tmp'): -                    pyfile.name = data["filename"] -                pyfile.size = parseFileSize(data["filesize"]) -                new_url = data["link"] +                self.logWarning(data['error']) +                self.tempOffline() +        else: +            if self.pyfile.name and not self.pyfile.name.endswith('.tmp'): +                self.pyfile.name = data['filename'] +            self.pyfile.size = parseFileSize(data['filesize']) +            self.link = data['link']          if self.getConfig("https"): -            new_url = new_url.replace("http://", "https://") +            self.link = self.link.replace("http://", "https://")          else: -            new_url = new_url.replace("https://", "http://") +            self.link = self.link.replace("https://", "http://") -        if new_url != pyfile.url: -            self.logDebug("New URL: %s" % new_url) +        if self.link != self.pyfile.url: +            self.logDebug("New URL: %s" % self.link) -        if pyfile.name.startswith("http") or pyfile.name.startswith("Unknown"): +        if self.pyfile.name.startswith("http") or self.pyfile.name.startswith("Unknown"):              #only use when name wasnt already set -            pyfile.name = self.getFilename(new_url) +            self.pyfile.name = self.getFilename(self.link) + + +    def checkFile(self): +        super(AlldebridCom, self).checkFile() -        self.download(new_url, disposition=True) +        if self.checkDownload({'error': "<title>An error occured while processing your request</title>"}) is "error": +            self.retry(wait_time=60, reason=_("An error occured while generating link")) -        check = self.checkDownload({"error": "<title>An error occured while processing your request</title>", -                                    "empty": re.compile(r"^$")}) -        if check == "error": -            self.retry(wait_time=60, reason="An error occured while generating link.") -        elif check == "empty": -            self.retry(wait_time=60, reason="Downloaded File was empty.") +getInfo = create_getInfo(AlldebridCom) diff --git a/module/plugins/hoster/BasePlugin.py b/module/plugins/hoster/BasePlugin.py index 970ac8081..d0d8e7cc8 100644 --- a/module/plugins/hoster/BasePlugin.py +++ b/module/plugins/hoster/BasePlugin.py @@ -1,113 +1,108 @@  # -*- coding: utf-8 -*- -from urlparse import urlparse -from re import match, search +import re +  from urllib import unquote +from urlparse import urljoin, urlparse  from module.network.HTTPRequest import BadHeader +from module.plugins.internal.SimpleHoster import create_getInfo  from module.plugins.Hoster import Hoster -from module.utils import html_unescape, remove_chars  class BasePlugin(Hoster): -    __name__ = "BasePlugin" -    __type__ = "hoster" +    __name__    = "BasePlugin" +    __type__    = "hoster" +    __version__ = "0.26" +      __pattern__ = r'^unmatchable$' -    __version__ = "0.19" +      __description__ = """Base Plugin when any other didnt fit""" -    __author_name__ = "RaNaN" -    __author_mail__ = "RaNaN@pyload.org" +    __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 +        return {'name': urlparse(unquote(url)).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 3 if url else 1, 'url': unquote(url) or ""} +      def setup(self):          self.chunkLimit = -1          self.resumeDownload = True +      def process(self, pyfile):          """main function""" -        #debug part, for api exerciser -        if pyfile.url.startswith("DEBUG_API"): -            self.multiDL = False -            return - -        # self.__name__ = "NetloadIn" -        # pyfile.name = "test" -        # self.html = self.load("http://localhost:9000/short") -        # self.download("http://localhost:9000/short") -        # self.api = self.load("http://localhost:9000/short") -        # self.decryptCaptcha("http://localhost:9000/captcha") -        # -        # if pyfile.url == "79": -        #     self.core.api.addPackage("test", [str(i) for i in xrange(80)], 1) -        # -        # return -        if pyfile.url.startswith("http"): +        pyfile.name = self.getInfo(pyfile.url)['name'] + +        if not pyfile.url.startswith("http"): +            self.fail(_("No plugin matched")) +        for _i in xrange(5):              try:                  self.downloadFile(pyfile) +              except BadHeader, e: -                if e.code in (401, 403): -                    self.logDebug("Auth required") +                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 +                    server  = urlparse(pyfile.url).netloc                      if server in servers:                          self.logDebug("Logging on to %s" % server) -                        self.req.addAuth(account.accounts[server]["password"]) +                        self.req.addAuth(account.accounts[server]['password'])                      else: -                        for pwd in pyfile.package().password.splitlines(): -                            if ":" in pwd: -                                self.req.addAuth(pwd.strip()) -                                break +                        pwd = self.getPassword() +                        if ':' in pwd: +                            self.req.addAuth(pwd)                          else: -                            self.fail(_("Authorization required (username:password)")) - -                    self.downloadFile(pyfile) +                            self.fail(_("Authorization required"))                  else: -                    raise - +                    self.fail(e) +            else: +                break          else: -            self.fail("No Plugin matched and not a downloadable url.") +            self.fail(_("No file downloaded"))  #@TODO: Move to hoster class in 0.4.10 + +        if self.checkDownload({'empty': re.compile(r"^$")}) is "empty":  #@TODO: Move to hoster in 0.4.10 +            self.fail(_("Empty file")) +      def downloadFile(self, pyfile):          url = pyfile.url -        for _ in xrange(5): -            header = self.load(url, just_header=True) - -            # self.load does not raise a BadHeader on 404 responses, do it here -            if 'code' in header and header['code'] == 404: -                raise BadHeader(404) +        for i in xrange(1, 7):  #@TODO: retrieve the pycurl.MAXREDIRS value set by req +            header = self.load(url, ref=True, cookies=True, just_header=True, decode=True) -            if 'location' in header: -                self.logDebug("Location: " + header['location']) -                base = match(r'https?://[^/]+', url).group(0) -                if header['location'].startswith("http"): -                    url = unquote(header['location']) -                elif header['location'].startswith("/"): -                    url = base + unquote(header['location']) +            if 'location' not in header or not header['location']: +                if 'code' in header and header['code'] not in (200, 201, 203, 206): +                    self.logDebug("Received HTTP status code: %d" % header['code']) +                    self.fail(_("File not found"))                  else: -                    url = '%s/%s' % (base, unquote(header['location'])) +                    break + +            location = header['location'] + +            self.logDebug("Redirect #%d to: %s" % (i, location)) + +            if urlparse(location).scheme: +                url = location              else: -                break +                p = urlparse(url) +                base = "%s://%s" % (p.scheme, p.netloc) +                url = urljoin(base, location) +        else: +            self.fail(_("Too many redirects")) + +        self.download(unquote(url), disposition=True) + -        name = html_unescape(unquote(urlparse(url).path.split("/")[-1])) - -        if 'content-disposition' in header: -            self.logDebug("Content-Disposition: " + header['content-disposition']) -            m = search("filename(?P<type>=|\*=(?P<enc>.+)'')(?P<name>.*)", header['content-disposition']) -            if m: -                disp = m.groupdict() -                self.logDebug(disp) -                if not disp['enc']: -                    disp['enc'] = 'utf-8' -                name = remove_chars(disp['name'], "\"';").strip() -                name = unicode(unquote(name), disp['enc']) - -        if not name: -            name = url -        pyfile.name = name -        self.logDebug("Filename: %s" % pyfile.name) -        self.download(url, disposition=True) +getInfo = create_getInfo(BasePlugin) diff --git a/module/plugins/hoster/BayfilesCom.py b/module/plugins/hoster/BayfilesCom.py index b080eb386..0929ece01 100644 --- a/module/plugins/hoster/BayfilesCom.py +++ b/module/plugins/hoster/BayfilesCom.py @@ -1,97 +1,18 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. +class BayfilesCom(DeadHoster): +    __name__    = "BayfilesCom" +    __type__    = "hoster" +    __version__ = "0.09" -    @author: zoidberg -""" +    __pattern__ = r'https?://(?:www\.)?bayfiles\.(com|net)/file/(?P<ID>\w+/\w+/[^/]+)' -import re -from time import time - -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo -from module.common.json_layer import json_loads - - -class BayfilesCom(SimpleHoster): -    __name__ = "BayfilesCom" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?bayfiles\.(com|net)/file/(?P<ID>[a-zA-Z0-9]+/[a-zA-Z0-9]+/[^/]+)' -    __version__ = "0.06"      __description__ = """Bayfiles.com hoster plugin""" -    __author_name__ = ("zoidberg", "Walter Purcaro") -    __author_mail__ = ("zoidberg@mujmail.cz", "vuolter@gmail.com") - -    FILE_INFO_PATTERN = r'<p title="(?P<N>[^"]+)">[^<]*<strong>(?P<S>[0-9., ]+)(?P<U>[kKMG])i?B</strong></p>' -    FILE_OFFLINE_PATTERN = r'(<p>The requested file could not be found.</p>|<title>404 Not Found</title>)' - -    WAIT_PATTERN = r'>Your IP [0-9.]* has recently downloaded a file\. Upgrade to premium or wait (\d+) minutes\.<' -    VARS_PATTERN = r'var vfid = (\d+);\s*var delay = (\d+);' -    LINK_PATTERN = r"javascript:window.location.href = '([^']+)';" -    PREMIUM_LINK_PATTERN = r'(?:<a class="highlighted-btn" href="|(?=http://s\d+\.baycdn\.com/dl/))(.*?)"' - -    def handleFree(self): -        found = re.search(self.WAIT_PATTERN, self.html) -        if found: -            self.wait(int(found.group(1)) * 60) -            self.retry() - -        # Get download token -        found = re.search(self.VARS_PATTERN, self.html) -        if not found: -            self.parseError('VARS') -        vfid, delay = found.groups() - -        response = json_loads(self.load('https://bayfiles.com/ajax_download', get={ -            "_": time() * 1000, -            "action": "startTimer", -            "vfid": vfid}, decode=True)) - -        if not "token" in response or not response['token']: -            self.fail('No token') - -        self.wait(int(delay)) - -        self.html = self.load('https://bayfiles.com/ajax_download', get={ -            "token": response['token'], -            "action": "getLink", -            "vfid": vfid}) - -        # Get final link and download -        found = re.search(self.LINK_PATTERN, self.html) -        if not found: -            self.parseError("Free link") -        self.startDownload(found.group(1)) - -    def handlePremium(self): -        found = re.search(self.PREMIUM_LINK_PATTERN, self.html) -        if not found: -            self.parseError("Premium link") -        self.startDownload(found.group(1)) - -    def startDownload(self, url): -        self.logDebug("%s URL: %s" % ("Premium" if self.premium else "Free", url)) -        self.download(url) -        # check download -        check = self.checkDownload({ -            "waitforfreeslots": re.compile(r"<title>BayFiles</title>"), -            "notfound": re.compile(r"<title>404 Not Found</title>") -        }) -        if check == "waitforfreeslots": -            self.retry(30, 5 * 60, "Wait for free slot") -        elif check == "notfound": -            self.retry(30, 5 * 60, "404 Not found") +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")]  getInfo = create_getInfo(BayfilesCom) diff --git a/module/plugins/hoster/BezvadataCz.py b/module/plugins/hoster/BezvadataCz.py index 55f3f33b9..9ca4b4b1a 100644 --- a/module/plugins/hoster/BezvadataCz.py +++ b/module/plugins/hoster/BezvadataCz.py @@ -1,65 +1,55 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re +  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class BezvadataCz(SimpleHoster): -    __name__ = "BezvadataCz" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?bezvadata.cz/stahnout/.*' -    __version__ = "0.24" +    __name__    = "BezvadataCz" +    __type__    = "hoster" +    __version__ = "0.25" + +    __pattern__ = r'http://(?:www\.)?bezvadata\.cz/stahnout/.+' +      __description__ = """BezvaData.cz hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __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>' -    FILE_NAME_PATTERN = r'<p><b>Soubor: (?P<N>[^<]+)</b></p>' -    FILE_SIZE_PATTERN = r'<li><strong>Velikost:</strong> (?P<S>[^<]+)</li>' -    FILE_OFFLINE_PATTERN = r'<title>BezvaData \| Soubor nenalezen</title>'      def setup(self): -        self.multiDL = self.resumeDownload = True +        self.resumeDownload = True +        self.multiDL        = True +      def handleFree(self):          #download button -        found = re.search(r'<a class="stahnoutSoubor".*?href="(.*?)"', self.html) -        if not found: -            self.parseError("page1 URL") -        url = "http://bezvadata.cz%s" % found.group(1) +        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 _ in xrange(5): +        for _i in xrange(5):              action, inputs = self.parseHtmlForm('frm-stahnoutFreeForm')              if not inputs: -                self.parseError("FreeForm") +                self.error(_("FreeForm")) -            found = re.search(r'<img src="data:image/png;base64,(.*?)"', self.html) -            if not found: -                self.parseError("captcha img") +            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(found.group(1), imgtype='png') +                inputs['captcha'] = self.decryptCaptcha(m.group(1), imgtype='png')              finally:                  self.load = proper_load @@ -69,30 +59,34 @@ class BezvadataCz(SimpleHoster):                  self.correctCaptcha()                  break          else: -            self.fail("No valid captcha code entered") +            self.fail(_("No valid captcha code entered"))          #download url          self.html = self.load("http://bezvadata.cz%s" % action, post=inputs)          self.checkErrors() -        found = re.search(r'<a class="stahnoutSoubor2" href="(.*?)">', self.html) -        if not found: -            self.parseError("page2 URL") -        url = "http://bezvadata.cz%s" % found.group(1) +        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 -        found = re.search(r'id="countdown">(\d\d):(\d\d)<', self.html) -        wait_time = (int(found.group(1)) * 60 + int(found.group(2)) + 1) if found else 120 +        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.download(url) +      def checkErrors(self):          if 'images/button-download-disable.png' in self.html: -            self.longWait(5 * 60, 24)  # parallel dl limit +            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/module/plugins/hoster/BillionuploadsCom.py b/module/plugins/hoster/BillionuploadsCom.py index ab2634c91..b20ace0f1 100644 --- a/module/plugins/hoster/BillionuploadsCom.py +++ b/module/plugins/hoster/BillionuploadsCom.py @@ -1,21 +1,24 @@  # -*- coding: utf-8 -*- -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo -class BillionuploadsCom(XFileSharingPro): -    __name__ = "BillionuploadsCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?billionuploads.com/\w{12}' -    __version__ = "0.01" +class BillionuploadsCom(XFSHoster): +    __name__    = "BillionuploadsCom" +    __type__    = "hoster" +    __version__ = "0.04" + +    __pattern__ = r'http://(?:www\.)?billionuploads\.com/\w{12}' +      __description__ = """Billionuploads.com hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    HOSTER_NAME = "billionuploads.com" +    HOSTER_DOMAIN = "billionuploads.com" -    FILE_NAME_PATTERN = r'<b>Filename:</b>(?P<N>.*?)<br>' -    FILE_SIZE_PATTERN = r'<b>Size:</b>(?P<S>.*?)<br>' +    NAME_PATTERN = r'<td class="dofir" title="(?P<N>.+?)"' +    SIZE_PATTERN = r'<td class="dofir">(?P<S>[\d.,]+) (?P<U>[\w^_]+)'  getInfo = create_getInfo(BillionuploadsCom) diff --git a/module/plugins/hoster/BitshareCom.py b/module/plugins/hoster/BitshareCom.py index d4b0668c0..1c5c53f55 100644 --- a/module/plugins/hoster/BitshareCom.py +++ b/module/plugins/hoster/BitshareCom.py @@ -4,71 +4,83 @@ from __future__ import with_statement  import re -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class BitshareCom(SimpleHoster): -    __name__ = "BitshareCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?bitshare\.com/(files/(?P<id1>[a-zA-Z0-9]+)(/(?P<name>.*?)\.html)?|\?f=(?P<id2>[a-zA-Z0-9]+))' -    __version__ = "0.49" +    __name__    = "BitshareCom" +    __type__    = "hoster" +    __version__ = "0.51" + +    __pattern__ = r'http://(?:www\.)?bitshare\.com/(files/)?(?(1)|\?f=)(?P<ID>\w+)(?(1)/(?P<NAME>.+?)\.html)' +      __description__ = """Bitshare.com hoster plugin""" -    __author_name__ = ("Paul King", "fragonib") -    __author_mail__ = ("", "fragonib[AT]yahoo[DOT]es") +    __license__     = "GPLv3" +    __authors__     = [("Paul King", None), +                       ("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' -    HOSTER_DOMAIN = "bitshare.com" -    FILE_OFFLINE_PATTERN = r'(>We are sorry, but the requested file was not found in our database|>Error - File not available<|The file was deleted either by the uploader, inactivity or due to copyright claim)' -    FILE_INFO_PATTERN = r'Downloading (?P<N>.+) - (?P<S>[\d.]+) (?P<U>\w+)</h1>' -    FILE_AJAXID_PATTERN = r'var ajaxdl = "(.*?)";' -    CAPTCHA_KEY_PATTERN = r"http://api\.recaptcha\.net/challenge\?k=(.*?) " -    TRAFFIC_USED_UP = r"Your Traffic is used up for today. Upgrade to premium to continue!"      def setup(self): -        self.req.cj.setCookie(self.HOSTER_DOMAIN, "language_selection", "EN") -        self.multiDL = self.premium +        self.multiDL    = self.premium          self.chunkLimit = 1 +      def process(self, pyfile):          if self.premium:              self.account.relogin(self.user) -        self.pyfile = pyfile -          # File id          m = re.match(self.__pattern__, pyfile.url) -        self.file_id = max(m.group('id1'), m.group('id2')) +        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.FILE_OFFLINE_PATTERN, self.html): +        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. Wait 1800 seconds or reconnect!") -            self.logDebug("Waiting %d seconds." % 1800) +            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.FILE_INFO_PATTERN, self.html) +        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.FILE_AJAXID_PATTERN, self.html).group(1) +        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 -        url = self.getDownloadUrl() -        self.logDebug("Downloading file with url [%s]" % url) -        self.download(url) +        self.download(self.getDownloadUrl()) + +        check = self.checkDownload({"404": ">404 Not Found<", "Error": ">Error occured<"}) + +        if check == "404": +            self.retry(3, 60, 'Error 404') + +        elif check == "error": +            self.retry(5, 5 * 60, "Bitshare host : Error occured") +      def getDownloadUrl(self):          # Return location if direct download is active @@ -79,13 +91,16 @@ class BitshareCom(SimpleHoster):          # Get download info          self.logDebug("Getting download info") -        response = self.load("http://bitshare.com/files-ajax/" + self.file_id + "/request.html", -                             post={"request": "generateID", "ajaxid": self.ajaxid}) -        self.handleErrors(response, ':') -        parts = response.split(":") +        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]) +        wait     = int(parts[1]) +        captcha  = int(parts[2]) +          self.logDebug("Download info [type: '%s', waiting: %d, captcha: %d]" % (filetype, wait, captcha))          # Waiting @@ -100,43 +115,48 @@ class BitshareCom(SimpleHoster):          # Resolve captcha          if captcha == 1:              self.logDebug("File is captcha protected") -            id = re.search(self.CAPTCHA_KEY_PATTERN, self.html).group(1) +            recaptcha = ReCaptcha(self) +              # Try up to 3 times              for i in xrange(3): -                self.logDebug("Resolving ReCaptcha with key [%s], round %d" % (id, i + 1)) -                recaptcha = ReCaptcha(self) -                challenge, code = recaptcha.challenge(id) -                response = 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": code}) -                if self.handleCaptchaErrors(response): +                challenge, response = 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") -        response = self.load("http://bitshare.com/files-ajax/" + self.file_id + "/request.html", -                             post={"request": "getDownloadURL", "ajaxid": self.ajaxid}) -        self.handleErrors(response, '#') -        url = response.split("#")[-1] +        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, response, separator): -        self.logDebug("Checking response [%s]" % response) -        if "ERROR:Session timed out" in response: + +    def handleErrors(self, res, separator): +        self.logDebug("Checking response [%s]" % res) +        if "ERROR:Session timed out" in res:              self.retry() -        elif "ERROR" in response: -            msg = response.split(separator)[-1] +        elif "ERROR" in res: +            msg = res.split(separator)[-1]              self.fail(msg) -    def handleCaptchaErrors(self, response): -        self.logDebug("Result of captcha resolving [%s]" % response) -        if "SUCCESS" in response: + +    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 response: +        elif "ERROR:SESSION ERROR" in res:              self.retry() -        self.logDebug("Wrong captcha") +          self.invalidCaptcha() diff --git a/module/plugins/hoster/BoltsharingCom.py b/module/plugins/hoster/BoltsharingCom.py index a1672dc22..924545a29 100644 --- a/module/plugins/hoster/BoltsharingCom.py +++ b/module/plugins/hoster/BoltsharingCom.py @@ -4,13 +4,15 @@ from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class BoltsharingCom(DeadHoster): -    __name__ = "BoltsharingCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?boltsharing.com/\w{12}' +    __name__    = "BoltsharingCom" +    __type__    = "hoster"      __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?boltsharing\.com/\w{12}' +      __description__ = """Boltsharing.com hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")]  getInfo = create_getInfo(BoltsharingCom) diff --git a/module/plugins/hoster/CatShareNet.py b/module/plugins/hoster/CatShareNet.py index efaf8e5f7..949a021dd 100644 --- a/module/plugins/hoster/CatShareNet.py +++ b/module/plugins/hoster/CatShareNet.py @@ -1,38 +1,67 @@  # -*- coding: utf-8 -*-  import re +  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  from module.plugins.internal.CaptchaService import ReCaptcha  class CatShareNet(SimpleHoster): -    __name__ = "CatShareNet" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?catshare.net/\w{16}.*' -    __version__ = "0.01" +    __name__    = "CatShareNet" +    __type__    = "hoster" +    __version__ = "0.08" + +    __pattern__ = r'http://(?:www\.)?catshare\.net/\w{16}' +      __description__ = """CatShare.net hoster plugin""" -    __author_name__ = "z00nx" -    __author_mail__ = "z00nx0@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("z00nx", "z00nx0@gmail.com"), +                       ("prOq", None), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    TEXT_ENCODING = True + +    INFO_PATTERN = r'<title>(?P<N>.+) \((?P<S>[\d.,]+) (?P<U>[\w^_]+)\)<' +    OFFLINE_PATTERN = ur'Podany plik zostaÅ usuniÄty\s*</div>' + +    IP_BLOCKED_PATTERN = ur'>Nasz serwis wykryÅ ÅŒe Twój adres IP nie pochodzi z Polski.<' +    SECONDS_PATTERN = 'var\scount\s=\s(\d+);' +    LINK_PATTERN = r'<form action="(.+?)" method="GET">' + + +    def setup(self): +        self.multiDL        = self.premium +        self.resumeDownload = True + + +    def getFileInfo(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).getFileInfo() -    FILE_INFO_PATTERN = r'<h3 class="pull-left"[^>]+>(?P<N>.*)</h3>\s+<h3 class="pull-right"[^>]+>(?P<S>.*)</h3>' -    FILE_OFFLINE_PATTERN = r'Podany plik zosta' -    SECONDS_PATTERN = 'var\s+count\s+=\s+(\d+);' -    RECAPTCHA_KEY = "6Lfln9kSAAAAANZ9JtHSOgxUPB9qfDFeLUI_QMEy"      def handleFree(self): -        found = re.search(self.SECONDS_PATTERN, self.html) -        seconds = int(found.group(1)) -        self.logDebug("Seconds found", seconds) -        self.wait(seconds + 1) +        m = re.search(self.SECONDS_PATTERN, self.html) +        if m: +            wait_time = int(m.group(1)) +            self.wait(wait_time, True) +          recaptcha = ReCaptcha(self) -        challenge, code = recaptcha.challenge(self.RECAPTCHA_KEY) -        post_data = {"recaptcha_challenge_field": challenge, "recaptcha_response_field": code} -        self.download(self.pyfile.url, post=post_data) -        check = self.checkDownload({"html": re.compile("\A<!DOCTYPE html PUBLIC")}) -        if check == "html": -            self.logDebug("Wrong captcha entered") + +        challenge, response = recaptcha.challenge() +        self.html = self.load(self.pyfile.url, +                              post={'recaptcha_challenge_field': challenge, +                                    'recaptcha_response_field' : response}) + +        m = re.search(self.LINK_PATTERN, self.html) +        if m is None:              self.invalidCaptcha() -            self.retry() +            self.retry(reason=_("Wrong captcha entered")) + +        dl_link = m.group(1) +        self.download(dl_link, disposition=True)  getInfo = create_getInfo(CatShareNet) diff --git a/module/plugins/hoster/CloudzerNet.py b/module/plugins/hoster/CloudzerNet.py index 6e47ce53f..c5440391f 100644 --- a/module/plugins/hoster/CloudzerNet.py +++ b/module/plugins/hoster/CloudzerNet.py @@ -4,13 +4,17 @@ from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class CloudzerNet(DeadHoster): -    __name__ = "CloudzerNet" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?(cloudzer\.net/file/|clz\.to/(file/)?)\w+' +    __name__    = "CloudzerNet" +    __type__    = "hoster"      __version__ = "0.05" + +    __pattern__ = r'https?://(?:www\.)?(cloudzer\.net/file/|clz\.to/(file/)?)\w+' +      __description__ = """Cloudzer.net hoster plugin""" -    __author_name__ = ("gs", "z00nx", "stickell") -    __author_mail__ = ("I-_-I-_-I@web.de", "z00nx0@gmail.com", "l.stickell@yahoo.it") +    __license__     = "GPLv3" +    __authors__     = [("gs", "I-_-I-_-I@web.de"), +                       ("z00nx", "z00nx0@gmail.com"), +                       ("stickell", "l.stickell@yahoo.it")]  getInfo = create_getInfo(CloudzerNet) diff --git a/module/plugins/hoster/CloudzillaTo.py b/module/plugins/hoster/CloudzillaTo.py new file mode 100644 index 000000000..d8b5e8b5d --- /dev/null +++ b/module/plugins/hoster/CloudzillaTo.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +import re + +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo + + +class CloudzillaTo(SimpleHoster): +    __name__    = "CloudzillaTo" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?cloudzilla\.to/share/file/(?P<ID>[\w^_]+)' + +    __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): +        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(int(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): +        return self.handleFree() + + +getInfo = create_getInfo(CloudzillaTo) diff --git a/module/plugins/hoster/CramitIn.py b/module/plugins/hoster/CramitIn.py index e882f5cea..f444718bc 100644 --- a/module/plugins/hoster/CramitIn.py +++ b/module/plugins/hoster/CramitIn.py @@ -1,24 +1,25 @@  # -*- coding: utf-8 -*- -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo -class CramitIn(XFileSharingPro): -    __name__ = "CramitIn" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?cramit.in/\w{12}' -    __version__ = "0.04" +class CramitIn(XFSHoster): +    __name__    = "CramitIn" +    __type__    = "hoster" +    __version__ = "0.07" + +    __pattern__ = r'http://(?:www\.)?cramit\.in/\w{12}' +      __description__ = """Cramit.in hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    HOSTER_NAME = "cramit.in" +    HOSTER_DOMAIN = "cramit.in" -    FILE_INFO_PATTERN = r'<span class=t2>\s*(?P<N>.*?)</span>.*?<small>\s*\((?P<S>.*?)\)' -    DIRECT_LINK_PATTERN = r'href="(http://cramit.in/file_download/.*?)"' +    INFO_PATTERN = r'<span class=t2>\s*(?P<N>.*?)</span>.*?<small>\s*\((?P<S>.*?)\)' -    def setup(self): -        self.resumeDownload = self.multiDL = self.premium +    LINK_PATTERN = r'href="(http://cramit\.in/file_download/.*?)"'  getInfo = create_getInfo(CramitIn) diff --git a/module/plugins/hoster/CrockoCom.py b/module/plugins/hoster/CrockoCom.py index 3cd2be59d..e5f94800b 100644 --- a/module/plugins/hoster/CrockoCom.py +++ b/module/plugins/hoster/CrockoCom.py @@ -2,63 +2,61 @@  import re -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class CrockoCom(SimpleHoster): -    __name__ = "CrockoCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?(crocko|easy-share).com/\w+' -    __version__ = "0.16" +    __name__    = "CrockoCom" +    __type__    = "hoster" +    __version__ = "0.17" + +    __pattern__ = r'http://(?:www\.)?(crocko|easy-share)\.com/\w+' +      __description__ = """Crocko hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] -    FILE_NAME_PATTERN = r'<span class="fz24">Download:\s*<strong>(?P<N>.*)' -    FILE_SIZE_PATTERN = r'<span class="tip1"><span class="inner">(?P<S>[^<]+)</span></span>' -    FILE_OFFLINE_PATTERN = r"<h1>Sorry,<br />the page you're looking for <br />isn't here.</h1>|File not found" -    DOWNLOAD_URL_PATTERN = r"window.location ='([^']+)';" -    CAPTCHA_URL_PATTERN = re.compile(r"u='(/file_contents/captcha/\w+)';\s*w='(\d+)';") -    CAPTCHA_KEY_PATTERN = re.compile(r'Recaptcha.create\("([^"]+)"') + +    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 = re.compile(r"u='(/file_contents/captcha/\w+)';\s*w='(\d+)';")      FORM_PATTERN = r'<form  method="post" action="([^"]+)">(.*?)</form>'      FORM_INPUT_PATTERN = r'<input[^>]* name="?([^" ]+)"? value="?([^" ]+)"?[^>]*>' -    FILE_NAME_REPLACEMENTS = [(r'<[^>]*>', '')] +    NAME_REPLACEMENTS = [(r'<[^>]*>', '')] +      def handleFree(self):          if "You need Premium membership to download this file." in self.html: -            self.fail("You need Premium membership to download this file.") +            self.fail(_("You need Premium membership to download this file")) -        for _ in xrange(5): -            found = re.search(self.CAPTCHA_URL_PATTERN, self.html) -            if found: -                url, wait_time = 'http://crocko.com' + found.group(1), found.group(2) +        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 -        found = re.search(self.CAPTCHA_KEY_PATTERN, self.html) -        if not found: -            self.parseError('Captcha KEY') -        captcha_key = found.group(1) +        m = re.search(self.FORM_PATTERN, self.html, re.S) +        if m is None: +            self.error(_("FORM_PATTERN not found")) -        found = re.search(self.FORM_PATTERN, self.html, re.DOTALL) -        if not found: -            self.parseError('ACTION') -        action, form = found.groups() +        action, form = m.groups()          inputs = dict(re.findall(self.FORM_INPUT_PATTERN, form)) -          recaptcha = ReCaptcha(self) -        for _ in xrange(5): -            inputs['recaptcha_challenge_field'], inputs['recaptcha_response_field'] = recaptcha.challenge(captcha_key) +        for _i in xrange(5): +            inputs['recaptcha_challenge_field'], inputs['recaptcha_response_field'] = recaptcha.challenge()              self.download(action, post=inputs)              check = self.checkDownload({ -                "captcha_err": self.CAPTCHA_KEY_PATTERN +                "captcha_err": recaptcha.KEY_AJAX_PATTERN              })              if check == "captcha_err": @@ -66,7 +64,7 @@ class CrockoCom(SimpleHoster):              else:                  break          else: -            self.fail('No valid captcha solution received') +            self.fail(_("No valid captcha solution received"))  getInfo = create_getInfo(CrockoCom) diff --git a/module/plugins/hoster/CyberlockerCh.py b/module/plugins/hoster/CyberlockerCh.py index a08bf8518..b26909096 100644 --- a/module/plugins/hoster/CyberlockerCh.py +++ b/module/plugins/hoster/CyberlockerCh.py @@ -4,13 +4,15 @@ from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class CyberlockerCh(DeadHoster): -    __name__ = "CyberlockerCh" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?cyberlocker\.ch/\w+' +    __name__    = "CyberlockerCh" +    __type__    = "hoster"      __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?cyberlocker\.ch/\w+' +      __description__ = """Cyberlocker.ch hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")]  getInfo = create_getInfo(CyberlockerCh) diff --git a/module/plugins/hoster/CzshareCom.py b/module/plugins/hoster/CzshareCom.py index a462deaff..aa381d712 100644 --- a/module/plugins/hoster/CzshareCom.py +++ b/module/plugins/hoster/CzshareCom.py @@ -1,97 +1,88 @@  # -*- coding: utf-8 -*- - -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" - -# Test links (random.bin): +# +# Test links:  # http://czshare.com/5278880/random.bin  import re -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, PluginParseError + +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  from module.utils import parseFileSize  class CzshareCom(SimpleHoster): -    __name__ = "CzshareCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?(czshare|sdilej)\.(com|cz)/(\d+/|download.php\?).*' -    __version__ = "0.94" +    __name__    = "CzshareCom" +    __type__    = "hoster" +    __version__ = "0.96" + +    __pattern__ = r'http://(?:www\.)?(czshare|sdilej)\.(com|cz)/(\d+/|download\.php\?).+' +      __description__ = """CZshare.com hoster plugin, now Sdilej.cz""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] -    FILE_NAME_PATTERN = r'<div class="tab" id="parameters">\s*<p>\s*Cel. n.zev: <a href=[^>]*>(?P<N>[^<]+)</a>' -    FILE_SIZE_PATTERN = r'<div class="tab" id="category">(?:\s*<p>[^\n]*</p>)*\s*Velikost:\s*(?P<S>[0-9., ]+)(?P<U>[kKMG])i?B\s*</div>' -    FILE_OFFLINE_PATTERN = r'<div class="header clearfix">\s*<h2 class="red">' -    FILE_SIZE_REPLACEMENTS = [(' ', '')] -    FILE_URL_REPLACEMENTS = [(r'http://[^/]*/download.php\?.*?id=(\w+).*', r'http://sdilej.cz/\1/x/')] -    SH_CHECK_TRAFFIC = True +    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 -->' -    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>([0-9., ]+)([kKMG]i?B)</strong>\s*</div><!-- .credit -->'      def checkTrafficLeft(self):          # check if user logged in -        found = re.search(self.USER_CREDIT_PATTERN, self.html) -        if not found: +        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, cookies=True, decode=True) -            found = re.search(self.USER_CREDIT_PATTERN, self.html) -            if not found: +            m = re.search(self.USER_CREDIT_PATTERN, self.html) +            if m is None:                  return False          # check user credit          try: -            credit = parseFileSize(found.group(1).replace(' ', ''), found.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)) +            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) +                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('Parse error (CREDIT): %s' % e) +            self.logError(e)          return True +      def handlePremium(self):      # parse download link          try: -            form = re.search(self.PREMIUM_FORM_PATTERN, self.html, re.DOTALL).group(1) +            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("Parse error (FORM): %s" % 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)          self.checkDownloadedFile() +      def handleFree(self):          # get free url -        found = re.search(self.FREE_URL_PATTERN, self.html) -        if found is None: -            raise PluginParseError('Free URL') -        parsed_url = "http://sdilej.cz" + found.group(1) +        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 @@ -100,16 +91,16 @@ class CzshareCom(SimpleHoster):              self.longWait(5 * 60, 12)          try: -            form = re.search(self.FREE_FORM_PATTERN, self.html, re.DOTALL).group(1) +            form = re.search(self.FREE_FORM_PATTERN, self.html, re.S).group(1)              inputs = dict(re.findall(self.FORM_INPUT_PATTERN, form))              self.pyfile.size = int(inputs['size'])          except Exception, e:              self.logError(e) -            raise PluginParseError('Form') +            self.error(_("Form")) -        # get and decrypt captcha         +        # get and decrypt captcha          captcha_url = 'http://sdilej.cz/captcha.php' -        for _ in xrange(5): +        for _i in xrange(5):              inputs['captchastring2'] = self.decryptCaptcha(captcha_url)              self.html = self.load(parsed_url, cookies=True, post=inputs, decode=True)              if u"<li>ZadanÜ ovÄÅovacà kód nesouhlasÃ!</li>" in self.html: @@ -120,34 +111,35 @@ class CzshareCom(SimpleHoster):                  self.correctCaptcha()                  break          else: -            self.fail("No valid captcha code entered") +            self.fail(_("No valid captcha code entered")) -        found = re.search("countdown_number = (\d+);", self.html) -        self.setWait(int(found.group(1)) if found else 50) +        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) -        found = re.search("free_wait.php\?server=(.*?)&(.*)", self.req.lastEffectiveURL) -        if not found: -            raise PluginParseError('Download URL') +        m = re.search("free_wait.php\?server=(.*?)&(.*)", self.req.lastEffectiveURL) +        if m is None: +            self.error(_("Download URL not found")) -        url = "http://%s/download.php?%s" % (found.group(1), found.group(2)) +        url = "http://%s/download.php?%s" % (m.group(1), m.group(2))          self.wait()          self.download(url)          self.checkDownloadedFile() +      def checkDownloadedFile(self):          # check download          check = self.checkDownload({ -            "tempoffline": re.compile(r"^Soubor je do.*asn.* nedostupn.*$"), +            "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_err": "<li>ZadanÜ ovÄÅovacà kód nesouhlasÃ!</li>"          }) -        if check == "tempoffline": -            self.fail("File not available - try later") +        if check == "temp_offline": +            self.fail(_("File not available - try later"))          if check == "credit":              self.resetAccount()          elif check == "multi_dl": diff --git a/module/plugins/hoster/DailymotionCom.py b/module/plugins/hoster/DailymotionCom.py index 17e1ecf92..dc42d1f60 100644 --- a/module/plugins/hoster/DailymotionCom.py +++ b/module/plugins/hoster/DailymotionCom.py @@ -1,49 +1,30 @@  # -*- coding: utf-8 -*- -############################################################################ -#  This program is free software: you can redistribute it and/or modify -#  it under the terms of the GNU Affero General Public License as -#  published by the Free Software Foundation, either version 3 of the -#  License, or (at your option) any later version. -# -#  This program is distributed in the hope that it will be useful, -#  but WITHOUT ANY WARRANTY; without even the implied warranty of -#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -#  GNU Affero General Public License for more details. -# -#  You should have received a copy of the GNU Affero General Public License -#  along with this program.  If not, see <http://www.gnu.org/licenses/>. -# -#  @author: Walter Purcaro -############################################################################ -  import re +from module.PyFile import statusMap  from module.common.json_layer import json_loads  from module.network.RequestFactory import getURL  from module.plugins.Hoster import Hoster -from module.PyFile import statusMap  def getInfo(urls): -    result = []  #: [ .. (name, size, status, url) .. ] -    regex = re.compile(DailymotionCom.__pattern__) -    apiurl = "https://api.dailymotion.com/video/" +    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.search(url).group("ID") -        page = getURL(apiurl + id, get=request) +        id   = regex.match(url).group('ID') +        page = getURL(apiurl % id, get=request)          info = json_loads(page) -        if "title" in info: -            name = info["title"] + ".mp4" -        else: -            name = url +        name = info['title'] + ".mp4" if "title" in info else url -        if "error" in info or info["access_error"]: +        if "error" in info or info['access_error']:              status = "offline"          else: -            status = info["status"] +            status = info['status']              if status in ("ready", "published"):                  status = "online"              elif status in ("waiting", "processing"): @@ -52,43 +33,57 @@ def getInfo(urls):                  status = "offline"          result.append((name, 0, statusMap[status], url)) +      return result  class DailymotionCom(Hoster): -    __name__ = "DailymotionCom" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?dailymotion\.com/.*?video/(?P<ID>[\w^_]+)' -    __version__ = "0.2" -    __config__ = [("quality", "Lowest;LD 144p;LD 240p;SD 384p;HQ 480p;HD 720p;HD 1080p;Highest", "Quality", "HD 720p")] +    __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""" -    __author_name__ = "Walter Purcaro" -    __author_mail__ = "vuolter@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] +      def setup(self): -        self.resumeDownload = self.multiDL = True +        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("\\", "") +            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 reversed([item for item in enumerate(streams)]): @@ -102,24 +97,29 @@ class DailymotionCom(Hoster):              idx = quality          s = streams[idx] -        self.logInfo("Download video quality %sx%s" % s[0]) + +        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") +        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() -        link = self.getLink(streams, quality) -        self.download(link) +        self.download(self.getLink(streams, quality)) diff --git a/module/plugins/hoster/DataHu.py b/module/plugins/hoster/DataHu.py index 0c872b419..437fea7cd 100644 --- a/module/plugins/hoster/DataHu.py +++ b/module/plugins/hoster/DataHu.py @@ -1,20 +1,6 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -# Test links (random.bin): +# +# Test links:  # http://data.hu/get/6381232/random.bin  import re @@ -23,30 +9,34 @@ from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class DataHu(SimpleHoster): -    __name__ = "DataHu" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?data.hu/get/\w+' -    __version__ = "0.01" +    __name__    = "DataHu" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?data\.hu/get/\w+' +      __description__ = """Data.hu hoster plugin""" -    __author_name__ = ("crash", "stickell") -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("crash", None), +                       ("stickell", "l.stickell@yahoo.it")] -    FILE_INFO_PATTERN = ur'<title>(?P<N>.*) \((?P<S>[^)]+)\) let\xf6lt\xe9se</title>' -    FILE_OFFLINE_PATTERN = ur'Az adott f\xe1jl nem l\xe9tezik' -    DIRECT_LINK_PATTERN = r'<div class="download_box_button"><a href="([^"]+)">' -    def handleFree(self): +    INFO_PATTERN = ur'<title>(?P<N>.*) \((?P<S>[^)]+)\) let\xf6lt\xe9se</title>' +    OFFLINE_PATTERN = ur'Az adott f\xe1jl nem l\xe9tezik' +    LINK_PATTERN = r'<div class="download_box_button"><a href="([^"]+)">' + + +    def setup(self):          self.resumeDownload = True -        self.html = self.load(self.pyfile.url, decode=True) +        self.multiDL        = self.premium + -        m = re.search(self.DIRECT_LINK_PATTERN, self.html) -        if m: -            url = m.group(1) -            self.logDebug('Direct link: ' + url) -        else: -            self.parseError('Unable to get direct link') +    def handleFree(self): +        m = re.search(self.LINK_PATTERN, self.html) +        if m is None: +            self.error(_("LINK_PATTERN not found")) -        self.download(url, disposition=True) +        self.download(m.group(1), disposition=True)  getInfo = create_getInfo(DataHu) diff --git a/module/plugins/hoster/DataportCz.py b/module/plugins/hoster/DataportCz.py index 291dcaf55..8e74f5553 100644 --- a/module/plugins/hoster/DataportCz.py +++ b/module/plugins/hoster/DataportCz.py @@ -1,62 +1,48 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" - -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, PluginParseError +class DataportCz(SimpleHoster): +    __name__    = "DataportCz" +    __type__    = "hoster" +    __version__ = "0.40" +    __pattern__ = r'http://(?:www\.)?dataport\.cz/file/(.+)' -class DataportCz(SimpleHoster): -    __name__ = "DataportCz" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?dataport.cz/file/(.*)' -    __version__ = "0.37"      __description__ = """Dataport.cz hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    FILE_NAME_PATTERN = r'<span itemprop="name">(?P<N>[^<]+)</span>' -    FILE_SIZE_PATTERN = r'<td class="fil">Velikost</td>\s*<td>(?P<S>[^<]+)</td>' -    FILE_OFFLINE_PATTERN = r'<h2>Soubor nebyl nalezen</h2>' -    FILE_URL_REPLACEMENTS = [(__pattern__, r'http://www.dataport.cz/file/\1')] +    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_URL_PATTERN = r'<section id="captcha_bg">\s*<img src="(.*?)"' +    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):          captchas = {"1": "jkeG", "2": "hMJQ", "3": "vmEK", "4": "ePQM", "5": "blBd"} -        for _ in xrange(60): +        for _i in xrange(60):              action, inputs = self.parseHtmlForm('free_download_form')              self.logDebug(action, inputs)              if not action or not inputs: -                raise PluginParseError('free_download_form') +                self.error(_("free_download_form")) -            if "captchaId" in inputs and inputs["captchaId"] in captchas: -                inputs['captchaCode'] = captchas[inputs["captchaId"]] +            if "captchaId" in inputs and inputs['captchaId'] in captchas: +                inputs['captchaCode'] = captchas[inputs['captchaId']]              else: -                raise PluginParseError('captcha') +                self.error(_("captcha"))              self.html = 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": -                raise PluginParseError('invalid captcha') +                self.error(_("invalid captcha"))              elif check == "slot":                  self.logDebug("No free slots - wait 60s and retry")                  self.wait(60, False) @@ -66,4 +52,4 @@ class DataportCz(SimpleHoster):                  break -create_getInfo(DataportCz) +getInfo = create_getInfo(DataportCz) diff --git a/module/plugins/hoster/DateiTo.py b/module/plugins/hoster/DateiTo.py index 223138592..e4bff8458 100644 --- a/module/plugins/hoster/DateiTo.py +++ b/module/plugins/hoster/DateiTo.py @@ -1,52 +1,39 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo +  from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class DateiTo(SimpleHoster): -    __name__ = "DateiTo" -    __type__ = "hoster" +    __name__    = "DateiTo" +    __type__    = "hoster" +    __version__ = "0.05" +      __pattern__ = r'http://(?:www\.)?datei\.to/datei/(?P<ID>\w+)\.html' -    __version__ = "0.02" +      __description__ = """Datei.to hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    FILE_NAME_PATTERN = r'Dateiname:</td>\s*<td colspan="2"><strong>(?P<N>.*?)</' -    FILE_SIZE_PATTERN = r'Dateigröße:</td>\s*<td colspan="2">(?P<S>.*?)</' -    FILE_OFFLINE_PATTERN = r'>Datei wurde nicht gefunden<|>Bitte wÀhle deine Datei aus... <' -    PARALELL_PATTERN = r'>Du lÀdst bereits eine Datei herunter<' +    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<' -    WAIT_PATTERN = r'countdown\({seconds: (\d+)'      DATA_PATTERN = r'url: "(.*?)", data: "(.*?)",' -    RECAPTCHA_KEY_PATTERN = r'Recaptcha.create\("(.*?)"' +      def handleFree(self):          url = 'http://datei.to/ajax/download.php' -        data = {'P': 'I', 'ID': self.file_info['ID']} - +        data = {'P': 'I', 'ID': self.info['pattern']['ID']}          recaptcha = ReCaptcha(self) -        for _ in xrange(10): +        for _i in xrange(10):              self.logDebug("URL", url, "POST", data)              self.html = self.load(url, post=data)              self.checkErrors() @@ -58,39 +45,38 @@ class DateiTo(SimpleHoster):                  elif data['P'] == 'IV':                      break -            found = re.search(self.DATA_PATTERN, self.html) -            if not found: -                self.parseError('data') -            url = 'http://datei.to/' + found.group(1) -            data = dict(x.split('=') for x in found.group(2).split('&')) +            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'): -                found = re.search(self.RECAPTCHA_KEY_PATTERN, self.html) -                recaptcha_key = found.group(1) if found else "6LdBbL8SAAAAAI0vKUo58XRwDd5Tu_Ze1DA7qTao" - -                data['recaptcha_challenge_field'], data['recaptcha_response_field'] = recaptcha.challenge(recaptcha_key) - +                data['recaptcha_challenge_field'], data['recaptcha_response_field'] = recaptcha.challenge()          else: -            self.fail('Too bad...') +            self.fail(_("Too bad...")) + +        self.download(self.html) -        download_url = self.html -        self.logDebug('Download URL', download_url) -        self.download(download_url)      def checkErrors(self): -        found = re.search(self.PARALELL_PATTERN, self.html) -        if found: -            found = re.search(self.WAIT_PATTERN, self.html) -            wait_time = int(found.group(1)) if found else 30 -            self.wait(wait_time + 1, False) -            self.retry() +        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): -        found = re.search(self.WAIT_PATTERN, self.html) -        wait_time = int(found.group(1)) if found else 30 +        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 + 1, False) +        self.wait(wait_time, False)  getInfo = create_getInfo(DateiTo) diff --git a/module/plugins/hoster/DdlstorageCom.py b/module/plugins/hoster/DdlstorageCom.py index eed53b1e1..a45ef27e9 100644 --- a/module/plugins/hoster/DdlstorageCom.py +++ b/module/plugins/hoster/DdlstorageCom.py @@ -1,89 +1,19 @@  # -*- coding: utf-8 -*- -import re -from hashlib import md5 +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo -from module.plugins.hoster.XFileSharingPro import XFileSharingPro -from module.network.RequestFactory import getURL -from module.plugins.Plugin import chunks -from module.common.json_layer import json_loads +class DdlstorageCom(DeadHoster): +    __name__    = "DdlstorageCom" +    __type__    = "hoster" +    __version__ = "1.02" -def getInfo(urls): -    # DDLStorage API Documentation: -    # http://www.ddlstorage.com/cgi-bin/api_req.cgi?req_type=doc -    ids = dict() -    for url in urls: -        m = re.search(DdlstorageCom.__pattern__, url) -        ids[m.group('ID')] = url +    __pattern__ = r'https?://(?:www\.)?ddlstorage\.com/\w+' -    for chunk in chunks(ids.keys(), 5): -        for _ in xrange(5): -            api = getURL('http://www.ddlstorage.com/cgi-bin/api_req.cgi', -                         post={'req_type': 'file_info_free', -                               'client_id': 53472, -                               'file_code': ','.join(chunk), -                               'sign': md5('file_info_free%d%s%s' % (53472, ','.join(chunk), -                                                                     '25JcpU2dPOKg8E2OEoRqMSRu068r0Cv3')).hexdigest()}) -            api = api.replace('<pre>', '').replace('</pre>', '') -            api = json_loads(api) -            if 'error' not in api: -                break - -        result = list() -        for el in api: -            if el['status'] == 'online': -                result.append((el['file_name'], int(el['file_size']), 2, ids[el['file_code']])) -            else: -                result.append((ids[el['file_code']], 0, 1, ids[el['file_code']])) -        yield result - - -class DdlstorageCom(XFileSharingPro): -    __name__ = "DdlstorageCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?ddlstorage.com/(?P<ID>\w{12})' -    __version__ = "1.01"      __description__ = """DDLStorage.com hoster plugin""" -    __author_name__ = ("zoidberg", "stickell") -    __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it") - -    HOSTER_NAME = "ddlstorage.com" - -    FILE_INFO_PATTERN = r'<p class="sub_title"[^>]*>(?P<N>.+) \((?P<S>[^)]+)\)</p>' - -    def prepare(self): -        self.getAPIData() -        super(DdlstorageCom, self).prepare() - -    def getAPIData(self): -        file_id = re.match(self.__pattern__, self.pyfile.url).group('ID') -        data = {'client_id': 53472, -                'file_code': file_id} -        if self.user: -            passwd = self.account.getAccountData(self.user)["password"] -            data['req_type'] = 'file_info_reg' -            data['user_login'] = self.user -            data['user_password'] = md5(passwd).hexdigest() -            data['sign'] = md5('file_info_reg%d%s%s%s%s' % (data['client_id'], data['user_login'], -                                                            data['user_password'], data['file_code'], -                                                            '25JcpU2dPOKg8E2OEoRqMSRu068r0Cv3')).hexdigest() -        else: -            data['req_type'] = 'file_info_free' -            data['sign'] = md5('file_info_free%d%s%s' % (data['client_id'], data['file_code'], -                                                         '25JcpU2dPOKg8E2OEoRqMSRu068r0Cv3')).hexdigest() - -        self.api_data = self.load('http://www.ddlstorage.com/cgi-bin/api_req.cgi', post=data) -        self.api_data = self.api_data.replace('<pre>', '').replace('</pre>', '') -        self.logDebug('API Data: ' + self.api_data) -        self.api_data = json_loads(self.api_data)[0] +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] -        if self.api_data['status'] == 'offline': -            self.offline() -        if 'file_name' in self.api_data: -            self.pyfile.name = self.api_data['file_name'] -        if 'file_size' in self.api_data: -            self.pyfile.size = self.api_data['size'] = self.api_data['file_size'] -        if 'file_md5_base64' in self.api_data: -            self.api_data['md5_ddlstorage'] = self.api_data['file_md5_base64'] +getInfo = create_getInfo(DdlstorageCom) diff --git a/module/plugins/hoster/DebridItaliaCom.py b/module/plugins/hoster/DebridItaliaCom.py index a8b4248b4..842e3cd63 100644 --- a/module/plugins/hoster/DebridItaliaCom.py +++ b/module/plugins/hoster/DebridItaliaCom.py @@ -1,60 +1,48 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################  import re -from module.plugins.Hoster import Hoster +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo -class DebridItaliaCom(Hoster): -    __name__ = "DebridItaliaCom" -    __version__ = "0.05" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:[^/]*\.)?debriditalia\.com' +class DebridItaliaCom(MultiHoster): +    __name__    = "DebridItaliaCom" +    __type__    = "hoster" +    __version__ = "0.16" + +    __pattern__ = r'https?://(?:www\.|s\d+\.)?debriditalia\.com/dl/\d+' +      __description__ = """Debriditalia.com hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    URL_REPLACEMENTS = [("https://", "http://")] +      def setup(self): -        self.chunkLimit = -1 +        self.chunkLimit     = 1          self.resumeDownload = True -    def process(self, pyfile): -        if re.match(self.__pattern__, pyfile.url): -            new_url = pyfile.url -        elif not self.account: -            self.logError(_("Please enter your %s account or deactivate this plugin") % "DebridItalia") -            self.fail("No DebridItalia account provided") -        else: -            self.logDebug("Old URL: %s" % pyfile.url) -            url = "http://debriditalia.com/linkgen2.php?xjxfun=convertiLink&xjxargs[]=S<![CDATA[%s]]>" % pyfile.url -            page = self.load(url) -            self.logDebug("XML data: %s" % page) -            if 'File not available' in page: -                self.fail('File not available') -            else: -                new_url = re.search(r'<a href="(?:[^"]+)">(?P<direct>[^<]+)</a>', page).group('direct') +    def handlePremium(self): +        self.html = self.load("http://www.debriditalia.com/api.php", +                              get={'generate': "on", 'link': self.pyfile.url, 'p': self.getPassword()}) -        if new_url != pyfile.url: -            self.logDebug("New URL: %s" % new_url) +        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.download(new_url, disposition=True) +            self.html = self.load("http://debriditalia.com/linkgen2.php", +                                  post={'xjxfun'   : "convertiLink", +                                        'xjxargs[]': "S<![CDATA[%s]]>" % self.pyfile.url, +                                        'xjxargs[]': "S%s" % self.getPassword()}) +            try: +                self.link = re.search(r'<a href="(.+?)"', self.html).group(1) +            except AttributeError: +                pass -        check = self.checkDownload({"empty": re.compile(r"^$")}) -        if check == "empty": -            self.retry(5, 2 * 60, "Empty file downloaded") +getInfo = create_getInfo(DebridItaliaCom) diff --git a/module/plugins/hoster/DepositfilesCom.py b/module/plugins/hoster/DepositfilesCom.py index 11b4f4112..6588a3b37 100644 --- a/module/plugins/hoster/DepositfilesCom.py +++ b/module/plugins/hoster/DepositfilesCom.py @@ -1,111 +1,123 @@  # -*- coding: utf-8 -*-  import re +  from urllib import unquote -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo +  from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class DepositfilesCom(SimpleHoster): -    __name__ = "DepositfilesCom" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?(depositfiles\.com|dfiles\.(eu|ru))(/\w{1,3})?/files/[\w]+' -    __version__ = "0.46" +    __name__    = "DepositfilesCom" +    __type__    = "hoster" +    __version__ = "0.51" + +    __pattern__ = r'https?://(?:www\.)?(depositfiles\.com|dfiles\.(eu|ru))(/\w{1,3})?/files/(?P<ID>\w+)' +      __description__ = """Depositfiles.com hoster plugin""" -    __author_name__ = ("spoob", "zoidberg") -    __author_mail__ = ("spoob@pyload.org", "zoidberg@mujmail.cz") - -    FILE_SIZE_PATTERN = r': <b>(?P<S>[0-9.]+) (?P<U>[kKMG])i?B</b>' -    FILE_NAME_PATTERN = r'<script type="text/javascript">eval\( unescape\(\'(?P<N>.*?)\'' -    FILE_OFFLINE_PATTERN = r'<span class="html_download_api-not_exists"></span>' -    FILE_URL_REPLACEMENTS = [(r"\.com(/.*?)?/files", ".com/en/files"), (r"\.html$", "")] -    FILE_NAME_REPLACEMENTS = [(r'\%u([0-9A-Fa-f]{4})', lambda m: unichr(int(m.group(1), 16))), +    __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")] + +    FREE_LINK_PATTERN      = r'<form id="downloader_file_form" action="(http://.+?\.(dfiles\.eu|depositfiles\.com)/.+?)" method="post"' +    PREMIUM_LINK_PATTERN   = r'class="repeat"><a href="(.+?)"' +    PREMIUM_MIRROR_PATTERN = r'class="repeat_mirror"><a href="(.+?)"' -    RECAPTCHA_PATTERN = r"Recaptcha.create\('([^']+)'" -    DOWNLOAD_LINK_PATTERN = r'<form id="downloader_file_form" action="(http://.+?\.(dfiles\.eu|depositfiles\.com)/.+?)" method="post"'      def handleFree(self):          self.html = self.load(self.pyfile.url, post={"gateway_result": "1"}, cookies=True) -        if re.search(self.FILE_OFFLINE_PATTERN, self.html): -            self.offline()          if re.search(r'File is checked, please try again in a minute.', self.html) is not None: -            self.logInfo("DepositFiles.com: The file is being checked. Waiting 1 minute.") -            self.wait(61) -            self.retry() +            self.logInfo(_("The file is being checked. Waiting 1 minute")) +            self.retry(wait_time=60)          wait = re.search(r'html_download_api-limit_interval\">(\d+)</span>', self.html)          if wait:              wait_time = int(wait.group(1)) -            self.logInfo("%s: Traffic used up. Waiting %d seconds." % (self.__name__, wait_time)) +            self.logInfo(_("Traffic used up. Waiting %d seconds") % wait_time)              self.wait(wait_time, True)              self.retry()          wait = re.search(r'>Try in (\d+) minutes or use GOLD account', self.html)          if wait:              wait_time = int(wait.group(1)) -            self.logInfo("%s: All free slots occupied. Waiting %d minutes." % (self.__name__, wait_time)) +            self.logInfo(_("All free slots occupied. Waiting %d minutes") % wait_time)              self.setWait(wait_time * 60, False)          wait = re.search(r'Please wait (\d+) sec', self.html)          if wait:              self.setWait(int(wait.group(1))) -        found = re.search(r"var fid = '(\w+)';", self.html) -        if not found: +        m = re.search(r"var fid = '(\w+)';", self.html) +        if m is None:              self.retry(wait_time=5) -        params = {'fid': found.group(1)} +        params = {'fid': m.group(1)}          self.logDebug("FID: %s" % params['fid']) -        captcha_key = '6LdRTL8SAAAAAE9UOdWZ4d0Ky-aeA7XfSqyWDM2m' -        found = re.search(self.RECAPTCHA_PATTERN, self.html) -        if found: -            captcha_key = found.group(1) -        self.logDebug("CAPTCHA_KEY: %s" % captcha_key) -          self.wait()          recaptcha = ReCaptcha(self) +        captcha_key = recaptcha.detect_key() +        if captcha_key is None: +            self.error(_("ReCaptcha key not found")) -        for _ in xrange(5): -            self.html = self.load("http://depositfiles.com/get_file.php", get=params) +        for _i in xrange(5): +            self.html = self.load("https://dfiles.eu/get_file.php", get=params)              if '<input type=button value="Continue" onclick="check_recaptcha' in self.html: -                if not captcha_key: -                    self.parseError('Captcha key')                  if 'response' in params:                      self.invalidCaptcha()                  params['challenge'], params['response'] = recaptcha.challenge(captcha_key)                  self.logDebug(params)                  continue -            found = re.search(self.DOWNLOAD_LINK_PATTERN, self.html) -            if found: +            m = re.search(self.FREE_LINK_PATTERN, self.html) +            if m:                  if 'response' in params:                      self.correctCaptcha() -                link = unquote(found.group(1)) +                link = unquote(m.group(1))                  self.logDebug("LINK: %s" % link)                  break              else: -                self.parseError('Download link') +                self.error(_("Download link"))          else: -            self.fail('No valid captcha response received') +            self.fail(_("No valid captcha response received"))          try:              self.download(link, disposition=True)          except:              self.retry(wait_time=60) +      def handlePremium(self):          if '<span class="html_download_api-gold_traffic_limit">' in self.html: -            self.logWarning("Download limit reached") +            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() -        link = unquote( -            re.search('<div id="download_url">\s*<a href="(http://.+?\.depositfiles.com/.+?)"', self.html).group(1)) -        self.download(link, disposition=True) +        else: +            link = re.search(self.PREMIUM_LINK_PATTERN, self.html) +            mirror = re.search(self.PREMIUM_MIRROR_PATTERN, self.html) +            if link: +                dlink = link.group(1) +            elif mirror: +                dlink = mirror.group(1) +            else: +                self.error(_("No direct download link or mirror found")) +            self.download(dlink, disposition=True)  getInfo = create_getInfo(DepositfilesCom) diff --git a/module/plugins/hoster/DevhostSt.py b/module/plugins/hoster/DevhostSt.py new file mode 100644 index 000000000..85e36edb3 --- /dev/null +++ b/module/plugins/hoster/DevhostSt.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://d-h.st/mM8 + +import re + +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo + + +class DevhostSt(SimpleHoster): +    __name__    = "DevhostSt" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?d-h\.st/(?!users/)\w{3}' + +    __description__ = """d-h.st hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de")] + + +    NAME_PATTERN = r'>Filename:</span> <div title="(?P<N>.+?)"' +    SIZE_PATTERN = r'>Size:</span> (?P<S>[\d.,]+) (?P<U>[\w^_]+)' + +    OFFLINE_PATTERN = r'>File Not Found<' +    LINK_PATTERN = r'id="downloadfile" href="(.+?)"' + + +    def setup(self): +        self.multiDL = True +        self.chunkLimit = 1 + + +    def handleFree(self): +        m = re.search(self.LINK_PATTERN, self.html) +        if m is None: +            self.error(_("Download link not found")) + +        dl_url = m.group(1) +        self.download(dl_url, disposition=True) + +        check = self.checkDownload({'html': re.compile("html")}) +        if check == "html": +            self.error(_("Downloaded file is an html page")) + + +getInfo = create_getInfo(DevhostSt) diff --git a/module/plugins/hoster/DlFreeFr.py b/module/plugins/hoster/DlFreeFr.py index 998dcd606..793c81b1c 100644 --- a/module/plugins/hoster/DlFreeFr.py +++ b/module/plugins/hoster/DlFreeFr.py @@ -1,23 +1,26 @@  # -*- coding: utf-8 -*- -import re  import pycurl +import re -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, replace_patterns -from module.common.json_layer import json_loads  from module.network.Browser import Browser  from module.network.CookieJar import CookieJar +from module.plugins.internal.CaptchaService import AdYouLike +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, replace_patterns  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: -            if len(args) > 2: -                post = args[2] + +        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) @@ -26,114 +29,46 @@ class CustomBrowser(Browser):              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 AdYouLike(): -    """ -    Class to support adyoulike captcha service -    """ -    ADYOULIKE_INPUT_PATTERN = r'Adyoulike.create\((.*?)\);' -    ADYOULIKE_CALLBACK = r'Adyoulike.g._jsonp_5579316662423138' -    ADYOULIKE_CHALLENGE_PATTERN = ADYOULIKE_CALLBACK + r'\((.*?)\)' - -    def __init__(self, plugin, engine="adyoulike"): -        self.plugin = plugin -        self.engine = engine - -    def challenge(self, html): -        adyoulike_data_string = None -        found = re.search(self.ADYOULIKE_INPUT_PATTERN, html) -        if found: -            adyoulike_data_string = found.group(1) -        else: -            self.plugin.fail("Can't read AdYouLike input data") -        # {"adyoulike":{"key":"P~zQ~O0zV0WTiAzC-iw0navWQpCLoYEP"}, -        # "all":{"element_id":"ayl_private_cap_92300","lang":"fr","env":"prod"}} -        ayl_data = json_loads(adyoulike_data_string) +        return Browser.load(self, *args, **kwargs) -        res = self.plugin.load( -            r'http://api-ayl.appspot.com/challenge?key=%(ayl_key)s&env=%(ayl_env)s&callback=%(callback)s' % { -            "ayl_key": ayl_data[self.engine]["key"], "ayl_env": ayl_data["all"]["env"], -            "callback": self.ADYOULIKE_CALLBACK}) -        found = re.search(self.ADYOULIKE_CHALLENGE_PATTERN, res) -        challenge_string = None -        if found: -            challenge_string = found.group(1) -        else: -            self.plugin.fail("Invalid AdYouLike challenge") -        challenge_data = json_loads(challenge_string) - -        return ayl_data, challenge_data - -    def result(self, ayl, 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"}) -        """ -        response = None -        try: -            instructions_visual = challenge["translations"][ayl["all"]["lang"]]["instructions_visual"] -            found = re.search(u".*«(.*)».*", instructions_visual) -            if found: -                response = found.group(1).strip() -            else: -                self.plugin.fail("Can't parse instructions visual") -        except KeyError: -            self.plugin.fail("No instructions visual") +class DlFreeFr(SimpleHoster): +    __name__    = "DlFreeFr" +    __type__    = "hoster" +    __version__ = "0.26" -        #TODO: Supports captcha +    __pattern__ = r'http://(?:www\.)?dl\.free\.fr/(\w+|getfile\.pl\?file=/\w+)' -        if not response: -            self.plugin.fail("AdYouLike result failed") +    __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")] -        return {"_ayl_captcha_engine": self.engine, -                "_ayl_env": ayl["all"]["env"], -                "_ayl_tid": challenge["tid"], -                "_ayl_token_challenge": challenge["token"], -                "_ayl_response": response} +    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é' -class DlFreeFr(SimpleHoster): -    __name__ = "DlFreeFr" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?dl\.free\.fr/([a-zA-Z0-9]+|getfile\.pl\?file=/[a-zA-Z0-9]+)' -    __version__ = "0.25" -    __description__ = """Dl.free.fr hoster plugin""" -    __author_name__ = ("the-razer", "zoidberg", "Toilal") -    __author_mail__ = ("daniel_ AT gmx DOT net", "zoidberg@mujmail.cz", "toilal.dev@gmail.com") - -    FILE_NAME_PATTERN = r"Fichier:</td>\s*<td[^>]*>(?P<N>[^>]*)</td>" -    FILE_SIZE_PATTERN = r"Taille:</td>\s*<td[^>]*>(?P<S>[\d.]+[KMG])o" -    FILE_OFFLINE_PATTERN = r"Erreur 404 - Document non trouv|Fichier inexistant|Le fichier demandé n'a pas été trouvé" -    #FILE_URL_PATTERN = r'href="(?P<url>http://.*?)">Télécharger ce fichier'         def setup(self): -        self.multiDL = self.resumeDownload = True -        self.limitDL = 5 -        self.chunkLimit = 1 +        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): -        self.req.setCookieJar(None) -        pyfile.url = replace_patterns(pyfile.url, self.FILE_URL_REPLACEMENTS) +    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) -        self.html = None          if headers.get('code') == 302:              valid_url = headers.get('location')              headers = self.load(valid_url, just_header=True) @@ -145,35 +80,35 @@ class DlFreeFr(SimpleHoster):                  self.html = self.load(valid_url)                  self.handleFree()              else: -                # Direct access to requested file for users using free.fr as Internet Service Provider.  +                # Direct access to requested file for users using free.fr as Internet Service Provider.                  self.download(valid_url, disposition=True)          elif headers.get('code') == 404:              self.offline()          else: -            self.fail("Invalid return code: " + str(headers.get('code'))) +            self.fail(_("Invalid return code: ") + str(headers.get('code'))) +      def handleFree(self):          action, inputs = self.parseHtmlForm('action="getfile.pl"')          adyoulike = AdYouLike(self) -        ayl, challenge = adyoulike.challenge(self.html) -        result = adyoulike.result(ayl, challenge) -        inputs.update(result) +        inputs.update(adyoulike.challenge())          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: -            found = re.search("(.*?)=(.*?); path=(.*?); domain=(.*?)", headers.get("set-cookie")) +            m = re.search("(.*?)=(.*?); path=(.*?); domain=(.*?)", headers.get("set-cookie"))              cj = CookieJar(__name__) -            if found: -                cj.setCookie(found.group(4), found.group(1), found.group(2), found.group(3)) +            if m: +                cj.setCookie(m.group(4), m.group(1), m.group(2), m.group(3))              else: -                self.fail("Cookie error") +                self.fail(_("Cookie error"))              location = headers.get("location")              self.req.setCookieJar(cj)              self.download(location, disposition=True)          else: -            self.fail("Invalid response") +            self.fail(_("Invalid response")) +      def getLastHeaders(self):          #parse header diff --git a/module/plugins/hoster/DodanePl.py b/module/plugins/hoster/DodanePl.py new file mode 100644 index 000000000..65d8452fa --- /dev/null +++ b/module/plugins/hoster/DodanePl.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class DodanePl(DeadHoster): +    __name__    = "DodanePl" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?dodane\.pl/file/\d+' + +    __description__ = """Dodane.pl hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("z00nx", "z00nx0@gmail.com")] + + +getInfo = create_getInfo(DodanePl) diff --git a/module/plugins/hoster/DropboxCom.py b/module/plugins/hoster/DropboxCom.py new file mode 100644 index 000000000..658974d13 --- /dev/null +++ b/module/plugins/hoster/DropboxCom.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +import re + +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo + + +class DropboxCom(SimpleHoster): +    __name__    = "DropboxCom" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'https?://(?:www\.)?dropbox\.com/.+' + +    __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): +        self.download(self.pyfile.url, get={'dl': "1"}) + +        check = self.checkDownload({'html': re.compile("html")}) +        if check == "html": +            self.error(_("Downloaded file is an html page")) + + +getInfo = create_getInfo(DropboxCom) diff --git a/module/plugins/hoster/DuploadOrg.py b/module/plugins/hoster/DuploadOrg.py index 5909f7ccf..73702eb67 100644 --- a/module/plugins/hoster/DuploadOrg.py +++ b/module/plugins/hoster/DuploadOrg.py @@ -1,34 +1,18 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo -class DuploadOrg(XFileSharingPro): -    __name__ = "DuploadOrg" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?dupload\.org/\w{12}' -    __version__ = "0.01" -    __description__ = """Dupload.grg hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +class DuploadOrg(DeadHoster): +    __name__    = "DuploadOrg" +    __type__    = "hoster" +    __version__ = "0.02" -    HOSTER_NAME = "dupload.org" +    __pattern__ = r'http://(?:www\.)?dupload\.org/\w{12}' -    FILE_INFO_PATTERN = r'<h3[^>]*>(?P<N>.+) \((?P<S>[\d.]+) (?P<U>\w+)\)</h3>' +    __description__ = """Dupload.grg hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")]  getInfo = create_getInfo(DuploadOrg) diff --git a/module/plugins/hoster/EasybytezCom.py b/module/plugins/hoster/EasybytezCom.py index 0ea9b186f..cd54bdc70 100644 --- a/module/plugins/hoster/EasybytezCom.py +++ b/module/plugins/hoster/EasybytezCom.py @@ -1,46 +1,26 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. +class EasybytezCom(XFSHoster): +    __name__    = "EasybytezCom" +    __type__    = "hoster" +    __version__ = "0.23" -    @author: zoidberg -""" +    __pattern__ = r'http://(?:www\.)?easybytez\.com/\w{12}' -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo - - -class EasybytezCom(XFileSharingPro): -    __name__ = "EasybytezCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?easybytez.com/(\w+).*' -    __version__ = "0.17"      __description__ = """Easybytez.com hoster plugin""" -    __author_name__ = ("zoidberg", "stickell") -    __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it") +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] -    HOSTER_NAME = "easybytez.com" -    FILE_INFO_PATTERN = r'<span class="name">(?P<N>.+)</span><br>\s*<span class="size">(?P<S>[^<]+)</span>' -    FILE_OFFLINE_PATTERN = r'<h1>File not available</h1>' +    HOSTER_DOMAIN = "easybytez.com" -    DIRECT_LINK_PATTERN = r'(http://(\w+\.(easyload|easybytez|zingload)\.(com|to)|\d+\.\d+\.\d+\.\d+)/files/\d+/\w+/[^"<]+)' -    OVR_DOWNLOAD_LINK_PATTERN = r'<h2>Download Link</h2>\s*<textarea[^>]*>([^<]+)' -    OVR_KILL_LINK_PATTERN = r'<h2>Delete Link</h2>\s*<textarea[^>]*>([^<]+)' -    ERROR_PATTERN = r'(?:class=["\']err["\'][^>]*>|<Center><b>)(.*?)</' +    OFFLINE_PATTERN = r'>File not available' -    def setup(self): -        self.resumeDownload = self.multiDL = self.premium +    LINK_PATTERN = r'(http://(\w+\.(easybytez|easyload|ezbytez|zingload)\.(com|to)|\d+\.\d+\.\d+\.\d+)/files/\d+/\w+/.+?)["\'<]'  getInfo = create_getInfo(EasybytezCom) diff --git a/module/plugins/hoster/EdiskCz.py b/module/plugins/hoster/EdiskCz.py index f2d0fa206..eb9338871 100644 --- a/module/plugins/hoster/EdiskCz.py +++ b/module/plugins/hoster/EdiskCz.py @@ -1,53 +1,42 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re +  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class EdiskCz(SimpleHoster): -    __name__ = "EdiskCz" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?edisk.(cz|sk|eu)/(stahni|sk/stahni|en/download)/.*' -    __version__ = "0.21" +    __name__    = "EdiskCz" +    __type__    = "hoster" +    __version__ = "0.22" + +    __pattern__ = r'http://(?:www\.)?edisk\.(cz|sk|eu)/(stahni|sk/stahni|en/download)/.+' +      __description__ = """Edisk.cz hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __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>' -    URL_PATTERN = r'<form name = "formular" action = "([^"]+)" method = "post">' -    FILE_INFO_PATTERN = r'<span class="fl" title="(?P<N>[^"]+)">\s*.*?\((?P<S>[0-9.]*) (?P<U>[kKMG])i?B\)</h1></span>'      ACTION_PATTERN = r'/en/download/(\d+/.*\.html)' -    DLLINK_PATTERN = r'http://.*edisk.cz.*\.html' -    FILE_OFFLINE_PATTERN = r'<h3>This file does not exist due to one of the following:</h3><ul><li>' +    LINK_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) +        self.logDebug("URL:" + url) -        found = re.search(self.ACTION_PATTERN, url) -        if found is None: -            self.parseError("ACTION") -        action = found.group(1) +        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() @@ -58,8 +47,8 @@ class EdiskCz(SimpleHoster):              "action": action          }) -        if not re.match(self.DLLINK_PATTERN, url): -            self.fail("Unexpected server response") +        if not re.match(self.LINK_PATTERN, url): +            self.fail(_("Unexpected server response"))          self.download(url) diff --git a/module/plugins/hoster/EgoFilesCom.py b/module/plugins/hoster/EgoFilesCom.py index 68a95b33a..9a2f50ed4 100644 --- a/module/plugins/hoster/EgoFilesCom.py +++ b/module/plugins/hoster/EgoFilesCom.py @@ -1,100 +1,18 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ -# Test link (random.bin): -# http://egofiles.com/mOZfMI1WLZ6HBkGG/random.bin +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo -import re -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo -from module.plugins.internal.CaptchaService import ReCaptcha +class EgoFilesCom(DeadHoster): +    __name__    = "EgoFilesCom" +    __type__    = "hoster" +    __version__ = "0.16" +    __pattern__ = r'https?://(?:www\.)?egofiles\.com/\w+' -class EgoFilesCom(SimpleHoster): -    __name__ = "EgoFilesCom" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?egofiles.com/(\w+)' -    __version__ = "0.15"      __description__ = """Egofiles.com hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" - -    FILE_INFO_PATTERN = r'<div class="down-file">\s+(?P<N>[^\t]+)\s+<div class="file-properties">\s+(File size|Rozmiar): (?P<S>[\w.]+) (?P<U>\w+) \|' -    FILE_OFFLINE_PATTERN = r'(File size|Rozmiar): 0 KB' -    WAIT_TIME_PATTERN = r'For next free download you have to wait <strong>((?P<m>\d*)m)? ?((?P<s>\d+)s)?</strong>' -    DIRECT_LINK_PATTERN = r'<a href="(?P<link>[^"]+)">Download ></a>' -    RECAPTCHA_KEY = '6LeXatQSAAAAAHezcjXyWAni-4t302TeYe7_gfvX' - -    def setup(self): -        # Set English language -        self.load("https://egofiles.com/ajax/lang.php?lang=en", just_header=True) - -    def process(self, pyfile): -        if self.premium and (not self.SH_CHECK_TRAFFIC or self.checkTrafficLeft()): -            self.handlePremium() -        else: -            self.handleFree() - -    def handleFree(self): -        self.html = self.load(self.pyfile.url, decode=True) -        self.getFileInfo() - -        # Wait time between free downloads -        if 'For next free download you have to wait' in self.html: -            m = re.search(self.WAIT_TIME_PATTERN, self.html).groupdict('0') -            waittime = int(m['m']) * 60 + int(m['s']) -            self.wait(waittime, True) - -        downloadURL = '' -        recaptcha = ReCaptcha(self) -        for _ in xrange(5): -            challenge, response = recaptcha.challenge(self.RECAPTCHA_KEY) -            post_data = {'recaptcha_challenge_field': challenge, -                         'recaptcha_response_field': response} -            self.html = self.load(self.pyfile.url, post=post_data, decode=True) -            m = re.search(self.DIRECT_LINK_PATTERN, self.html) -            if not m: -                self.logInfo('Wrong captcha') -                self.invalidCaptcha() -            elif hasattr(m, 'group'): -                downloadURL = m.group('link') -                self.correctCaptcha() -                break -            else: -                self.fail('Unknown error - Plugin may be out of date') - -        if not downloadURL: -            self.fail("No Download url retrieved/all captcha attempts failed") - -        self.download(downloadURL, disposition=True) - -    def handlePremium(self): -        header = self.load(self.pyfile.url, just_header=True) -        if 'location' in header: -            self.logDebug('DIRECT LINK from header: ' + header['location']) -            self.download(header['location']) -        else: -            self.html = self.load(self.pyfile.url, decode=True) -            self.getFileInfo() -            m = re.search(r'<a href="(?P<link>[^"]+)">Download ></a>', self.html) -            if not m: -                self.parseError('Unable to detect direct download url') -            else: -                self.logDebug('DIRECT URL from html: ' + m.group('link')) -                self.download(m.group('link'), disposition=True) +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")]  getInfo = create_getInfo(EgoFilesCom) diff --git a/module/plugins/hoster/EnteruploadCom.py b/module/plugins/hoster/EnteruploadCom.py new file mode 100644 index 000000000..bbd613f57 --- /dev/null +++ b/module/plugins/hoster/EnteruploadCom.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class EnteruploadCom(DeadHoster): +    __name__    = "EnteruploadCom" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?enterupload\.com/\w+' + +    __description__ = """EnterUpload.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +getInfo = create_getInfo(EnteruploadCom) diff --git a/module/plugins/hoster/EpicShareNet.py b/module/plugins/hoster/EpicShareNet.py index 65e9cb5c3..8ac8cdaf2 100644 --- a/module/plugins/hoster/EpicShareNet.py +++ b/module/plugins/hoster/EpicShareNet.py @@ -1,24 +1,18 @@  # -*- coding: utf-8 -*- -# Test links: -# BigBuckBunny_320x180.mp4 - 61.7 Mb - http://epicshare.net/fch3m2bk6ihp/BigBuckBunny_320x180.mp4.html +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +class EpicShareNet(DeadHoster): +    __name__    = "EpicShareNet" +    __type__    = "hoster" +    __version__ = "0.02" -class EpicShareNet(XFileSharingPro): -    __name__ = "EpicShareNet" -    __type__ = "hoster"      __pattern__ = r'https?://(?:www\.)?epicshare\.net/\w{12}' -    __version__ = "0.01" -    __description__ = """EpicShare.net hoster plugin""" -    __author_name__ = "t4skforce" -    __author_mail__ = "t4skforce1337[AT]gmail[DOT]com" - -    HOSTER_NAME = "epicshare.net" -    FILE_OFFLINE_PATTERN = r'<b>File Not Found</b><br><br>' -    FILE_NAME_PATTERN = r'<b>Password:</b></div>\s*<h2>(?P<N>[^<]+)</h2>' +    __description__ = """EpicShare.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")]  getInfo = create_getInfo(EpicShareNet) diff --git a/module/plugins/hoster/EuroshareEu.py b/module/plugins/hoster/EuroshareEu.py index fa6342014..cc10abb37 100644 --- a/module/plugins/hoster/EuroshareEu.py +++ b/module/plugins/hoster/EuroshareEu.py @@ -1,52 +1,41 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re +  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class EuroshareEu(SimpleHoster): -    __name__ = "EuroshareEu" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?euroshare.(eu|sk|cz|hu|pl)/file/.*' -    __version__ = "0.25" +    __name__    = "EuroshareEu" +    __type__    = "hoster" +    __version__ = "0.26" + +    __pattern__ = r'http://(?:www\.)?euroshare\.(eu|sk|cz|hu|pl)/file/.+' +      __description__ = """Euroshare.eu hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    FILE_INFO_PATTERN = r'<span style="float: left;"><strong>(?P<N>.+?)</strong> \((?P<S>.+?)\)</span>' -    FILE_OFFLINE_PATTERN = ur'<h2>S.bor sa nena.iel</h2>|PoÅŸadovaná stránka neexistuje!' +    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!'      FREE_URL_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/"' -    FILE_URL_REPLACEMENTS = [(r"(http://[^/]*\.)(sk|cz|hu|pl)/", r"\1eu/")] +    URL_REPLACEMENTS = [(r"(http://[^/]*\.)(sk|cz|hu|pl)/", r"\1eu/")] +      def setup(self):          self.multiDL = self.resumeDownload = self.premium          self.req.setOption("timeout", 120) +      def handlePremium(self):          if self.ERR_NOT_LOGGED_IN_PATTERN in self.html:              self.account.relogin(self.user) -            self.retry(reason="User not logged in") +            self.retry(reason=_("User not logged in"))          self.download(self.pyfile.url.rstrip('/') + "/download/") @@ -54,18 +43,19 @@ class EuroshareEu(SimpleHoster):                                      "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") +            self.retry(reason=_("Access token expired"))          elif check == "json":              self.fail(self.lastCheck.group(1)) +      def handleFree(self):          if re.search(self.ERR_PARDL_PATTERN, self.html) is not None:              self.longWait(5 * 60, 12) -        found = re.search(self.FREE_URL_PATTERN, self.html) -        if found is None: -            self.parseError("Parse error (URL)") -        parsed_url = "http://euroshare.eu%s" % found.group(1) +        m = re.search(self.FREE_URL_PATTERN, self.html) +        if m is None: +            self.error(_("FREE_URL_PATTERN not found")) +        parsed_url = "http://euroshare.eu%s" % m.group(1)          self.logDebug("URL", parsed_url)          self.download(parsed_url, disposition=True) diff --git a/module/plugins/hoster/ExtabitCom.py b/module/plugins/hoster/ExtabitCom.py index 4dba81a8f..7609954d3 100644 --- a/module/plugins/hoster/ExtabitCom.py +++ b/module/plugins/hoster/ExtabitCom.py @@ -1,87 +1,77 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo -from module.plugins.internal.CaptchaService import ReCaptcha  from module.common.json_layer import json_loads +from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, secondsToMidnight +  class ExtabitCom(SimpleHoster): -    __name__ = "ExtabitCom" -    __type__ = "hoster" +    __name__    = "ExtabitCom" +    __type__    = "hoster" +    __version__ = "0.63" +      __pattern__ = r'http://(?:www\.)?extabit\.com/(file|go|fid)/(?P<ID>\w+)' -    __version__ = "0.5" +      __description__ = """Extabit.com hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    FILE_NAME_PATTERN = r'<th>File:</th>\s*<td class="col-fileinfo">\s*<div title="(?P<N>[^"]+)">' -    FILE_SIZE_PATTERN = r'<th>Size:</th>\s*<td class="col-fileinfo">(?P<S>[^<]+)</td>' -    FILE_OFFLINE_PATTERN = r'>File not found<' -    TEMP_OFFLINE_PATTERN = r">(File is temporary unavailable|No download mirror)<" +    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_PATTERN = r'[\'"](http://guest\d+\.extabit\.com/\w+/.*?)[\'"]' -    DOWNLOAD_LINK_PATTERN = r'[\'"](http://guest\d+\.extabit\.com/[a-z0-9]+/.*?)[\'"]'      def handleFree(self):          if r">Only premium users can download this file" in self.html: -            self.fail("Only premium users can download this file") +            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.wait(1 * 60 * 60, True) +            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.file_info('ID') +        fileID = m.group('ID') if m else self.info('ID')          m = re.search(r'recaptcha/api/challenge\?k=(\w+)', self.html)          if m:              recaptcha = ReCaptcha(self)              captcha_key = m.group(1) -            for _ in xrange(5): +            for _i in xrange(5):                  get_data = {"type": "recaptcha"} -                get_data["challenge"], get_data["capture"] = recaptcha.challenge(captcha_key) -                response = json_loads(self.load("http://extabit.com/file/%s/" % fileID, get=get_data)) -                if "ok" in response: +                get_data['challenge'], get_data['capture'] = 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") +                self.fail(_("Invalid captcha"))          else: -            self.parseError('Captcha') +            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'])) -        if not "href" in response: -            self.parseError('JSON') +        m = re.search(self.LINK_PATTERN, self.html) +        if m is None: +            self.error(_("LINK_PATTERN not found")) -        self.html = self.load("http://extabit.com/file/%s%s" % (fileID, response['href'])) -        m = re.search(self.DOWNLOAD_LINK_PATTERN, self.html) -        if not m: -            self.parseError('Download URL')          url = m.group(1) -        self.logDebug("Download URL: " + url)          self.download(url) diff --git a/module/plugins/hoster/FastixRu.py b/module/plugins/hoster/FastixRu.py index 8eeabc17c..5f6fd2d4c 100644 --- a/module/plugins/hoster/FastixRu.py +++ b/module/plugins/hoster/FastixRu.py @@ -1,20 +1,25 @@  # -*- coding: utf-8 -*-  import re -from urllib import unquote +  from random import randrange -from module.plugins.Hoster import Hoster +from urllib import unquote +  from module.common.json_layer import json_loads +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo -class FastixRu(Hoster): -    __name__ = "FastixRu" -    __version__ = "0.04" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?fastix\.(ru|it)/file/(?P<ID>[a-zA-Z0-9]{24})' +class FastixRu(MultiHoster): +    __name__    = "FastixRu" +    __type__    = "hoster" +    __version__ = "0.08" + +    __pattern__ = r'http://(?:www\.)?fastix\.(ru|it)/file/\w{24}' +      __description__ = """Fastix hoster plugin""" -    __author_name__ = "Massimo Rosamilia" -    __author_mail__ = "max@spiritix.eu" +    __license__     = "GPLv3" +    __authors__     = [("Massimo Rosamilia", "max@spiritix.eu")] +      def getFilename(self, url):          try: @@ -25,42 +30,40 @@ class FastixRu(Hoster):              name += "%s.tmp" % randrange(100, 999)          return name +      def setup(self): -        self.chunkLimit = 3 +        self.chunkLimit     = 3          self.resumeDownload = True -    def process(self, pyfile): -        if re.match(self.__pattern__, pyfile.url): -            new_url = pyfile.url -        elif not self.account: -            self.logError(_("Please enter your %s account or deactivate this plugin") % "Fastix") -            self.fail("No Fastix account provided") + +    def handlePremium(self): +        api_key = self.account.getAccountData(self.user) +        api_key = api_key['api'] + +        page = self.load("http://fastix.ru/api_v2/", +                         get={'apikey': api_key, 'sub': "getdirectlink", 'link': self.pyfile.url}) +        data = json_loads(page) + +        self.logDebug("Json data", data) + +        if "error\":true" in page: +            self.offline()          else: -            self.logDebug("Old URL: %s" % pyfile.url) -            api_key = self.account.getAccountData(self.user) -            api_key = api_key["api"] -            url = "http://fastix.ru/api_v2/?apikey=%s&sub=getdirectlink&link=%s" % (api_key, pyfile.url) -            page = self.load(url) -            data = json_loads(page) -            self.logDebug("Json data: %s" % str(data)) -            if "error\":true" in page: -                self.offline() -            else: -                new_url = data["downloadlink"] - -        if new_url != pyfile.url: -            self.logDebug("New URL: %s" % new_url) - -        if pyfile.name.startswith("http") or pyfile.name.startswith("Unknown"): +            self.link = data['downloadlink'] + +        if self.link != self.pyfile.url: +            self.logDebug("New URL: %s" % self.link) + +        if self.pyfile.name.startswith("http") or self.pyfile.name.startswith("Unknown"):              #only use when name wasnt already set -            pyfile.name = self.getFilename(new_url) +            self.pyfile.name = self.getFilename(self.link) + + +    def checkFile(self): +        super(FastixRu, self).checkFile() -        self.download(new_url, disposition=True) +        if self.checkDownload({"error": "<title>An error occurred while processing your request</title>"}) is "error": +            self.retry(wait_time=60, reason=_("An error occurred while generating link")) -        check = self.checkDownload({"error": "<title>An error occurred while processing your request</title>", -                                    "empty": re.compile(r"^$")}) -        if check == "error": -            self.retry(wait_time=60, reason="An error occurred while generating link.") -        elif check == "empty": -            self.retry(wait_time=60, reason="Downloaded File was empty.") +getInfo = create_getInfo(FastixRu) diff --git a/module/plugins/hoster/FastshareCz.py b/module/plugins/hoster/FastshareCz.py index c7841a237..31437c6e7 100644 --- a/module/plugins/hoster/FastshareCz.py +++ b/module/plugins/hoster/FastshareCz.py @@ -1,98 +1,78 @@  # -*- coding: utf-8 -*- -############################################################################### -#  This program is free software: you can redistribute it and/or modify -#  it under the terms of the GNU Affero General Public License as -#  published by the Free Software Foundation, either version 3 of the -#  License, or (at your option) any later version. -# -#  This program is distributed in the hope that it will be useful, -#  but WITHOUT ANY WARRANTY; without even the implied warranty of -#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -#  GNU Affero General Public License for more details. -# -#  You should have received a copy of the GNU Affero General Public License -#  along with this program.  If not, see <http://www.gnu.org/licenses/>. -# -#  @author: zoidberg -############################################################################### - -# Test links (random.bin): -# http://www.fastshare.cz/2141189/random.bin  import re +  from urlparse import urljoin  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class FastshareCz(SimpleHoster): -    __name__ = "FastshareCz" -    __type__ = "hoster" +    __name__    = "FastshareCz" +    __type__    = "hoster" +    __version__ = "0.26" +      __pattern__ = r'http://(?:www\.)?fastshare\.cz/\d+/.+' -    __version__ = "0.22" +      __description__ = """FastShare.cz hoster plugin""" -    __author_name__ = ("zoidberg", "stickell", "Walter Purcaro") -    __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it", "vuolter@gmail.com") +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] -    FILE_INFO_PATTERN = r'<h1 class="dwp">(?P<N>[^<]+)</h1>\s*<div class="fileinfo">\s*Size\s*: (?P<S>\d+) (?P<U>\w+),' -    FILE_OFFLINE_PATTERN = '>(The file has been deleted|Requested page not found)' +    URL_REPLACEMENTS = [("#.*", "")] -    FILE_URL_REPLACEMENTS = [("#.*", "")] -    SH_COOKIES = [(".fastshare.cz", "lang", "en")] +    COOKIES = [("fastshare.cz", "lang", "en")] -    FREE_URL_PATTERN = r'action=(/free/.*?)>\s*<img src="([^"]*)"><br' -    PREMIUM_URL_PATTERN = r'(http://data\d+\.fastshare\.cz/download\.php\?id=\d+&)' -    CREDIT_PATTERN = " credit for " +    INFO_PATTERN    = r'<h1 class="dwp">(?P<N>[^<]+)</h1>\s*<div class="fileinfo">\s*Size\s*: (?P<S>\d+) (?P<U>[\w^_]+),' +    OFFLINE_PATTERN = r'>(The file has been deleted|Requested page not found)' -    def handleFree(self): -        if "> 100% of FREE slots are full" in self.html: -            self.retry(120, 60, "No free slots") +    LINK_FREE_PATTERN    = r'action=(/free/.*?)>\s*<img src="([^"]*)"><br' +    LINK_PREMIUM_PATTERN = r'(http://data\d+\.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) -        found = re.search(self.FREE_URL_PATTERN, self.html) -        if found: -            action, captcha_src = found.groups() + +    def handleFree(self): +        m = re.search(self.FREE_URL_PATTERN, self.html) +        if m: +            action, captcha_src = m.groups()          else: -            self.parseError("Free URL") +            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}) +        self.download(urljoin(baseurl, action), post={'code': captcha, 'btn.x': 77, 'btn.y': 18}) + + +    def checkFile(self): +        super(FastshareCz, self).checkFile()          check = self.checkDownload({ -            "paralell_dl": -            "<title>FastShare.cz</title>|<script>alert\('Pres FREE muzete stahovat jen jeden soubor najednou.'\)", -            "wrong_captcha": "Download for FREE" +            '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") +            self.retry(6, 10 * 60, _("Paralell download")) -    def handlePremium(self): -        header = self.load(self.pyfile.url, just_header=True) -        if "location" in header: -            url = header["location"] -        else: -            self.html = self.load(self.pyfile.url) - -            self.getFileInfo()  # - -            if self.CREDIT_PATTERN in self.html: -                self.logWarning("Not enough traffic left") -                self.resetAccount() -            else: -                found = re.search(self.PREMIUM_URL_PATTERN, self.html) -                if found: -                    url = found.group(1) -                else: -                    self.parseError("Premium URL") - -        self.logDebug("PREMIUM URL: " + url) -        self.download(url, disposition=True) +        elif check == "wrong_captcha": +            self.retry(max_tries=5, reason=_("Wrong captcha")) -        check = self.checkDownload({"credit": re.compile(self.CREDIT_PATTERN)}) -        if check == "credit": +        elif check == "credit":              self.resetAccount() diff --git a/module/plugins/hoster/File4safeCom.py b/module/plugins/hoster/File4safeCom.py deleted file mode 100644 index 9e06972e2..000000000 --- a/module/plugins/hoster/File4safeCom.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- - -import re -from pycurl import FOLLOWLOCATION - -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo - - -class File4safeCom(XFileSharingPro): -    __name__ = "File4safeCom" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?file4safe\.com/\w+' -    __version__ = "0.01" -    __description__ = """File4safe.com hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" - -    HOSTER_NAME = "file4safe.com" - -    def handlePremium(self): -        self.req.http.lastURL = self.pyfile.url - -        self.req.http.c.setopt(FOLLOWLOCATION, 0) -        self.load(self.pyfile.url, post=self.getPostParameters(), decode=True) -        self.header = self.req.http.header -        self.req.http.c.setopt(FOLLOWLOCATION, 1) - -        found = re.search(r"Location\s*:\s*(.*)", self.header, re.I) -        if found and re.match(self.DIRECT_LINK_PATTERN, found.group(1)): -            location = found.group(1).strip() -            self.startDownload(location) -        else: -            self.parseError("Unable to detect premium download link") - - -getInfo = create_getInfo(File4safeCom) diff --git a/module/plugins/hoster/FileApeCom.py b/module/plugins/hoster/FileApeCom.py index 34b4acd07..db843586b 100644 --- a/module/plugins/hoster/FileApeCom.py +++ b/module/plugins/hoster/FileApeCom.py @@ -4,13 +4,15 @@ from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class FileApeCom(DeadHoster): -    __name__ = "FileApeCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?fileape\.com/(index\.php\?act=download\&id=|dl/)\w+' +    __name__    = "FileApeCom" +    __type__    = "hoster"      __version__ = "0.12" + +    __pattern__ = r'http://(?:www\.)?fileape\.com/(index\.php\?act=download\&id=|dl/)\w+' +      __description__ = """FileApe.com hoster plugin""" -    __author_name__ = "espes" -    __author_mail__ = "" +    __license__     = "GPLv3" +    __authors__     = [("espes", None)]  getInfo = create_getInfo(FileApeCom) diff --git a/module/plugins/hoster/FileParadoxIn.py b/module/plugins/hoster/FileParadoxIn.py new file mode 100644 index 000000000..0b5b57e22 --- /dev/null +++ b/module/plugins/hoster/FileParadoxIn.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +import re + +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo + + +class FileParadoxIn(XFSHoster): +    __name__    = "FileParadoxIn" +    __type__    = "hoster" +    __version__ = "0.04" + +    __pattern__ = r'https?://(?:www\.)?fileparadox\.in/\w{12}' + +    __description__ = """FileParadox.in hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RazorWing", "muppetuk1@hotmail.com")] + + +    HOSTER_DOMAIN = "fileparadox.in" + + +getInfo = create_getInfo(FileParadoxIn) diff --git a/module/plugins/hoster/FileSharkPl.py b/module/plugins/hoster/FileSharkPl.py new file mode 100644 index 000000000..5250a92fe --- /dev/null +++ b/module/plugins/hoster/FileSharkPl.py @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo + + +class FileSharkPl(SimpleHoster): +    __name__    = "FileSharkPl" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?fileshark\.pl/pobierz/\d{6}/\w{5}' + +    __description__ = """FileShark.pl hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("prOq", None), +                       ("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 = '(P|p)lik zosta. (usuni.ty|przeniesiony)' + +    LINK_FREE_PATTERN    = r'<a href="(.*?)" class="btn-upload-free">' +    LINK_PREMIUM_PATTERN = r'<a 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 = '<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) + + +    #@NOTE: handlePremium method was never been tested +    def handlePremium(self): +        super(FilerNet, self).handlePremium() +        if self.link: +            self.link = urljoin("http://fileshark.pl/", self.link) + + +    def handleFree(self): +        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)) + +        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, cookies=True, disposition=True) + + +    def checkFile(self): +        super(FileSharkPl, self).checkFile() + +        check = self.checkDownload({'wrong_captcha': re.compile(r'<label for="form_captcha" generated="true" class="error">(.*?)</label>'), +                                    'wait_pattern' : re.compile(self.SECONDS_PATTERN), +                                    'DL-found'     : re.compile('<a href="(.*)">')}) + +        if check == "DL-found": +            self.correctCaptcha() + +        elif check == "wrong_captcha": +            self.invalidCaptcha() +            self.retry(10, 1, _("Wrong captcha solution")) + +        elif check == "wait_pattern": +            self.retry() + + +    def _decode64(self, data, *args, **kwargs): +        return data.decode("base64") + + +getInfo = create_getInfo(FileSharkPl) diff --git a/module/plugins/hoster/FileStoreTo.py b/module/plugins/hoster/FileStoreTo.py index 5a73fb9ef..e1bd8da71 100644 --- a/module/plugins/hoster/FileStoreTo.py +++ b/module/plugins/hoster/FileStoreTo.py @@ -1,47 +1,36 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: Walter Purcaro -""" -  import re  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class FileStoreTo(SimpleHoster): -    __name__ = "FileStoreTo" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?filestore\.to/\?d=(?P<ID>\w+)' +    __name__    = "FileStoreTo" +    __type__    = "hoster"      __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?filestore\.to/\?d=(?P<ID>\w+)' +      __description__ = """FileStore.to hoster plugin""" -    __author_name__ = ("Walter Purcaro", "stickell") -    __author_mail__ = ("vuolter@gmail.com", "l.stickell@yahoo.it") +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    INFO_PATTERN = r'File: <span[^>]*>(?P<N>.+)</span><br />Size: (?P<S>[\d.,]+) (?P<U>[\w^_]+)' +    OFFLINE_PATTERN = r'>Download-Datei wurde nicht gefunden<' -    FILE_INFO_PATTERN = r'File: <span[^>]*>(?P<N>.+)</span><br />Size: (?P<S>[\d,.]+) (?P<U>\w+)' -    FILE_OFFLINE_PATTERN = r'>Download-Datei wurde nicht gefunden<'      def setup(self): -        self.resumeDownload = self.multiDL = True +        self.resumeDownload = True +        self.multiDL        = True +      def handleFree(self):          self.wait(10)          ldc = re.search(r'wert="(\w+)"', self.html).group(1)          link = self.load("http://filestore.to/ajax/download.php", get={"LDC": ldc}) -        self.logDebug("Download link = " + link)          self.download(link) diff --git a/module/plugins/hoster/FilebeerInfo.py b/module/plugins/hoster/FilebeerInfo.py index dd26b9120..885010a2c 100644 --- a/module/plugins/hoster/FilebeerInfo.py +++ b/module/plugins/hoster/FilebeerInfo.py @@ -4,13 +4,15 @@ from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class FilebeerInfo(DeadHoster): -    __name__ = "FilebeerInfo" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?filebeer\.info/(?!\d*~f)(?P<ID>\w+).*' +    __name__    = "FilebeerInfo" +    __type__    = "hoster"      __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?filebeer\.info/(?!\d*~f)(?P<ID>\w+)' +      __description__ = """Filebeer.info plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")]  getInfo = create_getInfo(FilebeerInfo) diff --git a/module/plugins/hoster/FilecloudIo.py b/module/plugins/hoster/FilecloudIo.py index 56eedf33a..8b6804a68 100644 --- a/module/plugins/hoster/FilecloudIo.py +++ b/module/plugins/hoster/FilecloudIo.py @@ -1,119 +1,116 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, PluginParseError +  from module.common.json_layer import json_loads  from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class FilecloudIo(SimpleHoster): -    __name__ = "FilecloudIo" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?(?:filecloud\.io|ifile\.it|mihd\.net)/(?P<ID>\w+).*' -    __version__ = "0.02" +    __name__    = "FilecloudIo" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?(?:filecloud\.io|ifile\.it|mihd\.net)/(?P<ID>\w+)' +      __description__ = """Filecloud.io hoster plugin""" -    __author_name__ = ("zoidberg", "stickell") -    __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it") +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    SIZE_PATTERN = r'{var __ab1 = (?P<S>\d+);}' +    NAME_PATTERN = r'id="aliasSpan">(?P<N>.*?)  <' +    OFFLINE_PATTERN = r'l10n\.(FILES__DOESNT_EXIST|REMOVED)' +    TEMP_OFFLINE_PATTERN = r'l10n\.FILES__WARNING' -    FILE_SIZE_PATTERN = r'{var __ab1 = (?P<S>\d+);}' -    FILE_NAME_PATTERN = r'id="aliasSpan">(?P<N>.*?)  <' -    FILE_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_PATTERN = r'"(http://s\d+\.filecloud\.io/%s/\d+/.*?)"' -    UKEY_PATTERN = r"'ukey'\s*:'(\w+)'," -    AB1_PATTERN = r"if\( __ab1 == '(\w+)' \)" -    ERROR_MSG_PATTERN = r"var __error_msg\s*=\s*l10n\.(.*?);" -    DOWNLOAD_LINK_PATTERN = r'"(http://s\d+.filecloud.io/%s/\d+/.*?)"' -    RECAPTCHA_KEY_PATTERN = r"var __recaptcha_public\s*=\s*'([^']+)';" -    RECAPTCHA_KEY = '6Lf5OdISAAAAAEZObLcx5Wlv4daMaASRov1ysDB1'      def setup(self): -        self.resumeDownload = self.multiDL = True -        self.chunkLimit = 1 +        self.resumeDownload = True +        self.multiDL        = True +        self.chunkLimit     = 1 +      def handleFree(self): -        data = {"ukey": self.file_info['ID']} +        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() -        found = re.search(self.AB1_PATTERN, self.html) -        if not found: -            raise PluginParseError("__AB1") -        data["__ab1"] = found.group(1) +        if captcha_key is None: +            self.error(_("ReCaptcha key not found"))          if not self.account: -            self.fail("User not logged in") +            self.fail(_("User not logged in"))          elif not self.account.logged_in: -            recaptcha = ReCaptcha(self) -            captcha_challenge, captcha_response = recaptcha.challenge(self.RECAPTCHA_KEY) -            self.account.form_data = {"recaptcha_challenge_field": captcha_challenge, -                                      "recaptcha_response_field": captcha_response} +            challenge, response = 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" -        response = self.load(json_url, post=data) -        self.logDebug(response) -        response = json_loads(response) +        res = self.load(json_url, post=data) +        self.logDebug(res) +        res = json_loads(res) -        if "error" in response and response["error"]: -            self.fail(response) +        if "error" in res and res['error']: +            self.fail(res) -        self.logDebug(response) -        if response["captcha"]: -            recaptcha = ReCaptcha(self) -            found = re.search(self.RECAPTCHA_KEY_PATTERN, self.html) -            captcha_key = found.group(1) if found else self.RECAPTCHA_KEY -            data["ctype"] = "recaptcha" +        self.logDebug(res) +        if res['captcha']: +            data['ctype'] = "recaptcha" -            for _ in xrange(5): -                data["recaptcha_challenge"], data["recaptcha_response"] = recaptcha.challenge(captcha_key) +            for _i in xrange(5): +                data['recaptcha_challenge'], data['recaptcha_response'] = recaptcha.challenge(captcha_key)                  json_url = "http://filecloud.io/download-request.json" -                response = self.load(json_url, post=data) -                self.logDebug(response) -                response = json_loads(response) +                res = self.load(json_url, post=data) +                self.logDebug(res) +                res = json_loads(res) -                if "retry" in response and response["retry"]: +                if "retry" in res and res['retry']:                      self.invalidCaptcha()                  else:                      self.correctCaptcha()                      break              else: -                self.fail("Incorrect captcha") +                self.fail(_("Incorrect captcha")) -        if response["dl"]: +        if res['dl']:              self.html = self.load('http://filecloud.io/download.html') -            found = re.search(self.DOWNLOAD_LINK_PATTERN % self.file_info['ID'], self.html) -            if not found: -                raise PluginParseError("Download URL") -            download_url = found.group(1) -            self.logDebug("Download URL: %s" % download_url) - -            if "size" in self.file_info and self.file_info['size']: -                self.check_data = {"size": int(self.file_info['size'])} + +            m = re.search(self.LINK_PATTERN % self.info['pattern']['ID'], self.html) +            if m is None: +                self.error(_("LINK_PATTERN not found")) + +            if "size" in self.info and self.info['size']: +                self.check_data = {"size": int(self.info['size'])} + +            download_url = m.group(1)              self.download(download_url)          else: -            self.fail("Unexpected server response") +            self.fail(_("Unexpected server response")) +      def handlePremium(self):          akey = self.account.getAccountData(self.user)['akey'] -        ukey = self.file_info['ID'] +        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}) diff --git a/module/plugins/hoster/FilefactoryCom.py b/module/plugins/hoster/FilefactoryCom.py index 273a717b5..ada498a51 100644 --- a/module/plugins/hoster/FilefactoryCom.py +++ b/module/plugins/hoster/FilefactoryCom.py @@ -1,106 +1,90 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################  import re -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo +from urlparse import urljoin + +from module.network.RequestFactory import getURL +from module.plugins.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" -    __pattern__ = r'https?://(?:www\.)?filefactory\.com/file/(?P<id>[a-zA-Z0-9]+)' -    __version__ = "0.47" +    __name__    = "FilefactoryCom" +    __type__    = "hoster" +    __version__ = "0.52" + +    __pattern__ = r'https?://(?:www\.)?filefactory\.com/(file|trafficshare/\w+)/\w+' +      __description__ = """Filefactory.com hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __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_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")] -    FILE_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' -    DIRECT_LINK_PATTERN = r'<a href="(https?://[^"]+)"[^>]*><i[^>]*></i> Download with FileFactory Premium</a>' -    FILE_OFFLINE_PATTERN = r'<h2>File Removed</h2>' -    PREMIUM_ONLY_PATTERN = r'>Premium Account Required<'      def handleFree(self): -        self.html = self.load(self.pyfile.url, decode=True)          if "Currently only Premium Members can download files larger than" in self.html: -            self.fail("File too large for free download") +            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") +            self.retry(50, 15 * 60, _("All free slots are busy")) -        m = re.search(r'data-href-direct="(http://[^"]+)"', self.html) +        m = re.search(self.LINK_PATTERN, self.html) +        if m is None: +            self.error(_("Free download link not found")) + +        dl_link = m.group(1) + +        m = re.search(self.WAIT_PATTERN, self.html)          if m: -            t = re.search(r'<div id="countdown_clock" data-delay="(\d+)">', self.html) -            if t: -                t = t.group(1) -            else: -                self.logDebug("Unable to detect countdown duration. Guessing 60 seconds") -                t = 60 -            self.wait(t) -            direct = m.group(1) -        else:  # This section could be completely useless now -            # Load the page that contains the direct link -            url = re.search(r"document\.location\.host \+\s*'(.+)';", self.html) -            if not url: -                self.parseError('Unable to detect free link') -            url = 'http://www.filefactory.com' + url.group(1) -            self.html = self.load(url, decode=True) - -            # Free downloads wait time -            waittime = re.search(r'id="startWait" value="(\d+)"', self.html) -            if not waittime: -                self.parseError('Unable to detect wait time') -            self.wait(int(waittime.group(1))) - -            # Parse the direct link and download it -            direct = re.search(r'data-href-direct="(.*)" class="button', self.html) -            if not direct: -                self.parseError('Unable to detect free direct link') -            direct = direct.group(1) - -        self.logDebug('DIRECT LINK: ' + direct) -        self.download(direct, disposition=True) - -        check = self.checkDownload({"multiple": "You are currently downloading too many files at once.", -                                    "error": '<div id="errorMessage">'}) +            self.wait(int(m.group(1))) + +        self.download(dl_link, disposition=True) + +        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") +            self.retry(wait_time=15 * 60, reason=_("Parallel downloads"))          elif check == "error": -            self.fail("Unknown error") +            self.error(_("Unknown error")) +      def handlePremium(self):          header = self.load(self.pyfile.url, just_header=True) +          if 'location' in header:              url = header['location'].strip()              if not url.startswith("http://"): -                url = "http://www.filefactory.com" + url +                url = urljoin("http://www.filefactory.com", url)          elif 'content-disposition' in header:              url = self.pyfile.url          else: -            self.logInfo('You could enable "Direct Downloads" on http://filefactory.com/account/')              html = self.load(self.pyfile.url) -            found = re.search(self.DIRECT_LINK_PATTERN, html) -            if found: -                url = found.group(1) +            m = re.search(self.LINK_PATTERN, html) +            if m: +                url = m.group(1)              else: -                self.parseError('Unable to detect premium direct link') +                self.error(_("Premium download link not found")) -        self.logDebug('DIRECT PREMIUM LINK: ' + url)          self.download(url, disposition=True) - - -getInfo = create_getInfo(FilefactoryCom) diff --git a/module/plugins/hoster/FilejungleCom.py b/module/plugins/hoster/FilejungleCom.py index 910798bf6..8a8aee9e2 100644 --- a/module/plugins/hoster/FilejungleCom.py +++ b/module/plugins/hoster/FilejungleCom.py @@ -1,37 +1,23 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  from module.plugins.hoster.FileserveCom import FileserveCom, checkFile  from module.plugins.Plugin import chunks  class FilejungleCom(FileserveCom): -    __name__ = "FilejungleCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?filejungle\.com/f/(?P<id>[^/]+).*' +    __name__    = "FilejungleCom" +    __type__    = "hoster"      __version__ = "0.51" + +    __pattern__ = r'http://(?:www\.)?filejungle\.com/f/(?P<ID>[^/]+)' +      __description__ = """Filejungle.com hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __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'] +    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">(?:<[^>]*>| )*([^<]*)' diff --git a/module/plugins/hoster/FileomCom.py b/module/plugins/hoster/FileomCom.py index 9fda2353c..2b6fd34db 100644 --- a/module/plugins/hoster/FileomCom.py +++ b/module/plugins/hoster/FileomCom.py @@ -1,52 +1,35 @@  # -*- coding: utf-8 -*- -############################################################################### -#  This program is free software: you can redistribute it and/or modify -#  it under the terms of the GNU Affero General Public License as -#  published by the Free Software Foundation, either version 3 of the -#  License, or (at your option) any later version.  # -#  This program is distributed in the hope that it will be useful, -#  but WITHOUT ANY WARRANTY; without even the implied warranty of -#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -#  GNU Affero General Public License for more details. -# -#  You should have received a copy of the GNU Affero General Public License -#  along with this program.  If not, see <http://www.gnu.org/licenses/>. -# -#  @author: Walter Purcaro -############################################################################### - -# Test links (random.bin): +# Test links:  # http://fileom.com/gycaytyzdw3g/random.bin.html -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo + +class FileomCom(XFSHoster): +    __name__    = "FileomCom" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'https?://(?:www\.)?fileom\.com/\w{12}' -class FileomCom(XFileSharingPro): -    __name__ = "FileomCom" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?fileom\.com/\w+' -    __version__ = "0.01"      __description__ = """Fileom.com hoster plugin""" -    __author_name__ = "Walter Purcaro" -    __author_mail__ = "vuolter@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] -    HOSTER_NAME = "fileom.com" -    FILE_URL_REPLACEMENTS = [(r'/$', "")] -    SH_COOKIES = [(".fileom.com", "lang", "english")] +    HOSTER_DOMAIN = "fileom.com" -    FILE_NAME_PATTERN = r'Filename: <span>(?P<N>.+?)<' -    FILE_SIZE_PATTERN = r'File Size: <span class="size">(?P<S>[\d\.]+) (?P<U>\w+)' +    NAME_PATTERN = r'Filename: <span>(?P<N>.+?)<' +    SIZE_PATTERN = r'File Size: <span class="size">(?P<S>[\d.,]+) (?P<U>[\w^_]+)' -    ERROR_PATTERN = r'class=["\']err["\'][^>]*>(.*?)(?:\'|</)' +    LINK_PATTERN = r'var url2 = \'(.+?)\';' -    DIRECT_LINK_PATTERN = r"var url2 = '(.+?)';"      def setup(self): -        self.resumeDownload = self.premium          self.multiDL = True          self.chunkLimit = 1 +        self.resumeDownload = self.premium  getInfo = create_getInfo(FileomCom) diff --git a/module/plugins/hoster/FilepostCom.py b/module/plugins/hoster/FilepostCom.py index 01ac76850..66c040770 100644 --- a/module/plugins/hoster/FilepostCom.py +++ b/module/plugins/hoster/FilepostCom.py @@ -1,87 +1,71 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg - -    changelog: -      0.27 - 2012-08-12 - hgg -          fix "global name 'js_answer' is not defined" bug -          fix captcha bug #1 (failed on non-english "captcha wrong" errors) -""" -  import re +  from time import time -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo -from module.plugins.internal.CaptchaService import ReCaptcha  from module.common.json_layer import json_loads +from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class FilepostCom(SimpleHoster): -    __name__ = "FilepostCom" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?(?:filepost\.com/files|fp.io)/([^/]+).*' -    __version__ = "0.27" +    __name__    = "FilepostCom" +    __type__    = "hoster" +    __version__ = "0.31" + +    __pattern__ = r'https?://(?:www\.)?(?:filepost\.com/files|fp\.io)/(?P<ID>[^/]+)' +      __description__ = """Filepost.com hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] -    FILE_INFO_PATTERN = r'<input type="text" id="url" value=\'<a href[^>]*>(?P<N>[^>]+?) - (?P<S>[0-9\.]+ [kKMG]i?B)</a>\' class="inp_text"/>' -    #FILE_INFO_PATTERN = r'<h1>(?P<N>[^<]+)</h1>\s*<div class="ul">\s*<ul>\s*<li><span>Size:</span> (?P<S>[0-9.]+) (?P<U>[kKMG])i?B</li>' -    FILE_OFFLINE_PATTERN = r'class="error_msg_title"> Invalid or Deleted File. </div>|<div class="file_info file_info_deleted">' -    RECAPTCHA_KEY_PATTERN = r"Captcha.init\({\s*key:\s*'([^']+)'" -    FLP_TOKEN_PATTERN = r"set_store_options\({token: '([^']+)'" -    def handleFree(self): -        # Find token and captcha key -        file_id = re.match(self.__pattern__, self.pyfile.url).group(1) +    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: \'(.+?)\'' -        found = re.search(self.FLP_TOKEN_PATTERN, self.html) -        if not found: -            self.parseError("Token") -        flp_token = found.group(1) -        found = re.search(self.RECAPTCHA_KEY_PATTERN, self.html) -        if not found: -            self.parseError("Captcha key") -        captcha_key = found.group(1) +    def handleFree(self): +        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() * 10000)) + '-xml'} -        post_dict = {'action': 'set_download', 'token': flp_token, 'code': file_id} +        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": file_id, "file_pass": ''} +        post_dict = {"token": flp_token, "code": self.info['pattern']['ID'], "file_pass": ''}          if 'var is_pass_exists = true;' in self.html: -            # Solve password             -            for file_pass in self.getPassword().splitlines(): +            # Solve password +            password = self.getPassword() + +            if password: +                self.logInfo(_("Password protected link, trying ") + file_pass) +                  get_dict['JsHttpRequest'] = str(int(time() * 10000)) + '-xml'                  post_dict['file_pass'] = file_pass -                self.logInfo("Password protected link, trying " + file_pass) -                download_url = self.getJsonResponse(get_dict, post_dict, 'link') -                if download_url: -                    break +                self.link = self.getJsonResponse(get_dict, post_dict, 'link') +                if not self.link: +                    self.fail(_("Incorrect password"))              else: -                self.fail("No or incorrect password") +                self.fail(_("No password found"))          else:              # Solve recaptcha @@ -90,10 +74,10 @@ class FilepostCom(SimpleHoster):              for i in xrange(5):                  get_dict['JsHttpRequest'] = str(int(time() * 10000)) + '-xml'                  if i: -                    post_dict["recaptcha_challenge_field"], post_dict["recaptcha_response_field"] = recaptcha.challenge( +                    post_dict['recaptcha_challenge_field'], post_dict['recaptcha_response_field'] = recaptcha.challenge(                          captcha_key)                      self.logDebug(u"RECAPTCHA: %s : %s : %s" % ( -                        captcha_key, post_dict["recaptcha_challenge_field"], post_dict["recaptcha_response_field"])) +                        captcha_key, post_dict['recaptcha_challenge_field'], post_dict['recaptcha_response_field']))                  download_url = self.getJsonResponse(get_dict, post_dict, 'link')                  if download_url: @@ -104,43 +88,46 @@ class FilepostCom(SimpleHoster):                      self.invalidCaptcha()              else: -                self.fail("Invalid captcha") +                self.fail(_("Invalid captcha"))          # Download          self.download(download_url) +      def getJsonResponse(self, get_dict, post_dict, field): -        json_response = json_loads(self.load('https://filepost.com/files/get/', get=get_dict, post=post_dict)) -        self.logDebug(json_response) +        res = json_loads(self.load('https://filepost.com/files/get/', get=get_dict, post=post_dict)) -        if not 'js' in json_response: -            self.parseError('JSON %s 1' % field) +        self.logDebug(res) -        # i changed js_answer to json_response['js'] since js_answer is nowhere set. +        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 json_response['js']['error'] as well as js_answer['error']. +        # accessed res['js']['error'] as well as js_answer['error'].          # see the two lines commented out with  "# ~?". -        if 'error' in json_response['js']: -            if json_response['js']['error'] == 'download_delay': -                self.retry(wait_time=json_response['js']['params']['next_download']) +        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 json_response['js']['error']: -                return None -            elif 'You entered a wrong CAPTCHA code' in json_response['js']['error']: -                return None -            elif 'CAPTCHA Code nicht korrekt' in json_response['js']['error']: + +            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 json_response['js']['error']: -                self.logDebug('error response is unknown, but mentions CAPTCHA -> return None') + +            elif 'CAPTCHA' in res['js']['error']: +                self.logDebug("Error response is unknown, but mentions CAPTCHA")                  return None +              else: -                self.fail(json_response['js']['error']) -                # ~? self.fail(js_answer['error']) +                self.fail(res['js']['error']) -        if not 'answer' in json_response['js'] or not field in json_response['js']['answer']: -            self.parseError('JSON %s 2' % field) +        if not 'answer' in res['js'] or not field in res['js']['answer']: +            self.error(_("JSON %s 2") % field) -        return json_response['js']['answer'][field] +        return res['js']['answer'][field]  getInfo = create_getInfo(FilepostCom) diff --git a/module/plugins/hoster/FilepupNet.py b/module/plugins/hoster/FilepupNet.py new file mode 100644 index 000000000..cc3e86bc9 --- /dev/null +++ b/module/plugins/hoster/FilepupNet.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://www.filepup.net/files/k5w4ZVoF1410184283.html +# http://www.filepup.net/files/R4GBq9XH1410186553.html + +import re + +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo + + +class FilepupNet(SimpleHoster): +    __name__    = "FilepupNet" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?filepup\.net/files/\w+' + +    __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_PATTERN = r'(http://www\.filepup\.net/get/.+?)\'' + + +    def setup(self): +        self.multiDL = False +        self.chunkLimit = 1 + + +    def handleFree(self): +        m = re.search(self.LINK_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"}) + +        check = self.checkDownload({'html': re.compile("html")}) +        if check == "html": +            self.error(_("Downloaded file is an html page")) + + +getInfo = create_getInfo(FilepupNet) diff --git a/module/plugins/hoster/FilerNet.py b/module/plugins/hoster/FilerNet.py index d39666922..2a38ac470 100644 --- a/module/plugins/hoster/FilerNet.py +++ b/module/plugins/hoster/FilerNet.py @@ -1,119 +1,82 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -# Test links (random.bin): +# +# Test links:  # http://filer.net/get/ivgf5ztw53et3ogd  # http://filer.net/get/hgo14gzcng3scbvv  import pycurl  import re +  from urlparse import urljoin -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class FilerNet(SimpleHoster): -    __name__ = "FilerNet" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?filer\.net/get/(\w+)' -    __version__ = "0.03" +    __name__    = "FilerNet" +    __type__    = "hoster" +    __version__ = "0.12" + +    __pattern__ = r'https?://(?:www\.)?filer\.net/get/\w+' +      __description__ = """Filer.net hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] -    FILE_INFO_PATTERN = r'<h1 class="page-header">Free Download (?P<N>\S+) <small>(?P<S>[\w.]+) (?P<U>\w+)</small></h1>' -    FILE_OFFLINE_PATTERN = r'Nicht gefunden' -    RECAPTCHA_KEY = '6LcFctISAAAAAAgaeHgyqhNecGJJRnxV1m_vAz3V' -    DIRECT_LINK_PATTERN = r'href="([^"]+)">Get download</a>' -    def process(self, pyfile): -        if self.premium and (not self.SH_CHECK_TRAFFIC or self.checkTrafficLeft()): -            self.handlePremium() -        else: -            self.handleFree() +    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' -    def handleFree(self): -        self.req.setOption("timeout", 120) -        self.html = self.load(self.pyfile.url, decode=not self.SH_BROKEN_ENCODING, cookies=self.SH_COOKIES) +    LINK_FREE_PATTERN = LINK_PREMIUM_PATTERN = r'href="([^"]+)">Get download</a>' + +    def checkErrors(self):          # Wait between downloads          m = re.search(r'musst du <span id="time">(\d+)</span> Sekunden warten', self.html)          if m: -            waittime = int(m.group(1)) -            self.retry(3, waittime, "Wait between free downloads") +            errmsg = self.info['error'] = _("Wait between free downloads") +            self.retry(wait_time=int(m.group(1)), reason=errmsg) -        self.getFileInfo() +        self.info.pop('error', None) -        self.html = self.load(self.pyfile.url, decode=True) -        inputs = self.parseHtmlForm(input_names='token')[1] +    def handleFree(self): +        inputs = self.parseHtmlForm(input_names={'token': re.compile(r'.+')})[1]          if 'token' not in inputs: -            self.parseError('Unable to detect token') -        token = inputs['token'] -        self.logDebug('Token: ' + token) +            self.error(_("Unable to detect token")) -        self.html = self.load(self.pyfile.url, post={'token': token}, decode=True) +        self.html = self.load(self.pyfile.url, post={'token': inputs['token']}, decode=True) -        inputs = self.parseHtmlForm(input_names='hash')[1] +        inputs = self.parseHtmlForm(input_names={'hash': re.compile(r'.+')})[1]          if 'hash' not in inputs: -            self.parseError('Unable to detect hash') -        hash_data = inputs['hash'] -        self.logDebug('Hash: ' + hash_data) +            self.error(_("Unable to detect hash")) -        downloadURL = ''          recaptcha = ReCaptcha(self) -        for _ in xrange(5): -            challenge, response = recaptcha.challenge(self.RECAPTCHA_KEY) -            post_data = {'recaptcha_challenge_field': challenge, -                         'recaptcha_response_field': response, -                         'hash': hash_data} -            # Workaround for 0.4.9 just_header issue. In 0.5 clean the code using just_header +        for _i in xrange(5): +            challenge, response = 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(self.pyfile.url, post=post_data) +            self.load(self.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(): -                location = re.search(r'location: (\S+)', self.req.http.header, re.I).group(1) -                downloadURL = urljoin('http://filer.net', location) +                self.link = re.search(r'location: (\S+)', self.req.http.header, re.I).group(1)                  self.correctCaptcha()                  break              else: -                self.logInfo('Wrong captcha')                  self.invalidCaptcha() -        if not downloadURL: -            self.fail("No Download url retrieved/all captcha attempts failed") - -        self.download(downloadURL, disposition=True) - -    def handlePremium(self): -        header = self.load(self.pyfile.url, just_header=True) -        if 'location' in header:  # Direct Download ON -            dl = self.pyfile.url -        else:  # Direct Download OFF -            html = self.load(self.pyfile.url) -            m = re.search(self.DIRECT_LINK_PATTERN, html) -            if not m: -                self.parseError("Unable to detect direct link, try to enable 'Direct download' in your user settings") -            dl = 'http://filer.net' + m.group(1) -        self.logDebug('Direct link: ' + dl) -        self.download(dl, disposition=True) +    def downloadLink(self, link): +        if link and isinstance(link, basestring): +            self.download(urljoin("http://filer.net/", link), disposition=True)  getInfo = create_getInfo(FilerNet) diff --git a/module/plugins/hoster/FilerioCom.py b/module/plugins/hoster/FilerioCom.py index a0c67509f..db81f5b16 100644 --- a/module/plugins/hoster/FilerioCom.py +++ b/module/plugins/hoster/FilerioCom.py @@ -1,24 +1,25 @@  # -*- coding: utf-8 -*- -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo -class FilerioCom(XFileSharingPro): -    __name__ = "FilerioCom" -    __type__ = "hoster" +class FilerioCom(XFSHoster): +    __name__    = "FilerioCom" +    __type__    = "hoster" +    __version__ = "0.07" +      __pattern__ = r'http://(?:www\.)?(filerio\.(in|com)|filekeen\.com)/\w{12}' -    __version__ = "0.02" +      __description__ = """FileRio.in hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    HOSTER_NAME = "filerio.in" +    HOSTER_DOMAIN = "filerio.in" -    FILE_OFFLINE_PATTERN = '<b>"File Not Found"</b>|File has been removed due to Copyright Claim' -    FILE_URL_REPLACEMENTS = [(r'http://.*?/', 'http://filerio.in/')] +    URL_REPLACEMENTS = [(r'filekeen\.com', "filerio.in")] -    def setup(self): -        self.resumeDownload = self.multiDL = self.premium +    OFFLINE_PATTERN = r'>"File Not Found|File has been removed'  getInfo = create_getInfo(FilerioCom) diff --git a/module/plugins/hoster/FilesMailRu.py b/module/plugins/hoster/FilesMailRu.py index 34998726d..9b39d818f 100644 --- a/module/plugins/hoster/FilesMailRu.py +++ b/module/plugins/hoster/FilesMailRu.py @@ -1,8 +1,9 @@  # -*- coding: utf-8 -*-  import re -from module.plugins.Hoster import Hoster +  from module.network.RequestFactory import getURL +from module.plugins.Hoster import Hoster  from module.plugins.Plugin import chunks @@ -10,15 +11,15 @@ def getInfo(urls):      result = []      for chunk in chunks(urls, 10):          for url in chunk: -            src = getURL(url) -            if r'<div class="errorMessage mb10">' in src: +            html = getURL(url) +            if r'<div class="errorMessage mb10">' in html:                  result.append((url, 0, 1, url)) -            elif r'Page cannot be displayed' in src: +            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, src).group(0).split(', event)">')[1].split('</a>')[0] +                    file_name = re.search(url_pattern, html).group(0).split(', event)">')[1].split('</a>')[0]                      result.append((file_name, 0, 2, url))                  except:                      pass @@ -29,18 +30,22 @@ def getInfo(urls):  class FilesMailRu(Hoster): -    __name__ = "FilesMailRu" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?files\.mail\.ru/.*' +    __name__    = "FilesMailRu" +    __type__    = "hoster"      __version__ = "0.31" + +    __pattern__ = r'http://(?:www\.)?files\.mail\.ru/.+' +      __description__ = """Files.mail.ru hoster plugin""" -    __author_name__ = "oZiRiz" -    __author_mail__ = "ich@oziriz.de" +    __license__     = "GPLv3" +    __authors__     = [("oZiRiz", "ich@oziriz.de")] +      def setup(self):          if not self.account:              self.multiDL = False +      def process(self, pyfile):          self.html = self.load(pyfile.url)          self.url_pattern = '<a href="(.+?)" onclick="return Act\(this\, \'dlink\'\, event\)">(.+?)</a>' @@ -64,22 +69,23 @@ class FilesMailRu(Hoster):              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""" -        file_url = re.search(self.url_pattern, self.html).group(0).split('<a href="')[1].split('" onclick="return Act')[ -            0] -        return file_url +        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""" -        file_name = re.search(self.url_pattern, self.html).group(0).split(', event)">')[1].split('</a>')[0] -        return file_name +        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 @@ -87,7 +93,7 @@ class FilesMailRu(Hoster):          # 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 diff --git a/module/plugins/hoster/FileserveCom.py b/module/plugins/hoster/FileserveCom.py index 4c678c1b1..52a071b52 100644 --- a/module/plugins/hoster/FileserveCom.py +++ b/module/plugins/hoster/FileserveCom.py @@ -1,34 +1,21 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. -""" -  import re -from module.plugins.Hoster import Hoster + +from module.common.json_layer import json_loads  from module.network.RequestFactory import getURL +from module.plugins.Hoster import Hoster +from module.plugins.Plugin import chunks  from module.plugins.internal.CaptchaService import ReCaptcha -from module.common.json_layer import json_loads +from module.plugins.internal.SimpleHoster import secondsToMidnight  from module.utils import parseFileSize -from module.plugins.Plugin import chunks  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.DOTALL): +    for li in re.finditer(plugin.LINKCHECK_TR, html, re.S):          try:              cols = re.findall(plugin.LINKCHECK_TD, li.group(1))              if cols: @@ -44,34 +31,40 @@ def checkFile(plugin, urls):  class FileserveCom(Hoster): -    __name__ = "FileserveCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?fileserve\.com/file/(?P<id>[^/]+).*' -    __version__ = "0.51" +    __name__    = "FileserveCom" +    __type__    = "hoster" +    __version__ = "0.53" + +    __pattern__ = r'http://(?:www\.)?fileserve\.com/file/(?P<ID>[^/]+)' +      __description__ = """Fileserve.com hoster plugin""" -    __author_name__ = ("jeix", "mkaay", "Paul King", "zoidberg") -    __author_mail__ = ("jeix@hasnomail.de", "mkaay@mkaay.de", "", "zoidberg@mujmail.cz") +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("mkaay", "mkaay@mkaay.de"), +                       ("Paul King", None), +                       ("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>' +    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='(?P<key>[^']+)'" +    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 = "Your download link has expired" -    DAILY_LIMIT_PATTERN = "Your daily download limit has been reached" -    NOT_LOGGED_IN_PATTERN = '<form (name="loginDialogBoxForm"|id="login_form")|<li><a href="/login.php">Login</a></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>' -    # shares code with FilejungleCom and UploadstationCom      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.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: @@ -83,6 +76,7 @@ class FileserveCom(Hoster):          else:              self.handleFree() +      def handleFree(self):          self.html = self.load(self.url)          action = self.load(self.url, post={"checkDownload": "check"}, decode=True) @@ -90,34 +84,34 @@ class FileserveCom(Hoster):          self.logDebug(action)          if "fail" in action: -            if action["fail"] == "timeLimit": +            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") +            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"]) +                self.fail(_("Download check returned: %s") % action['fail'])          elif "success" in action: -            if action["success"] == "showCaptcha": +            if action['success'] == "showCaptcha":                  self.doCaptcha()                  self.doTimmer() -            elif action["success"] == "showTimmer": +            elif action['success'] == "showTimmer":                  self.doTimmer()          else: -            self.fail("Unknown server response") +            self.error(_("Unknown server response"))          # show download link -        response = self.load(self.url, post={"downloadLink": "show"}, decode=True) -        self.logDebug("show downloadLink response : %s" % response) -        if "fail" in response: -            self.fail("Couldn't retrieve download url") +        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"}) @@ -133,50 +127,51 @@ class FileserveCom(Hoster):          elif check == "wait":              self.doLongWait(self.lastCheck)          elif check == "limit": -            #download limited reached for today (not a exact time known) -            self.setWait(3 * 60 * 60, True)  # wait 3 hours #TO-DO: resolve waittime using UnrestrictLi's secondsToMidnight +            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): -        response = self.load(self.url, post={"downloadLink": "wait"}, decode=True) -        self.logDebug("wait response : %s" % response[:80]) +        res = self.load(self.url, post={"downloadLink": "wait"}, decode=True) +        self.logDebug("Wait response: %s" % res[:80]) -        if "fail" in response: -            self.fail("Failed getting wait time") +        if "fail" in res: +            self.fail(_("Failed getting wait time"))          if self.__name__ == "FilejungleCom": -            found = re.search(r'"waitTime":(\d+)', response) -            if not found: -                self.fail("Cannot get wait time") -            wait_time = int(found.group(1)) +            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(response) + 3 +            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("key") +        captcha_key = re.search(self.CAPTCHA_KEY_PATTERN, self.html).group(1)          recaptcha = ReCaptcha(self) -        for _ in xrange(5): -            challenge, code = recaptcha.challenge(captcha_key) - -            response = json_loads(self.load(self.URLS[2], -                                            post={'recaptcha_challenge_field': challenge, -                                                  'recaptcha_response_field': code, -                                                  'recaptcha_shortencode_field': self.file_id})) -            self.logDebug("reCaptcha response : %s" % response) -            if not response["success"]: +        for _i in xrange(5): +            challenge, response = 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") +            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 @@ -184,27 +179,28 @@ class FileserveCom(Hoster):          self.wait()          self.retry() +      def handlePremium(self):          premium_url = None          if self.__name__ == "FileserveCom":              #try api download -            response = 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 response: -                response = json_loads(response) -                if response['error_code'] == "302": -                    premium_url = response['next'] -                elif response['error_code'] in ["305", "500"]: +            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 response['error_code'] in ["403", "605"]: +                elif res['error_code'] in ["403", "605"]:                      self.resetAccount() -                elif response['error_code'] in ["606", "607", "608"]: +                elif res['error_code'] in ["606", "607", "608"]:                      self.offline()                  else: -                    self.logError(response['error_code'], response['error_message']) +                    self.logError(res['error_code'], res['error_message'])          self.download(premium_url or self.pyfile.url) @@ -213,7 +209,7 @@ class FileserveCom(Hoster):              if check == "login":                  self.account.relogin(self.user) -                self.retry(reason=_("Not logged in.")) +                self.retry(reason=_("Not logged in"))  def getInfo(urls): diff --git a/module/plugins/hoster/FileshareInUa.py b/module/plugins/hoster/FileshareInUa.py index db2b1a998..08e10dccb 100644 --- a/module/plugins/hoster/FileshareInUa.py +++ b/module/plugins/hoster/FileshareInUa.py @@ -1,79 +1,18 @@  # -*- coding: utf-8 -*- -import re -from module.plugins.Hoster import Hoster -from module.network.RequestFactory import getURL -from module.utils import parseFileSize +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo -class FileshareInUa(Hoster): -    __name__ = "FileshareInUa" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?fileshare.in.ua/[A-Za-z0-9]+' -    __version__ = "0.01" -    __description__ = """Fileshare.in.ua hoster plugin""" -    __author_name__ = "fwannmacher" -    __author_mail__ = "felipe@warhammerproject.com" - -    PATTERN_FILENAME = r'<h3 class="b-filename">(.*?)</h3>' -    PATTERN_FILESIZE = r'<b class="b-filesize">(.*?)</b>' -    PATTERN_OFFLINE = "This file doesn't exist, or has been removed." - -    def setup(self): -        self.resumeDownload = self.multiDL = True - -    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() - -        self.link = self._getLink() - -        if not self.link.startswith('http://'): -            self.link = "http://fileshare.in.ua" + self.link - -        self.download(self.link) +class FileshareInUa(DeadHoster): +    __name__    = "FileshareInUa" +    __type__    = "hoster" +    __version__ = "0.02" -    def _checkOnline(self): -        if re.search(self.PATTERN_OFFLINE, self.html): -            return False -        else: -            return True +    __pattern__ = r'https?://(?:www\.)?fileshare\.in\.ua/\w{7}' -    def _getName(self): -        name = re.search(self.PATTERN_FILENAME, self.html) -        if name is None: -            self.fail("%s: Plugin broken." % self.__name__) - -        return name.group(1) - -    def _getLink(self): -        return re.search("<a href=\"(/get/.+)\" class=\"b-button m-blue m-big\" >", self.html).group(1) - - -def getInfo(urls): -    result = [] - -    for url in urls: -        html = getURL(url) - -        if re.search(FileshareInUa.PATTERN_OFFLINE, html): -            result.append((url, 0, 1, url)) -        else: -            name = re.search(FileshareInUa.PATTERN_FILENAME, html) - -            if name is None: -                result.append((url, 0, 1, url)) -                continue - -            name = name.group(1) -            size = re.search(FileshareInUa.PATTERN_FILESIZE, html) -            size = parseFileSize(size.group(1)) +    __description__ = """Fileshare.in.ua hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("fwannmacher", "felipe@warhammerproject.com")] -            result.append((name, size, 3, url)) -    yield result +getInfo = create_getInfo(FileshareInUa) diff --git a/module/plugins/hoster/FilesonicCom.py b/module/plugins/hoster/FilesonicCom.py new file mode 100644 index 000000000..8bfa0fa2e --- /dev/null +++ b/module/plugins/hoster/FilesonicCom.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class FilesonicCom(DeadHoster): +    __name__    = "FilesonicCom" +    __type__    = "hoster" +    __version__ = "0.35" + +    __pattern__ = r'http://(?:www\.)?filesonic\.com/file/\w+' + +    __description__ = """Filesonic.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("paulking", None)] + + +getInfo = create_getInfo(FilesonicCom) diff --git a/module/plugins/hoster/FilezyNet.py b/module/plugins/hoster/FilezyNet.py index cd0902ab3..4197a2858 100644 --- a/module/plugins/hoster/FilezyNet.py +++ b/module/plugins/hoster/FilezyNet.py @@ -1,36 +1,18 @@  # -*- coding: utf-8 -*- -import re -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo -class FilezyNet(XFileSharingPro): -    __name__ = "FilezyNet" -    __type__ = "hoster" -    __version__ = "0.1" -    __pattern__ = r'http://(?:www\.)?filezy.net/.*/.*.html' -    __description__ = """Filezy.net hoster plugin""" - -    HOSTER_NAME = "filezy.net" - -    FILE_SIZE_PATTERN = r'<span class="plansize">(?P<S>[0-9.]+) (?P<U>[kKMG])i?B</span>' -    WAIT_PATTERN = r'<div id="countdown_str" class="seconds">\n<!--Wait--> <span id=".*?">(\d+)</span>' -    DOWNLOAD_JS_PATTERN = r"<script type='text/javascript'>eval(.*)" +class FilezyNet(DeadHoster): +    __name__    = "FilezyNet" +    __type__    = "hoster" +    __version__ = "0.20" -    def setup(self): -        self.resumeDownload = True -        self.multiDL = self.premium +    __pattern__ = r'http://(?:www\.)?filezy\.net/\w{12}' -    def getDownloadLink(self): -        self.logDebug("Getting download link") - -        data = self.getPostParameters() -        self.html = self.load(self.pyfile.url, post=data, ref=True, decode=True) - -        obfuscated_js = re.search(self.DOWNLOAD_JS_PATTERN, self.html) -        dl_file_now = self.js.eval(obfuscated_js.group(1)) -        link = re.search(self.DIRECT_LINK_PATTERN, dl_file_now) -        return link.group(1) +    __description__ = """Filezy.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = []  getInfo = create_getInfo(FilezyNet) diff --git a/module/plugins/hoster/FiredriveCom.py b/module/plugins/hoster/FiredriveCom.py new file mode 100644 index 000000000..0e3a4e847 --- /dev/null +++ b/module/plugins/hoster/FiredriveCom.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class FiredriveCom(DeadHoster): +    __name__    = "FiredriveCom" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'https?://(?:www\.)?(firedrive|putlocker)\.com/(mobile/)?(file|embed)/(?P<ID>\w+)' + +    __description__ = """Firedrive.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +getInfo = create_getInfo(FiredriveCom) diff --git a/module/plugins/hoster/FlyFilesNet.py b/module/plugins/hoster/FlyFilesNet.py index 3e0466a41..49705958d 100644 --- a/module/plugins/hoster/FlyFilesNet.py +++ b/module/plugins/hoster/FlyFilesNet.py @@ -1,24 +1,31 @@  # -*- coding: utf-8 -*-  import re -import urllib -from module.plugins.internal.SimpleHoster import SimpleHoster +from urllib import unquote +  from module.network.RequestFactory import getURL +from module.plugins.internal.SimpleHoster import SimpleHoster  class FlyFilesNet(SimpleHoster): -    __name__ = "FlyFilesNet" -    __version__ = "0.1" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?flyfiles\.net/.*' +    __name__    = "FlyFilesNet" +    __type__    = "hoster" +    __version__ = "0.10" + +    __pattern__ = r'http://(?:www\.)?flyfiles\.net/.+' + +    __description__ = """FlyFiles.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = []      SESSION_PATTERN = r'flyfiles\.net/(.*)/.*' -    FILE_NAME_PATTERN = r'flyfiles\.net/.*/(.*)' +    NAME_PATTERN = r'flyfiles\.net/.*/(.*)' +      def process(self, pyfile): -        pyfile.name = re.search(self.FILE_NAME_PATTERN, pyfile.url).group(1) -        pyfile.name = urllib.unquote_plus(pyfile.name) +        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) @@ -29,11 +36,10 @@ class FlyFilesNet(SimpleHoster):          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.logWarning(_("Could not get the download URL. Please wait 10 minutes"))              self.wait(10 * 60, True)              self.retry()          download_url = parsed_url.replace('#downlink|', '') -        self.logDebug("Download URL: %s" % download_url)          self.download(download_url) diff --git a/module/plugins/hoster/FourSharedCom.py b/module/plugins/hoster/FourSharedCom.py index 9c7752fe1..0406df0c4 100644 --- a/module/plugins/hoster/FourSharedCom.py +++ b/module/plugins/hoster/FourSharedCom.py @@ -6,45 +6,51 @@ from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class FourSharedCom(SimpleHoster): -    __name__ = "FourSharedCom" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?4shared(\-china)?\.com/(account/)?(download|get|file|document|photo|video|audio|mp3|office|rar|zip|archive|music)/.+?/.*' -    __version__ = "0.29" +    __name__    = "FourSharedCom" +    __type__    = "hoster" +    __version__ = "0.30" + +    __pattern__ = r'https?://(?:www\.)?4shared(\-china)?\.com/(account/)?(download|get|file|document|photo|video|audio|mp3|office|rar|zip|archive|music)/.+' +      __description__ = """4Shared.com hoster plugin""" -    __author_name__ = ("jeix", "zoidberg") -    __author_mail__ = ("jeix@hasnomail.de", "zoidberg@mujmail.cz") +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("zoidberg", "zoidberg@mujmail.cz")] + -    FILE_NAME_PATTERN = r'<meta name="title" content="(?P<N>.+?)"' -    FILE_SIZE_PATTERN = '<span title="Size: (?P<S>[0-9,.]+) (?P<U>[kKMG])i?B">' -    FILE_OFFLINE_PATTERN = 'The file link that you requested is not valid\.|This file was deleted.' -    FILE_NAME_REPLACEMENTS = [(r"&#(\d+).", lambda m: unichr(int(m.group(1))))] -    FILE_SIZE_REPLACEMENTS = [(",", "")] +    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 = [(",", "")] -    DOWNLOAD_BUTTON_PATTERN = 'id="btnLink" href="(.*?)"' -    FID_PATTERN = 'name="d3fid" value="(.*?)"'      DOWNLOAD_URL_PATTERN = r'name="d3link" value="(.*?)"' +    DOWNLOAD_BUTTON_PATTERN = r'id="btnLink" href="(.*?)"' +    FID_PATTERN = r'name="d3fid" value="(.*?)"' +      def handleFree(self):          if not self.account: -            self.fail("User not logged in") +            self.fail(_("User not logged in")) -        found = re.search(self.DOWNLOAD_BUTTON_PATTERN, self.html) -        if found: -            link = found.group(1) +        m = re.search(self.DOWNLOAD_BUTTON_PATTERN, self.html) +        if m: +            link = m.group(1)          else:              link = re.sub(r'/(download|get|file|document|photo|video|audio)/', r'/get/', self.pyfile.url)          self.html = self.load(link) -        found = re.search(self.DOWNLOAD_URL_PATTERN, self.html) -        if not found: -            self.parseError('Download link') -        link = found.group(1) +        m = re.search(self.DOWNLOAD_URL_PATTERN, self.html) +        if m is None: +            self.error(_("Download link")) +        link = m.group(1)          try: -            found = re.search(self.FID_PATTERN, self.html) -            response = self.load('http://www.4shared.com/web/d2/getFreeDownloadLimitInfo?fileId=%s' % found.group(1)) -            self.logDebug(response) +            m = re.search(self.FID_PATTERN, self.html) +            res = self.load('http://www.4shared.com/web/d2/getFreeDownloadLimitInfo?fileId=%s' % m.group(1)) +            self.logDebug(res)          except:              pass diff --git a/module/plugins/hoster/FreakshareCom.py b/module/plugins/hoster/FreakshareCom.py index ad9abeb96..2238f07ef 100644 --- a/module/plugins/hoster/FreakshareCom.py +++ b/module/plugins/hoster/FreakshareCom.py @@ -1,23 +1,32 @@  # -*- coding: utf-8 -*-  import re +  from module.plugins.Hoster import Hoster  from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import secondsToMidnight  class FreakshareCom(Hoster): -    __name__ = "FreakshareCom" -    __type__ = "hoster" +    __name__    = "FreakshareCom" +    __type__    = "hoster" +    __version__ = "0.40" +      __pattern__ = r'http://(?:www\.)?freakshare\.(net|com)/files/\S*?/' -    __version__ = "0.38" +      __description__ = """Freakshare.com hoster plugin""" -    __author_name__ = ("sitacuisses", "spoob", "mkaay", "Toilal") -    __author_mail__ = ("sitacuisses@yahoo.de", "spoob@pyload.org", "mkaay@mkaay.de", "toilal.dev@gmail.com") +    __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 @@ -41,24 +50,23 @@ class FreakshareCom(Hoster):                                          "downloadserver": "No Downloadserver. Please try again later!"})              if check == "bad": -                self.fail("Bad Try.") +                self.fail(_("Bad Try"))              elif check == "paralell":                  self.setWait(300, True)                  self.wait()                  self.retry()              elif check == "empty": -                self.fail("File not downloadable") +                self.fail(_("File not downloadable"))              elif check == "wrong_captcha":                  self.invalidCaptcha()                  self.retry()              elif check == "downloadserver": -                self.retry(5, 15 * 60, "No Download server") +                self.retry(5, 15 * 60, _("No Download server")) +      def prepare(self):          pyfile = self.pyfile -        self.wantReconnect = False -          self.download_html()          if not self.file_exists(): @@ -73,14 +81,16 @@ class FreakshareCom(Hoster):          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 self.html is None: +        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 @@ -89,8 +99,9 @@ class FreakshareCom(Hoster):          else:              self.offline() +      def get_file_name(self): -        if self.html is None: +        if not self.html:              self.download_html()          if not self.wantReconnect:              file_name = re.search(r"<h1\sclass=\"box_heading\"\sstyle=\"text-align:center;\">([^ ]+)", self.html) @@ -102,9 +113,10 @@ class FreakshareCom(Hoster):          else:              return self.pyfile.url +      def get_file_size(self):          size = 0 -        if self.html is None: +        if not self.html:              self.download_html()          if not self.wantReconnect:              file_size_check = re.search( @@ -116,30 +128,33 @@ class FreakshareCom(Hoster):          return size +      def get_waiting_time(self): -        if self.html is None: +        if not self.html:              self.download_html()          if "Your Traffic is used up for today" in self.html:              self.wantReconnect = True -            return 24 * 60 * 60 +            return secondsToMidnight(gmt=2) -        timestring = re.search('\s*var\s(?:downloadWait|time)\s=\s(\d*)[.\d]*;', self.html) +        timestring = re.search('\s*var\s(?:downloadWait|time)\s=\s(\d*)[\d.]*;', self.html)          if timestring: -            return int(timestring.group(1)) + 1  # add 1 sec as tenths of seconds are cut off +            return int(timestring.group(1))          else:              return 60 +      def file_exists(self):          """ returns True or False          """ -        if self.html is None: +        if not self.html:              self.download_html()          if re.search(r"This file does not exist!", self.html) is not None:              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 @@ -148,21 +163,14 @@ class FreakshareCom(Hoster):          herewego = self.load(self.pyfile.url, None, request_options)  # the actual download-Page -        # comment this in, when it doesnt work -        # with open("DUMP__FS_.HTML", "w") as fp: -        # fp.write(herewego) -          to_sort = re.findall(r"<input\stype=\".*?\"\svalue=\"(\S*?)\".*?name=\"(\S*?)\"\s.*?\/>", herewego)          request_options = dict((n, v) for (v, n) in to_sort) -        # comment this in, when it doesnt work as well -        #print "\n\n%s\n\n" % ";".join(["%s=%s" % x for x in to_sort]) - -        challenge = re.search(r"http://api\.recaptcha\.net/challenge\?k=([0-9A-Za-z]+)", herewego) +        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)) +            (request_options['recaptcha_challenge_field'], +             request_options['recaptcha_response_field']) = re_captcha.challenge(challenge.group(1))          return request_options diff --git a/module/plugins/hoster/FreeWayMe.py b/module/plugins/hoster/FreeWayMe.py index d72b12a12..80d0b8515 100644 --- a/module/plugins/hoster/FreeWayMe.py +++ b/module/plugins/hoster/FreeWayMe.py @@ -1,49 +1,34 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. +class FreeWayMe(MultiHoster): +    __name__    = "FreeWayMe" +    __type__    = "hoster" +    __version__ = "0.14" -    @author: Nicolas Giese -""" +    __pattern__ = r'https://(?:www\.)?free-way\.me/.+' -from module.plugins.Hoster import Hoster - - -class FreeWayMe(Hoster): -    __name__ = "FreeWayMe" -    __version__ = "0.11" -    __type__ = "hoster" -    __pattern__ = r'https://(?:www\.)?free-way.me/.*'      __description__ = """FreeWayMe hoster plugin""" -    __author_name__ = "Nicolas Giese" -    __author_mail__ = "james@free-way.me" +    __license__     = "GPLv3" +    __authors__     = [("Nicolas Giese", "james@free-way.me")] +      def setup(self):          self.resumeDownload = False -        self.chunkLimit = 1 -        self.multiDL = self.premium +        self.multiDL        = self.premium +        self.chunkLimit     = 1 -    def process(self, pyfile): -        if not self.account: -            self.logError(_("Please enter your %s account or deactivate this plugin") % "FreeWayMe") -            self.fail("No FreeWay account provided") -        self.logDebug("Old URL: %s" % pyfile.url) - -        (user, data) = self.account.selectAccount() +    def handlePremium(self): +        user, data = self.account.selectAccount() +        self.link = True          self.download(              "https://www.free-way.me/load.php", -            get={"multiget": 7, "url": pyfile.url, "user": user, "pw": self.account.getpw(user), "json": ""}, +            get={"multiget": 7, "url": self.pyfile.url, "user": user, "pw": self.account.getpw(user), "json": ""},              disposition=True) + + +getInfo = create_getInfo(FreeWayMe) diff --git a/module/plugins/hoster/FreevideoCz.py b/module/plugins/hoster/FreevideoCz.py index eebb43b48..e56d1a299 100644 --- a/module/plugins/hoster/FreevideoCz.py +++ b/module/plugins/hoster/FreevideoCz.py @@ -1,68 +1,18 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. +class FreevideoCz(DeadHoster): +    __name__    = "FreevideoCz" +    __type__    = "hoster" +    __version__ = "0.30" -    @author: zoidberg -""" +    __pattern__ = r'http://(?:www\.)?freevideo\.cz/vase-videa/.+' -import re -from module.plugins.Hoster import Hoster -from module.network.RequestFactory import getURL - - -def getInfo(urls): -    result = [] - -    for url in urls: - -        html = getURL(url) -        if re.search(FreevideoCz.FILE_OFFLINE_PATTERN, html): -            # File offline -            result.append((url, 0, 1, url)) -        else: -            result.append((url, 0, 2, url)) -    yield result - - -class FreevideoCz(Hoster): -    __name__ = "FreevideoCz" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?freevideo.cz/vase-videa/(.*)\.html' -    __version__ = "0.2"      __description__ = """Freevideo.cz hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" - -    URL_PATTERN = r'clip: {\s*url: "([^"]+)"' -    FILE_OFFLINE_PATTERN = r'<h2 class="red-corner-full">Str.nka nebyla nalezena</h2>' - -    def setup(self): -        self.multiDL = self.resumeDownload = True - -    def process(self, pyfile): - -        self.html = self.load(pyfile.url, decode=True) - -        if re.search(self.FILE_OFFLINE_PATTERN, self.html): -            self.offline() - -        found = re.search(self.URL_PATTERN, self.html) -        if found is None: -            self.fail("Parse error (URL)") -        download_url = found.group(1) +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] -        pyfile.name = re.match(self.__pattern__, pyfile.url).group(1) + ".mp4" -        self.download(download_url) +getInfo = create_getInfo(FreevideoCz)
\ No newline at end of file diff --git a/module/plugins/hoster/FshareVn.py b/module/plugins/hoster/FshareVn.py index f9a9b6c16..a3f9703df 100644 --- a/module/plugins/hoster/FshareVn.py +++ b/module/plugins/hoster/FshareVn.py @@ -1,22 +1,20 @@  # -*- coding: utf-8 -*-  import re +  from time import strptime, mktime, gmtime -from module.plugins.internal.SimpleHoster import SimpleHoster, parseFileInfo  from module.network.RequestFactory import getURL +from module.plugins.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) - -        file_info = parseFileInfo(FshareVn, url, html) +        html = getURL("http://www.fshare.vn/check_link.php", +                      post={'action': "check_link", 'arrlinks': url}, +                      decode=True) -        yield file_info +        yield parseFileInfo(FshareVn, url, html)  def doubleDecode(m): @@ -24,21 +22,26 @@ def doubleDecode(m):  class FshareVn(SimpleHoster): -    __name__ = "FshareVn" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?fshare.vn/file/.*' -    __version__ = "0.16" +    __name__    = "FshareVn" +    __type__    = "hoster" +    __version__ = "0.18" + +    __pattern__ = r'http://(?:www\.)?fshare\.vn/file/.+' +      __description__ = """FshareVn hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" - -    FILE_INFO_PATTERN = r'<p>(?P<N>[^<]+)<\\/p>[\\trn\s]*<p>(?P<S>[0-9,.]+)\s*(?P<U>[kKMG])i?B<\\/p>' -    FILE_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>' -    FILE_NAME_REPLACEMENTS = [("(.*)", doubleDecode)] -    DOWNLOAD_URL_PATTERN = r'action="(http://download.*?)[#"]' -    VIP_URL_PATTERN = r'<form action="([^>]+)" method="get" name="frm_download">' +    __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_PATTERN = r'action="(http://download.*?)[#"]'      WAIT_PATTERN = ur'Lượt tải xuá»ng kế tiếp là:\s*(.*?)\s*<' +      def process(self, pyfile):          self.html = self.load('http://www.fshare.vn/check_link.php', post={              "action": "check_link", @@ -52,6 +55,7 @@ class FshareVn(SimpleHoster):              self.handleFree()          self.checkDownloadedFile() +      def handleFree(self):          self.html = self.load(self.pyfile.url, decode=True) @@ -61,49 +65,60 @@ class FshareVn(SimpleHoster):          self.url = self.pyfile.url + action          if not inputs: -            self.parseError('FORM') +            self.error(_("No FORM")) +          elif 'link_file_pwd_dl' in inputs: -            for password in self.getPassword().splitlines(): -                self.logInfo('Password protected link, trying "%s"' % password) +            password = self.getPassword() + +            if password: +                self.logInfo(_("Password protected link, trying ") + password)                  inputs['link_file_pwd_dl'] = password                  self.html = self.load(self.url, post=inputs, decode=True) -                if not 'name="link_file_pwd_dl"' in self.html: -                    break + +                if 'name="link_file_pwd_dl"' in self.html: +                    self.fail(_("Incorrect password"))              else: -                self.fail("No or incorrect password") +                self.fail(_("No password found")) +          else:              self.html = self.load(self.url, post=inputs, decode=True)          self.checkErrors() -        found = re.search(r'var count = (\d+)', self.html) -        self.setWait(int(found.group(1)) if found else 30) +        m = re.search(r'var count = (\d+)', self.html) +        self.setWait(int(m.group(1)) if m else 30) -        found = re.search(self.DOWNLOAD_URL_PATTERN, self.html) -        if not found: -            self.parseError('FREE DL URL') -        self.url = found.group(1) +        m = re.search(self.LINK_PATTERN, self.html) +        if m is None: +            self.error(_("LINK_PATTERN not found")) +        self.url = m.group(1)          self.logDebug("FREE DL URL: %s" % self.url)          self.wait()          self.download(self.url) +      def handlePremium(self):          self.download(self.pyfile.url) +      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() -        found = re.search(self.WAIT_PATTERN, self.html) -        if found: -            self.logInfo("Wait until %s ICT" % found.group(1)) -            wait_until = mktime(strptime(found.group(1), "%d/%m/%Y %H:%M")) +        m = re.search(self.WAIT_PATTERN, self.html) +        if m: +            self.logInfo(_("Wait until %s ICT") % m.group(1)) +            wait_until = mktime(strptime(m.group(1), "%d/%m/%Y %H:%M"))              self.wait(wait_until - mktime(gmtime()) - 7 * 60 * 60, True)              self.retry()          elif '<ul class="message-error">' in self.html: -            self.logError("Unknown error occured or wait time not parsed") -            self.retry(30, 2 * 60, "Unknown error") +            msg = "Unknown error occured or wait time not parsed" +            self.logError(msg) +            self.retry(30, 2 * 60, msg) + +        self.info.pop('error', None) +      def checkDownloadedFile(self):          # check download @@ -112,4 +127,4 @@ class FshareVn(SimpleHoster):          })          if check == "not_found": -            self.fail("File not found on server") +            self.fail(_("File not m on server")) diff --git a/module/plugins/hoster/Ftp.py b/module/plugins/hoster/Ftp.py index af9191a4d..c6ad68e49 100644 --- a/module/plugins/hoster/Ftp.py +++ b/module/plugins/hoster/Ftp.py @@ -1,43 +1,33 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: jeix -    @author: mkaay -""" -from urlparse import urlparse -from urllib import quote, unquote  import pycurl  import re +from urllib import quote, unquote +from urlparse import urlparse +  from module.plugins.Hoster import Hoster  class Ftp(Hoster): -    __name__ = "Ftp" -    __version__ = "0.41" -    __pattern__ = r'(ftps?|sftp)://(.*?:.*?@)?.*?/.*'  # ftp://user:password@ftp.server.org/path/to/file -    __type__ = "hoster" +    __name__    = "Ftp" +    __type__    = "hoster" +    __version__ = "0.44" + +    __pattern__ = r'(?:ftps?|sftp)://([\w.-]+(:[\w.-]+)?@)?[\w.-]+(:\d+)?/.+' +      __description__ = """Download from ftp directory""" -    __author_name__ = ("jeix", "mkaay", "zoidberg") -    __author_mail__ = ("jeix@hasnomail.com", "mkaay@mkaay.de", "zoidberg@mujmail.cz") +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.com"), +                       ("mkaay", "mkaay@mkaay.de"), +                       ("zoidberg", "zoidberg@mujmail.cz")] +      def setup(self):          self.chunkLimit = -1          self.resumeDownload = True +      def process(self, pyfile):          parsed_url = urlparse(pyfile.url)          netloc = parsed_url.netloc @@ -53,38 +43,36 @@ class Ftp(Hoster):              if netloc in servers:                  self.logDebug("Logging on to %s" % netloc) -                self.req.addAuth(self.account.accounts[netloc]["password"]) +                self.req.addAuth(self.account.accounts[netloc]['password'])              else: -                for pwd in pyfile.package().password.splitlines(): -                    if ":" in pwd: -                        self.req.addAuth(pwd.strip()) -                        break +                pwd = self.getPassword() +                if ':' in pwd: +                    self.req.addAuth(pwd)          self.req.http.c.setopt(pycurl.NOBODY, 1)          try: -            response = self.load(pyfile.url) +            res = self.load(pyfile.url)          except pycurl.error, e: -            self.fail("Error %d: %s" % e.args) +            self.fail(_("Error %d: %s") % e.args)          self.req.http.c.setopt(pycurl.NOBODY, 0)          self.logDebug(self.req.http.header) -        found = re.search(r"Content-Length:\s*(\d+)", response) -        if found: -            pyfile.size = int(found.group(1)) +        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           +            #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])) +                pkgname = "/".join(pyfile.package().name, urlparse(pyfile.url).path.rpartition('/')[2])                  pyfile.url += '/'                  self.req.http.c.setopt(48, 1)  # CURLOPT_DIRLISTONLY -                response = self.load(pyfile.url, decode=False) -                links = [pyfile.url + quote(x) for x in response.splitlines()] +                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, 1) -                #self.core.files.addLinks(links, pyfile.package().id) +                self.core.api.addPackage(pkgname, links)              else: -                self.fail("Unexpected server response") +                self.fail(_("Unexpected server response")) diff --git a/module/plugins/hoster/GamefrontCom.py b/module/plugins/hoster/GamefrontCom.py index 0cd54d2ea..c68866f87 100644 --- a/module/plugins/hoster/GamefrontCom.py +++ b/module/plugins/hoster/GamefrontCom.py @@ -1,27 +1,34 @@  # -*- coding: utf-8 -*-  import re -from module.plugins.Hoster import Hoster +  from module.network.RequestFactory import getURL +from module.plugins.Hoster import Hoster  from module.utils import parseFileSize  class GamefrontCom(Hoster): -    __name__ = "GamefrontCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?gamefront.com/files/[A-Za-z0-9]+' +    __name__    = "GamefrontCom" +    __type__    = "hoster"      __version__ = "0.04" + +    __pattern__ = r'http://(?:www\.)?gamefront\.com/files/\w+' +      __description__ = """Gamefront.com hoster plugin""" -    __author_name__ = "fwannmacher" -    __author_mail__ = "felipe@warhammerproject.com" +    __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 = "This file doesn't exist, or has been removed." +    PATTERN_OFFLINE = r'This file doesn\'t exist, or has been removed.' +      def setup(self): -        self.resumeDownload = self.multiDL = True -        self.chunkLimit = -1 +        self.resumeDownload = True +        self.multiDL        = True +        self.chunkLimit     = -1 +      def process(self, pyfile):          self.pyfile = pyfile @@ -32,12 +39,13 @@ class GamefrontCom(Hoster):          pyfile.name = self._getName() -        self.link = self._getLink() +        link = self._getLink() + +        if not link.startswith('http://'): +            link = "http://www.gamefront.com/" + link -        if not self.link.startswith('http://'): -            self.link = "http://www.gamefront.com/" + self.link +        self.download(link) -        self.download(self.link)      def _checkOnline(self):          if re.search(self.PATTERN_OFFLINE, self.html): @@ -45,19 +53,19 @@ class GamefrontCom(Hoster):          else:              return True +      def _getName(self):          name = re.search(self.PATTERN_FILENAME, self.html)          if name is None: -            self.fail("%s: Plugin broken." % self.__name__) +            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=[A-Za-z0-9]+)", +        self.html2 = self.load("http://www.gamefront.com/" + re.search("(files/service/thankyou\\?id=\w+)",                                                                         self.html).group(1)) -        self.link = re.search("<a href=\"(http://media[0-9]+\.gamefront.com/.*)\">click here</a>", self.html2) - -        return self.link.group(1).replace("&", "&") +        return re.search("<a href=\"(http://media\d+\.gamefront.com/.*)\">click here</a>", self.html2).group(1).replace("&", "&")  def getInfo(urls): @@ -70,15 +78,13 @@ def getInfo(urls):              result.append((url, 0, 1, url))          else:              name = re.search(GamefrontCom.PATTERN_FILENAME, html) -              if name is None:                  result.append((url, 0, 1, url)) -                continue - -            name = name.group(1) -            size = re.search(GamefrontCom.PATTERN_FILESIZE, html) -            size = parseFileSize(size.group(1)) +            else: +                name = name.group(1) +                size = re.search(GamefrontCom.PATTERN_FILESIZE, html) +                size = parseFileSize(size.group(1)) -            result.append((name, size, 3, url)) +                result.append((name, size, 3, url))      yield result diff --git a/module/plugins/hoster/GigapetaCom.py b/module/plugins/hoster/GigapetaCom.py index 1282c996d..37af7f216 100644 --- a/module/plugins/hoster/GigapetaCom.py +++ b/module/plugins/hoster/GigapetaCom.py @@ -1,42 +1,31 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re -from random import randint +  from pycurl import FOLLOWLOCATION +from random import randint  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class GigapetaCom(SimpleHoster): -    __name__ = "GigapetaCom" -    __type__ = "hoster" +    __name__    = "GigapetaCom" +    __type__    = "hoster" +    __version__ = "0.02" +      __pattern__ = r'http://(?:www\.)?gigapeta\.com/dl/\w+' -    __version__ = "0.01" +      __description__ = """GigaPeta.com hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __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")] -    SH_COOKIES = [("http://gigapeta.com", "lang", "us")] -    FILE_NAME_PATTERN = r'<img src=".*" alt="file" />-->\s*(?P<N>.*?)\s*</td>' -    FILE_SIZE_PATTERN = r'<th>\s*Size\s*</th>\s*<td>\s*(?P<S>.*?)\s*</td>' -    FILE_OFFLINE_PATTERN = r'<div id="page_error">'      def handleFree(self):          captcha_key = str(randint(1, 100000000)) @@ -44,7 +33,7 @@ class GigapetaCom(SimpleHoster):          self.req.http.c.setopt(FOLLOWLOCATION, 0) -        for _ in xrange(5): +        for _i in xrange(5):              self.checkErrors()              captcha = self.decryptCaptcha(captcha_url) @@ -53,24 +42,26 @@ class GigapetaCom(SimpleHoster):                  "captcha": captcha,                  "download": "Download"}) -            found = re.search(r"Location\s*:\s*(.*)", self.req.http.header, re.I) -            if found: -                download_url = found.group(1) +            m = re.search(r'Location\s*:\s*(.+)', self.req.http.header, re.I) +            if m: +                download_url = 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") +            self.fail(_("No valid captcha code entered"))          self.req.http.c.setopt(FOLLOWLOCATION, 1) -        self.logDebug("Download URL: %s" % download_url)          self.download(download_url) +      def checkErrors(self):          if "All threads for IP" in self.html: -            self.logDebug("Your IP is already downloading a file - wait and retry") +            self.logDebug("Your IP is already downloading a file")              self.wait(5 * 60, True)              self.retry() +        self.info.pop('error', None) +  getInfo = create_getInfo(GigapetaCom) diff --git a/module/plugins/hoster/GooIm.py b/module/plugins/hoster/GooIm.py index 1f77b7fe0..436d825a9 100644 --- a/module/plugins/hoster/GooIm.py +++ b/module/plugins/hoster/GooIm.py @@ -1,18 +1,7 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ +# +# Test links: +# https://goo.im/devs/liquidsmooth/3.x/codina/Nightly/LS-KK-v3.2-2014-08-01-codina.zip  import re @@ -20,34 +9,31 @@ from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class GooIm(SimpleHoster): -    __name__ = "GooIm" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?goo\.im/.+' -    __version__ = "0.02" +    __name__    = "GooIm" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'https?://(?:www\.)?goo\.im/.+' +      __description__ = """Goo.im hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __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' -    FILE_NAME_PATTERN = r'<h3>Filename: (?P<N>.+)</h3>' -    FILE_OFFLINE_PATTERN = r'The file you requested was not found'      def setup(self): -        self.chunkLimit = -1 -        self.multiDL = self.resumeDownload = True +        self.resumeDownload = True +        self.multiDL        = True +      def handleFree(self): -        self.html = self.load(self.pyfile.url) -        m = re.search(r'MD5sum: (?P<MD5>[0-9a-z]{32})</h3>', self.html) -        if m: -            self.check_data = {"md5": m.group('MD5')} +        url = self.pyfile.url +        self.html = self.load(url, cookies=True)          self.wait(10) - -        header = self.load(self.pyfile.url, just_header=True) -        if header['location']: -            self.logDebug("Direct link: " + header['location']) -            self.download(header['location']) -        else: -            self.parseError("Unable to detect direct download link") +        self.download(url, cookies=True)  getInfo = create_getInfo(GooIm) diff --git a/module/plugins/hoster/HellshareCz.py b/module/plugins/hoster/HellshareCz.py index 91e51e314..4b44cc335 100644 --- a/module/plugins/hoster/HellshareCz.py +++ b/module/plugins/hoster/HellshareCz.py @@ -1,59 +1,47 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re +  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class HellshareCz(SimpleHoster): -    __name__ = "HellshareCz" -    __type__ = "hoster" -    __pattern__ = r'(http://(?:www\.)?hellshare\.(?:cz|com|sk|hu|pl)/[^?]*/\d+).*' -    __version__ = "0.82" +    __name__    = "HellshareCz" +    __type__    = "hoster" +    __version__ = "0.83" + +    __pattern__ = r'(http://(?:www\.)?hellshare\.(?:cz|com|sk|hu|pl)/[^?]*/\d+)' +      __description__ = """Hellshare.cz hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    FILE_NAME_PATTERN = r'<h1 id="filename"[^>]*>(?P<N>[^<]+)</h1>' -    FILE_SIZE_PATTERN = r'<strong id="FileSize_master">(?P<S>[0-9.]*) (?P<U>[kKMG])i?B</strong>' -    FILE_OFFLINE_PATTERN = r'<h1>File not found.</h1>' +    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>'      SHOW_WINDOW_PATTERN = r'<a href="([^?]+/(\d+)/\?do=(fileDownloadButton|relatedFileDownloadButton-\2)-showDownloadWindow)"' +      def setup(self):          self.resumeDownload = self.multiDL = True if self.account else False          self.chunkLimit = 1 +      def process(self, pyfile):          if not self.account: -            self.fail("User not logged in") +            self.fail(_("User not logged in"))          pyfile.url = re.match(self.__pattern__, pyfile.url).group(1)          self.html = self.load(pyfile.url, decode=True)          self.getFileInfo()          if not self.checkTrafficLeft(): -            self.fail("Not enough traffic left for user %s." % self.user) +            self.fail(_("Not enough traffic left for user ") + self.user) -        found = re.search(self.SHOW_WINDOW_PATTERN, self.html) -        if not found: -            self.parseError('SHOW WINDOW') -        self.url = "http://www.hellshare.com" + found.group(1) -        self.logDebug("DOWNLOAD URL: " + self.url) +        m = re.search(self.SHOW_WINDOW_PATTERN, self.html) +        if m is None: +            self.error(_("SHOW_WINDOW_PATTERN not found")) +        self.url = "http://www.hellshare.com" + m.group(1)          self.download(self.url) diff --git a/module/plugins/hoster/HellspyCz.py b/module/plugins/hoster/HellspyCz.py index e4ddce514..2b9b76b8a 100644 --- a/module/plugins/hoster/HellspyCz.py +++ b/module/plugins/hoster/HellspyCz.py @@ -1,33 +1,18 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class HellspyCz(DeadHoster): -    __name__ = "HellspyCz" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?(?:hellspy\.(?:cz|com|sk|hu|pl)|sciagaj.pl)(/\S+/\d+)/?.*' +    __name__    = "HellspyCz" +    __type__    = "hoster"      __version__ = "0.28" + +    __pattern__ = r'http://(?:www\.)?(?:hellspy\.(?:cz|com|sk|hu|pl)|sciagaj\.pl)(/\S+/\d+)' +      __description__ = """HellSpy.cz hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")]  getInfo = create_getInfo(HellspyCz) diff --git a/module/plugins/hoster/HotfileCom.py b/module/plugins/hoster/HotfileCom.py index 9a0947f2c..f7724faf2 100644 --- a/module/plugins/hoster/HotfileCom.py +++ b/module/plugins/hoster/HotfileCom.py @@ -4,13 +4,18 @@ from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class HotfileCom(DeadHoster): -    __name__ = "HotfileCom" -    __type__ = "hoster" -    __pattern__ = r'https?://(www.)?hotfile\.com/dl/\d+/[0-9a-zA-Z]+/' +    __name__    = "HotfileCom" +    __type__    = "hoster"      __version__ = "0.37" + +    __pattern__ = r'https?://(?:www\.)?hotfile\.com/dl/\d+/\w+' +      __description__ = """Hotfile.com hoster plugin""" -    __author_name__ = ("sitacuisses", "spoob", "mkaay", "JoKoT3") -    __author_mail__ = ("sitacuisses@yhoo.de", "spoob@pyload.org", "mkaay@mkaay.de", "jokot3@gmail.com") +    __license__     = "GPLv3" +    __authors__     = [("sitacuisses", "sitacuisses@yhoo.de"), +                       ("spoob", "spoob@pyload.org"), +                       ("mkaay", "mkaay@mkaay.de"), +                       ("JoKoT3", "jokot3@gmail.com")]  getInfo = create_getInfo(HotfileCom) diff --git a/module/plugins/hoster/HugefilesNet.py b/module/plugins/hoster/HugefilesNet.py index 2375c75f8..f7221f8c5 100644 --- a/module/plugins/hoster/HugefilesNet.py +++ b/module/plugins/hoster/HugefilesNet.py @@ -1,37 +1,27 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -# Test links (random.bin): -# http://hugefiles.net/prthf9ya4w6s - -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo - - -class HugefilesNet(XFileSharingPro): -    __name__ = "HugefilesNet" -    __type__ = "hoster" + +import re + +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo + + +class HugefilesNet(XFSHoster): +    __name__    = "HugefilesNet" +    __type__    = "hoster" +    __version__ = "0.05" +      __pattern__ = r'http://(?:www\.)?hugefiles\.net/\w{12}' -    __version__ = "0.01" +      __description__ = """Hugefiles.net hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    HOSTER_DOMAIN = "hugefiles.net" -    HOSTER_NAME = "hugefiles.net" +    SIZE_PATTERN = r'File Size:</span>\s*<span[^>]*>(?P<S>[^<]+)</span></div>' -    FILE_SIZE_PATTERN = r'File Size:</span>\s*<span[^>]*>(?P<S>[^<]+)</span></div>' +    FORM_INPUTS_MAP = {'ctype': re.compile(r'\d+')}  getInfo = create_getInfo(HugefilesNet) diff --git a/module/plugins/hoster/HundredEightyUploadCom.py b/module/plugins/hoster/HundredEightyUploadCom.py index 6e5ffac30..317a49caf 100644 --- a/module/plugins/hoster/HundredEightyUploadCom.py +++ b/module/plugins/hoster/HundredEightyUploadCom.py @@ -1,38 +1,24 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -# Test links (random.bin): +# +# Test links:  # http://180upload.com/js9qdm6kjnrs -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo -class HundredEightyUploadCom(XFileSharingPro): -    __name__ = "HundredEightyUploadCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?180upload\.com/(\w+).*' -    __version__ = "0.01" +class HundredEightyUploadCom(XFSHoster): +    __name__    = "HundredEightyUploadCom" +    __type__    = "hoster" +    __version__ = "0.04" + +    __pattern__ = r'http://(?:www\.)?180upload\.com/\w{12}' +      __description__ = """180upload.com hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] -    HOSTER_NAME = "180upload.com" -    FILE_NAME_PATTERN = r'Filename:</b></td><td nowrap>(?P<N>.+)</td></tr>-->' -    FILE_SIZE_PATTERN = r'Size:</b></td><td>(?P<S>[\d.]+) (?P<U>[A-Z]+)\s*<small>' +    HOSTER_DOMAIN = "180upload.com"  getInfo = create_getInfo(HundredEightyUploadCom) diff --git a/module/plugins/hoster/IFileWs.py b/module/plugins/hoster/IFileWs.py index 5b0adaa97..b4f225c4b 100644 --- a/module/plugins/hoster/IFileWs.py +++ b/module/plugins/hoster/IFileWs.py @@ -1,22 +1,18 @@  # -*- coding: utf-8 -*- -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo -class IFileWs(XFileSharingPro): -    __name__ = "IFileWs" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?ifile\.ws/\w+(/.+)?' -    __version__ = "0.01" -    __description__ = """Ifile.ws hoster plugin""" -    __author_name__ = "z00nx" -    __author_mail__ = "z00nx0@gmail.com" +class IFileWs(DeadHoster): +    __name__    = "IFileWs" +    __type__    = "hoster" +    __version__ = "0.02" -    HOSTER_NAME = "ifile.ws" +    __pattern__ = r'http://(?:www\.)?ifile\.ws/\w{12}' -    FILE_INFO_PATTERN = '<h1\s+style="display:inline;">(?P<N>[^<]+)</h1>\s+\[(?P<S>[^]]+)\]' -    FILE_OFFLINE_PATTERN = 'File Not Found|The file was removed by administrator' -    LONG_WAIT_PATTERN = "(?P<M>\d(?=\s+minutes)).*(?P<S>\d+(?=\s+seconds))" +    __description__ = """Ifile.ws hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("z00nx", "z00nx0@gmail.com")]  getInfo = create_getInfo(IFileWs) diff --git a/module/plugins/hoster/IcyFilesCom.py b/module/plugins/hoster/IcyFilesCom.py index 82d760d01..921b64207 100644 --- a/module/plugins/hoster/IcyFilesCom.py +++ b/module/plugins/hoster/IcyFilesCom.py @@ -1,33 +1,18 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: godofdream -""" -  from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class IcyFilesCom(DeadHoster): -    __name__ = "IcyFilesCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?icyfiles\.com/(.*)' +    __name__    = "IcyFilesCom" +    __type__    = "hoster"      __version__ = "0.06" + +    __pattern__ = r'http://(?:www\.)?icyfiles\.com/(.+)' +      __description__ = """IcyFiles.com hoster plugin""" -    __author_name__ = "godofdream" -    __author_mail__ = "soilfiction@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("godofdream", "soilfiction@gmail.com")]  getInfo = create_getInfo(IcyFilesCom) diff --git a/module/plugins/hoster/IfileIt.py b/module/plugins/hoster/IfileIt.py index 6c754624b..2dae4cd80 100644 --- a/module/plugins/hoster/IfileIt.py +++ b/module/plugins/hoster/IfileIt.py @@ -1,77 +1,67 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo +  from module.common.json_layer import json_loads  from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class IfileIt(SimpleHoster): -    __name__ = "IfileIt" -    __type__ = "hoster" +    __name__    = "IfileIt" +    __type__    = "hoster" +    __version__ = "0.28" +      __pattern__ = r'^unmatchable$' -    __version__ = "0.27" +      __description__ = """Ifile.it""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" - -    #EVAL_PATTERN = r'(eval\(function\(p,a,c,k,e,d\).*)' -    #DEC_PATTERN = r"requestBtn_clickEvent[^}]*url:\s*([^,]+)" -    DOWNLOAD_LINK_PATTERN = r'</span> If it doesn\'t, <a target="_blank" href="([^"]+)">' -    RECAPTCHA_KEY_PATTERN = r"var __recaptcha_public\s*=\s*'([^']+)';" -    FILE_INFO_PATTERN = r'<span style="cursor: default;[^>]*>\s*(?P<N>.*?)\s* \s*<strong>\s*(?P<S>[0-9.]+)\s*(?P<U>[kKMG])i?B\s*</strong>\s*</span>' -    FILE_OFFLINE_PATTERN = r'<span style="cursor: default;[^>]*>\s* \s*<strong>\s*</strong>\s*</span>' +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    LINK_PATTERN = r'</span> If it doesn\'t, <a target="_blank" href="([^"]+)">' +    RECAPTCHA_PATTERN = r'var __recaptcha_public\s*=\s*\'(.+?)\'' +    INFO_PATTERN = r'<span style="cursor: default;[^>]*>\s*(?P<N>.*?)\s* \s*<strong>\s*(?P<S>[\d.,]+)\s*(?P<U>[\w^_]+)\s*</strong>\s*</span>' +    OFFLINE_PATTERN = r'<span style="cursor: default;[^>]*>\s* \s*<strong>\s*</strong>\s*</span>'      TEMP_OFFLINE_PATTERN = r'<span class="msg_red">Downloading of this file is temporarily disabled</span>' +      def handleFree(self): -        ukey = re.match(self.__pattern__, self.pyfile.url).group(1) -        json_url = 'http://ifile.it/new_download-request.json' +        ukey      = re.match(self.__pattern__, self.pyfile.url).group(1) +        json_url  = 'http://ifile.it/new_download-request.json'          post_data = {"ukey": ukey, "ab": "0"} +        res       = json_loads(self.load(json_url, post=post_data)) + +        self.logDebug(res) -        json_response = json_loads(self.load(json_url, post=post_data)) -        self.logDebug(json_response) -        if json_response['status'] == 3: +        if res['status'] == 3:              self.offline() -        if json_response["captcha"]: -            captcha_key = re.search(self.RECAPTCHA_KEY_PATTERN, self.html).group(1) +        if res['captcha']: +            captcha_key = re.search(self.RECAPTCHA_PATTERN, self.html).group(1) +              recaptcha = ReCaptcha(self) -            post_data["ctype"] = "recaptcha" +            post_data['ctype'] = "recaptcha" -            for _ in xrange(5): -                post_data["recaptcha_challenge"], post_data["recaptcha_response"] = recaptcha.challenge(captcha_key) -                json_response = json_loads(self.load(json_url, post=post_data)) -                self.logDebug(json_response) +            for _i in xrange(5): +                challenge, response = recaptcha.challenge(captcha_key) +                post_data.update({'recaptcha_challenge': challenge, +                                  'recaptcha_response' : response}) +                res = json_loads(self.load(json_url, post=post_data)) +                self.logDebug(res) -                if json_response["retry"]: +                if res['retry']:                      self.invalidCaptcha()                  else:                      self.correctCaptcha()                      break              else: -                self.fail("Incorrect captcha") +                self.fail(_("Incorrect captcha")) -        if not "ticket_url" in json_response: -            self.parseError("Download URL") +        if not "ticket_url" in res: +            self.error(_("No download URL")) -        self.download(json_response["ticket_url"]) +        self.download(res['ticket_url'])  getInfo = create_getInfo(IfileIt) diff --git a/module/plugins/hoster/IfolderRu.py b/module/plugins/hoster/IfolderRu.py index 7c5fede72..255f10f45 100644 --- a/module/plugins/hoster/IfolderRu.py +++ b/module/plugins/hoster/IfolderRu.py @@ -1,50 +1,39 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re +  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class IfolderRu(SimpleHoster): -    __name__ = "IfolderRu" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?(?:ifolder\.ru|rusfolder\.(?:com|net|ru))/(?:files/)?(?P<ID>\d+).*' +    __name__    = "IfolderRu" +    __type__    = "hoster"      __version__ = "0.38" + +    __pattern__ = r'http://(?:www\.)?(?:ifolder\.ru|rusfolder\.(?:com|net|ru))/(?:files/)?(?P<ID>\d+)' +      __description__ = """Ifolder.ru hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" - -    FILE_SIZE_REPLACEMENTS = [(u'Ðб', 'KB'), (u'Ðб', 'MB'), (u'Ðб', 'GB')] -    FILE_NAME_PATTERN = ur'(?:<div><span>)?ÐазваМОе:(?:</span>)? <b>(?P<N>[^<]+)</b><(?:/div|br)>' -    FILE_SIZE_PATTERN = ur'(?:<div><span>)?РазЌеÑ:(?:</span>)? <b>(?P<S>[^<]+)</b><(?:/div|br)>' -    FILE_OFFLINE_PATTERN = ur'<p>Ѐайл ÐœÐŸÐŒÐµÑ <b>[^<]*</b> (Ме МайЎеМ|ÑЎалеМ) !!!</p>' - -    SESSION_ID_PATTERN = r'<a href=(http://ints.(?:rusfolder.com|ifolder.ru)/ints/sponsor/\?bi=\d*&session=([^&]+)&u=[^>]+)>' -    INTS_SESSION_PATTERN = r'\(\'ints_session\'\);\s*if\(tag\)\{tag.value = "([^"]+)";\}' -    HIDDEN_INPUT_PATTERN = r"var v = .*?name='([^']+)' value='1'" -    DOWNLOAD_LINK_PATTERN = r'<a id="download_file_href" href="([^"]+)"' +    __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'<a href=(http://ints\.(?:rusfolder\.com|ifolder\.ru)/ints/sponsor/\?bi=\d*&session=([^&]+)&u=[^>]+)>' +    INTS_SESSION_PATTERN = r'\(\'ints_session\'\);\s*if\(tag\)\{tag\.value = "([^"]+)";\}' +    HIDDEN_INPUT_PATTERN = r'var v = .*?name=\'(.+?)\' value=\'1\'' +    LINK_PATTERN = r'<a id="download_file_href" href="([^"]+)"'      WRONG_CAPTCHA_PATTERN = ur'<font color=Red>МевеÑМÑй кПЎ,<br>ввеЎОÑе еÑе Ñаз</font><br>' +      def setup(self):          self.resumeDownload = self.multiDL = True if self.account else False          self.chunkLimit = 1 +      def process(self, pyfile):          file_id = re.match(self.__pattern__, pyfile.url).group('ID')          self.html = self.load("http://rusfolder.com/%s" % file_id, cookies=True, decode=True) @@ -62,7 +51,7 @@ class IfolderRu(SimpleHoster):          self.wait(31, False)          captcha_url = "http://ints.rusfolder.com/random/images/?session=%s" % session_id -        for _ in xrange(5): +        for _i in xrange(5):              self.html = self.load(url, cookies=True)              action, inputs = self.parseHtmlForm('ID="Form1"')              inputs['ints_session'] = re.search(self.INTS_SESSION_PATTERN, self.html).group(1) @@ -77,11 +66,10 @@ class IfolderRu(SimpleHoster):              else:                  break          else: -            self.fail("Invalid captcha") +            self.fail(_("Invalid captcha")) -        download_url = re.search(self.DOWNLOAD_LINK_PATTERN, self.html).group(1) +        download_url = re.search(self.LINK_PATTERN, self.html).group(1)          self.correctCaptcha() -        self.logDebug("Download URL: %s" % download_url)          self.download(download_url) diff --git a/module/plugins/hoster/JumbofilesCom.py b/module/plugins/hoster/JumbofilesCom.py index cdeffff34..cabc0f098 100644 --- a/module/plugins/hoster/JumbofilesCom.py +++ b/module/plugins/hoster/JumbofilesCom.py @@ -1,31 +1,37 @@  # -*- coding: utf-8 -*-  import re +  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class JumbofilesCom(SimpleHoster): -    __name__ = "JumbofilesCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?jumbofiles.com/(\w{12}).*' +    __name__    = "JumbofilesCom" +    __type__    = "hoster"      __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?jumbofiles\.com/(\w{12})' +      __description__ = """JumboFiles.com hoster plugin""" -    __author_name__ = "godofdream" -    __author_mail__ = "soilfiction@gmail.com" +    __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_PATTERN = r'<meta http-equiv="refresh" content="10;url=(.+)">' -    FILE_INFO_PATTERN = '<TR><TD>(?P<N>[^<]+?)\s*<small>\((?P<S>[\d.]+)\s*(?P<U>[KMG][bB])\)</small></TD></TR>' -    FILE_OFFLINE_PATTERN = 'Not Found or Deleted / Disabled due to inactivity or DMCA' -    DIRECT_LINK_PATTERN = '<meta http-equiv="refresh" content="10;url=(.+)">'      def setup(self): -        self.resumeDownload = self.multiDL = True +        self.resumeDownload = True +        self.multiDL        = True +      def handleFree(self):          ukey = re.match(self.__pattern__, self.pyfile.url).group(1)          post_data = {"id": ukey, "op": "download3", "rand": ""}          html = self.load(self.pyfile.url, post=post_data, decode=True) -        url = re.search(self.DIRECT_LINK_PATTERN, html).group(1) -        self.logDebug("Download " + url) +        url = re.search(self.LINK_PATTERN, html).group(1)          self.download(url) diff --git a/module/plugins/hoster/JunocloudMe.py b/module/plugins/hoster/JunocloudMe.py new file mode 100644 index 000000000..56d6588fa --- /dev/null +++ b/module/plugins/hoster/JunocloudMe.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo + + +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")] + + +    HOSTER_DOMAIN = "junocloud.me" + +    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.<' + + +getInfo = create_getInfo(JunocloudMe) diff --git a/module/plugins/hoster/Keep2shareCC.py b/module/plugins/hoster/Keep2shareCC.py deleted file mode 100644 index a7f1efdb8..000000000 --- a/module/plugins/hoster/Keep2shareCC.py +++ /dev/null @@ -1,120 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -# Test links (random.bin): -# http://k2s.cc/file/55fb73e1c00c5/random.bin - -import re -from urlparse import urlparse, urljoin - -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo -from module.plugins.internal.CaptchaService import ReCaptcha - - -class Keep2shareCC(SimpleHoster): -    __name__ = "Keep2shareCC" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?(keep2share|k2s|keep2s)\.cc/file/(?P<ID>\w+)' -    __version__ = "0.10" -    __description__ = """Keep2share.cc hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" - -    FILE_NAME_PATTERN = r'File: <span>(?P<N>.+)</span>' -    FILE_SIZE_PATTERN = r'Size: (?P<S>[^<]+)</div>' -    FILE_OFFLINE_PATTERN = r'File not found or deleted|Sorry, this file is blocked or deleted|Error 404' - -    DIRECT_LINK_PATTERN = r'To download this file with slow speed, use <a href="([^"]+)">this link</a>' -    WAIT_PATTERN = r'Please wait ([\d:]+) to download this file' -    ALREADY_DOWNLOADING_PATTERN = r'Free account does not allow to download more than one file at the same time' - -    RECAPTCHA_KEY = '6LcYcN0SAAAAABtMlxKj7X0hRxOY8_2U86kI1vbb' - -    def handleFree(self): -        self.sanitize_url() -        self.html = self.load(self.pyfile.url) - -        self.fid = re.search(r'<input type="hidden" name="slow_id" value="([^"]+)">', self.html).group(1) -        self.html = self.load(self.pyfile.url, post={'yt0': '', 'slow_id': self.fid}) - -        m = re.search(r"function download\(\){.*window\.location\.href = '([^']+)';", self.html, re.DOTALL) -        if m:  # Direct mode -            self.startDownload(m.group(1)) -        else: -            self.handleCaptcha() - -            self.wait(30) - -            self.html = self.load(self.pyfile.url, post={'uniqueId': self.fid, 'free': 1}) - -            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.wait(wait_time, reconnect=True) -                self.retry() - -            m = re.search(self.ALREADY_DOWNLOADING_PATTERN, self.html) -            if m: -                # if someone is already downloading on our line, wait 30min and retry -                self.logDebug('Already downloading, waiting for 30 minutes') -                self.wait(30 * 60, reconnect=True) -                self.retry() - -            m = re.search(self.DIRECT_LINK_PATTERN, self.html) -            if not m: -                self.parseError("Unable to detect direct link") -            self.startDownload(m.group(1)) - -    def handleCaptcha(self): -        recaptcha = ReCaptcha(self) -        for _ in xrange(5): -            challenge, response = recaptcha.challenge(self.RECAPTCHA_KEY) -            post_data = {'recaptcha_challenge_field': challenge, -                         'recaptcha_response_field': response, -                         'CaptchaForm%5Bcode%5D': '', -                         'free': 1, -                         'freeDownloadRequest': 1, -                         'uniqueId': self.fid, -                         'yt0': ''} - -            self.html = self.load(self.pyfile.url, post=post_data) - -            if 'recaptcha' not in self.html: -                self.correctCaptcha() -                break -            else: -                self.logInfo('Wrong captcha') -                self.invalidCaptcha() -        else: -            self.fail("All captcha attempts failed") - -    def startDownload(self, url): -        d = urljoin(self.base_url, url) -        self.logDebug('Direct Link: ' + d) -        self.download(d, disposition=True) - -    def sanitize_url(self): -        header = self.load(self.pyfile.url, just_header=True) -        if 'location' in header: -            self.pyfile.url = header['location'] -        p = urlparse(self.pyfile.url) -        self.base_url = "%s://%s" % (p.scheme, p.hostname) - - -getInfo = create_getInfo(Keep2shareCC) diff --git a/module/plugins/hoster/Keep2shareCc.py b/module/plugins/hoster/Keep2shareCc.py new file mode 100644 index 000000000..86c28e93b --- /dev/null +++ b/module/plugins/hoster/Keep2shareCc.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin, urlparse + +from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import _isDirectLink, SimpleHoster, create_getInfo + + +class Keep2shareCc(SimpleHoster): +    __name__    = "Keep2shareCc" +    __type__    = "hoster" +    __version__ = "0.18" + +    __pattern__ = r'https?://(?:www\.)?(keep2share|k2s|keep2s)\.cc/file/(?P<ID>\w+)' + +    __description__ = """Keep2share.cc hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    URL_REPLACEMENTS = [(__pattern__ + ".*", "http://k2s.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 = LINK_PREMIUM_PATTERN = r'"([^"]+url.html?file=.+?)"|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): +        self.fid  = re.search(r'<input type="hidden" name="slow_id" value="([^"]+)">', self.html).group(1) +        self.html = self.load(self.pyfile.url, post={'yt0': '', 'slow_id': self.fid}) + +        self.checkErrors() + +        m = re.search(self.LINK_FREE_PATTERN, self.html) + +        if m is None: +            self.handleCaptcha() + +            self.wait(30) + +            self.html = self.load(self.pyfile.url, post={'uniqueId': self.fid, 'free': 1}) + +            self.checkErrors() + +            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) + + +    def handleCaptcha(self): +        recaptcha = ReCaptcha(self) + +        for _i in xrange(5): +            post_data = {'free'               : 1, +                         'freeDownloadRequest': 1, +                         'uniqueId'           : self.fid, +                         'yt0'                : ''} + +            m = re.search(self.CAPTCHA_PATTERN, self.html) +            if m: +                captcha_url = urljoin("http://k2s.cc/", m.group(1)) +                post_data['CaptchaForm[code]'] = self.decryptCaptcha(captcha_url) +            else: +                challenge, response = recaptcha.challenge() +                post_data.update({'recaptcha_challenge_field': challenge, +                                  'recaptcha_response_field' : response}) + +            self.html = self.load(self.pyfile.url, post=post_data) + +            if 'recaptcha' not in self.html: +                self.correctCaptcha() +                break +            else: +                self.invalidCaptcha() +        else: +            self.fail(_("All captcha attempts failed")) + + +    def downloadLink(self, link): +        if not link or not isinstance(link, basestring): +            return + +        link = _isDirectLink(self, link, self.premium) + +        if link: +            self.download(urljoin("http://k2s.cc/", link), disposition=True) + + +getInfo = create_getInfo(Keep2shareCc) diff --git a/module/plugins/hoster/KickloadCom.py b/module/plugins/hoster/KickloadCom.py new file mode 100644 index 000000000..1c39db46c --- /dev/null +++ b/module/plugins/hoster/KickloadCom.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class KickloadCom(DeadHoster): +    __name__    = "KickloadCom" +    __type__    = "hoster" +    __version__ = "0.21" + +    __pattern__ = r'http://(?:www\.)?kickload\.com/get/.+' + +    __description__ = """Kickload.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de")] + + +getInfo = create_getInfo(KickloadCom) diff --git a/module/plugins/hoster/KingfilesNet.py b/module/plugins/hoster/KingfilesNet.py new file mode 100644 index 000000000..13cbf4781 --- /dev/null +++ b/module/plugins/hoster/KingfilesNet.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- + +import re + +from module.plugins.internal.CaptchaService import SolveMedia +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo + + +class KingfilesNet(SimpleHoster): +    __name__    = "KingfilesNet" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?kingfiles\.net/(?P<ID>\w{12})' + +    __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_PATTERN = r'var download_url = \'(.+)\';' + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True + + +    def handleFree(self): +        # Click the free user button +        post_data = {'op'         : "download1", +                     'usr_login'  : "", +                     'id'         : self.info['pattern']['ID'], +                     'fname'      : self.pyfile.name, +                     'referer'    : "", +                     'method_free': "+"} + +        self.html = self.load(self.pyfile.url, post=post_data, cookies=True, decode=True) + +        solvemedia = SolveMedia(self) +        challenge, response = 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'         : self.pyfile.url, +                     'method_free'     : "+", +                     'method_premium'  : "", +                     'adcopy_response' : response, +                     'adcopy_challenge': challenge, +                     'down_direct'     : "1"} + +        self.html = self.load(self.pyfile.url, post=post_data, cookies=True, decode=True) + +        m = re.search(self.LINK_PATTERN, self.html) +        if m is None: +            self.error(_("Download url not found")) + +        self.download(m.group(1), cookies=True, disposition=True) + +        check = self.checkDownload({'html': re.compile("<html>")}) +        if check == "html": +            self.error(_("Downloaded file is an html page")) + + +getInfo = create_getInfo(KingfilesNet) diff --git a/module/plugins/hoster/LemUploadsCom.py b/module/plugins/hoster/LemUploadsCom.py index 426a757b3..22fbd60dd 100644 --- a/module/plugins/hoster/LemUploadsCom.py +++ b/module/plugins/hoster/LemUploadsCom.py @@ -1,24 +1,18 @@  # -*- coding: utf-8 -*- -# Test links: -# BigBuckBunny_320x180.mp4 - 61.7 Mb - http://lemuploads.com/uwol0aly9dld +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +class LemUploadsCom(DeadHoster): +    __name__    = "LemUploadsCom" +    __type__    = "hoster" +    __version__ = "0.02" -class LemUploadsCom(XFileSharingPro): -    __name__ = "LemUploadsCom" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?lemuploads.com/\w{12}' -    __version__ = "0.01" -    __description__ = """LemUploads.com hoster plugin""" -    __author_name__ = "t4skforce" -    __author_mail__ = "t4skforce1337[AT]gmail[DOT]com" - -    HOSTER_NAME = "lemuploads.com" +    __pattern__ = r'https?://(?:www\.)?lemuploads\.com/\w{12}' -    FILE_OFFLINE_PATTERN = r'<b>File Not Found</b><br><br>' -    FILE_NAME_PATTERN = r'<b>Password:</b></div>\s*<h2>(?P<N>[^<]+)</h2>' +    __description__ = """LemUploads.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")]  getInfo = create_getInfo(LemUploadsCom) diff --git a/module/plugins/hoster/LetitbitNet.py b/module/plugins/hoster/LetitbitNet.py index 58532fd28..a7b11047f 100644 --- a/module/plugins/hoster/LetitbitNet.py +++ b/module/plugins/hoster/LetitbitNet.py @@ -1,40 +1,25 @@  # -*- coding: utf-8 -*- - -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" - +#  # API Documentation:  # http://api.letitbit.net/reg/static/api.pdf - -# Test links (random.bin): +# +# Test links:  # http://letitbit.net/download/07874.0b5709a7d3beee2408bb1f2eefce/random.bin.html  import re -import urllib -from module.plugins.internal.SimpleHoster import SimpleHoster +from urllib import urlencode, urlopen +from urlparse import urljoin +  from module.common.json_layer import json_loads, json_dumps  from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import SimpleHoster, secondsToMidnight  def api_download_info(url): -    json_data = ['yw7XQy2v9', ["download/info", {"link": url}]] -    post_data = urllib.urlencode({'r': json_dumps(json_data)}) -    api_rep = urllib.urlopen('http://api.letitbit.net/json', data=post_data).read() +    json_data = ["yw7XQy2v9", ["download/info", {"link": url}]] +    post_data = urlencode({'r': json_dumps(json_data)}) +    api_rep = urlopen("http://api.letitbit.net/json", data=post_data).read()      return json_loads(api_rep) @@ -49,25 +34,27 @@ def getInfo(urls):  class LetitbitNet(SimpleHoster): -    __name__ = "LetitbitNet" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?(letitbit|shareflare).net/download/.*' -    __version__ = "0.23" +    __name__    = "LetitbitNet" +    __type__    = "hoster" +    __version__ = "0.27" + +    __pattern__ = r'https?://(?:www\.)?(letitbit|shareflare)\.net/download/.+' +      __description__ = """Letitbit.net hoster plugin""" -    __author_name__ = ("zoidberg", "z00nx") -    __author_mail__ = ("zoidberg@mujmail.cz", "z00nx0@gmail.com") +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("z00nx", "z00nx0@gmail.com")] -    CHECK_URL_PATTERN = r"ajax_check_url\s*=\s*'((http://[^/]+)[^']+)';" -    SECONDS_PATTERN = r"seconds\s*=\s*(\d+);" -    CAPTCHA_CONTROL_FIELD = r"recaptcha_control_field\s=\s'(?P<value>[^']+)'" -    DOMAIN = "http://letitbit.net" -    FILE_URL_REPLACEMENTS = [(r"(?<=http://)([^/]+)", "letitbit.net")] -    RECAPTCHA_KEY = "6Lc9zdMSAAAAAF-7s2wuQ-036pLRbM0p8dDaQdAM" +    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 -        #TODO confirm that resume works +      def getFileInfo(self):          api_rep = api_download_info(self.pyfile.url) @@ -78,92 +65,77 @@ class LetitbitNet(SimpleHoster):          else:              self.offline() +      def handleFree(self):          action, inputs = self.parseHtmlForm('id="ifree_form"')          if not action: -            self.parseError("page 1 / ifree_form") +            self.error(_("ifree_form")) +          self.pyfile.size = float(inputs['sssize'])          self.logDebug(action, inputs)          inputs['desc'] = "" -        self.html = self.load(self.DOMAIN + action, post=inputs, cookies=True) - -        # action, inputs = self.parseHtmlForm('id="d3_form"') -        # if not action: self.parseError("page 2 / d3_form") -        # #self.logDebug(action, inputs) -        # -        # self.html = self.load(action, post = inputs, cookies = True) -        # -        # try: -        #     ajax_check_url, captcha_url = re.search(self.CHECK_URL_PATTERN, self.html).groups() -        #     found = re.search(self.SECONDS_PATTERN, self.html) -        #     seconds = int(found.group(1)) if found else 60 -        #     self.wait(seconds+1) -        # except Exception, e: -        #     self.logError(e) -        #     self.parseError("page 3 / js") - -        found = re.search(self.SECONDS_PATTERN, self.html) -        seconds = int(found.group(1)) if found else 60 +        self.html = self.load(urljoin("http://letitbit.net/", action), post=inputs, cookies=True) + +        m = re.search(self.SECONDS_PATTERN, self.html) +        seconds = int(m.group(1)) if m else 60          self.logDebug("Seconds found", seconds) -        found = re.search(self.CAPTCHA_CONTROL_FIELD, self.html) -        recaptcha_control_field = found.group(1) +        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 + 1) +        self.wait(seconds) -        response = self.load("%s/ajax/download3.php" % self.DOMAIN, post=" ", cookies=True) -        if response != '1': -            self.parseError('Unknown response - ajax_check_url') -        self.logDebug(response) +        res = self.load("http://letitbit.net/ajax/download3.php", post=" ", cookies=True) +        if res != '1': +            self.error(_("Unknown response - ajax_check_url")) +        self.logDebug(res)          recaptcha = ReCaptcha(self) -        challenge, response = recaptcha.challenge(self.RECAPTCHA_KEY) -        post_data = {"recaptcha_challenge_field": challenge, "recaptcha_response_field": response, +        challenge, response = 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) -        response = self.load('%s/ajax/check_recaptcha.php' % self.DOMAIN, post=post_data, cookies=True) -        self.logDebug(response) -        if not response: +        res = self.load("http://letitbit.net/ajax/check_recaptcha.php", post=post_data, cookies=True) +        self.logDebug(res) +        if not res:              self.invalidCaptcha() -        if response == "error_free_download_blocked": -            self.logInfo("Daily limit reached, waiting 24 hours") -            self.wait(24 * 60 * 60) -        if response == "error_wrong_captcha": -            self.logInfo("Wrong Captcha") +        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 response.startswith('['): -            urls = json_loads(response) -        elif response.startswith('http://'): -            urls = [response] +        elif res.startswith('['): +            urls = json_loads(res) +        elif res.startswith('http://'): +            urls = [res]          else: -            self.parseError("Unknown response - captcha check") +            self.error(_("Unknown response - captcha check"))          self.correctCaptcha()          for download_url in urls:              try: -                self.logDebug("Download URL", download_url)                  self.download(download_url)                  break              except Exception, e:                  self.logError(e)          else: -            self.fail("Download did not finish correctly") +            self.fail(_("Download did not finish correctly")) +      def handlePremium(self):          api_key = self.user -        premium_key = self.account.getAccountData(self.user)["password"] +        premium_key = self.account.getAccountData(self.user)['password']          json_data = [api_key, ["download/direct_links", {"pass": premium_key, "link": self.pyfile.url}]]          api_rep = self.load('http://api.letitbit.net/json', post={'r': json_dumps(json_data)}) -        self.logDebug('API Data: ' + api_rep) +        self.logDebug("API Data: " + api_rep)          api_rep = json_loads(api_rep)          if api_rep['status'] == 'FAIL':              self.fail(api_rep['data']) -        direct_link = api_rep['data'][0][0] -        self.logDebug('Direct Link: ' + direct_link) - -        self.download(direct_link, disposition=True) +        self.download(api_rep['data'][0][0], disposition=True) diff --git a/module/plugins/hoster/LinestorageCom.py b/module/plugins/hoster/LinestorageCom.py new file mode 100644 index 000000000..a3caacc0c --- /dev/null +++ b/module/plugins/hoster/LinestorageCom.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo + + +class LinestorageCom(XFSHoster): +    __name__    = "LinestorageCom" +    __type__    = "hoster" +    __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?linestorage\.com/\w{12}' + +    __description__ = """Linestorage.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "linestorage.com" + + +getInfo = create_getInfo(LinestorageCom) diff --git a/module/plugins/hoster/LinksnappyCom.py b/module/plugins/hoster/LinksnappyCom.py index 2ed7744d7..90a47d1ac 100644 --- a/module/plugins/hoster/LinksnappyCom.py +++ b/module/plugins/hoster/LinksnappyCom.py @@ -1,68 +1,75 @@  # -*- coding: utf-8 -*-  import re +  from urlparse import urlsplit -from module.plugins.Hoster import Hoster  from module.common.json_layer import json_loads, json_dumps +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo + +class LinksnappyCom(MultiHoster): +    __name__    = "LinksnappyCom" +    __type__    = "hoster" +    __version__ = "0.06" -class LinksnappyCom(Hoster): -    __name__ = "LinksnappyCom" -    __version__ = "0.02" -    __type__ = "hoster"      __pattern__ = r'https?://(?:[^/]*\.)?linksnappy\.com' +      __description__ = """Linksnappy.com hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] +      SINGLE_CHUNK_HOSTERS = ('easybytez.com') +      def setup(self): -        self.chunkLimit = -1 +        self.chunkLimit     = -1          self.resumeDownload = True -    def process(self, pyfile): -        if re.match(self.__pattern__, pyfile.url): -            new_url = pyfile.url -        elif not self.account: -            self.logError(_("Please enter your %s account or deactivate this plugin") % "Linksnappy.com") -            self.fail("No Linksnappy.com account provided") -        else: -            self.logDebug("Old URL: %s" % pyfile.url) -            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] +    def handlePremium(self): +        host = self._get_host(self.pyfile.url) +        json_params = json_dumps({'link': self.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) -            if j['error']: -                self.logError('Error converting the link: %s' % j['error']) -                self.fail('Error converting the link') +        j = json_loads(r)['links'][0] -            pyfile.name = j['filename'] -            new_url = j['generated'] +        if j['error']: +            msg = _("Error converting the link") +            self.logError(msg, j['error']) +            self.fail(msg) -            if host in self.SINGLE_CHUNK_HOSTERS: -                self.chunkLimit = 1 -            else: -                self.setup() +        self.pyfile.name = j['filename'] +        self.link = j['generated'] -        if new_url != pyfile.url: -            self.logDebug("New URL: " + new_url) +        if host in self.SINGLE_CHUNK_HOSTERS: +            self.chunkLimit = 1 +        else: +            self.setup() -        self.download(new_url, disposition=True) +        if self.link != self.pyfile.url: +            self.logDebug("New URL: " + self.link) + + +    def checkFile(self): +        super(LinksnappyCom, self).checkFile()          check = self.checkDownload({"html302": "<title>302 Found</title>"}) +          if check == "html302": -            self.retry(wait_time=5, reason="Linksnappy returns only HTML data.") +            self.retry(wait_time=5, reason=_("Linksnappy returns only HTML data")) +      @staticmethod      def _get_host(url):          host = urlsplit(url).netloc          return re.search(r'[\w-]+\.\w+$', host).group(0) + + +getInfo = create_getInfo(LinksnappyCom) diff --git a/module/plugins/hoster/LoadTo.py b/module/plugins/hoster/LoadTo.py index e52346274..0a5b26410 100644 --- a/module/plugins/hoster/LoadTo.py +++ b/module/plugins/hoster/LoadTo.py @@ -1,90 +1,75 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -# Test links (random.bin): +# +# Test links:  # http://www.load.to/JWydcofUY6/random.bin  # http://www.load.to/oeSmrfkXE/random100.bin  import re +from module.plugins.internal.CaptchaService import SolveMedia  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo -from module.plugins.internal.CaptchaService import ReCaptcha  class LoadTo(SimpleHoster): -    __name__ = "LoadTo" -    __type__ = "hoster" +    __name__    = "LoadTo" +    __type__    = "hoster" +    __version__ = "0.18" +      __pattern__ = r'http://(?:www\.)?load\.to/\w+' -    __version__ = "0.13" -    __description__ = """Load.to hoster plugin""" -    __author_name__ = ("halfman", "stickell") -    __author_mail__ = ("Pulpan3@gmail.com", "l.stickell@yahoo.it") - -    FILE_INFO_PATTERN = r'<a [^>]+>(?P<N>.+)</a></h3>\s*Size: (?P<S>\d+) (?P<U>[kKmMgG]?i?[bB])' -    URL_PATTERN = r'<form method="post" action="(.+?)"' -    FILE_OFFLINE_PATTERN = r'Can\'t find file. Please check URL.' + +    __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_PATTERN = r'<form method="post" action="(.+?)"'      WAIT_PATTERN = r'type="submit" value="Download \((\d+)\)"' -    RECAPTCHA_PATTERN = r'http://www.google.com/recaptcha/api/challenge' -    RECAPTCHA_KEY = "6Lc34eISAAAAAKNbPVyxBgNriTjPRmF-FA1oxApG" + +    URL_REPLACEMENTS = [(r'(\w)$', r'\1/')] +      def setup(self):          self.multiDL = True          self.chunkLimit = 1 -    def process(self, pyfile): -        self.html = self.load(pyfile.url, decode=True) -        self.getFileInfo() - -        # Check if File is online -        if re.search(self.FILE_OFFLINE_PATTERN, self.html): -            self.offline() +    def handleFree(self):          # Search for Download URL -        m = re.search(self.URL_PATTERN, self.html) -        if not m: -            self.parseError('Unable to detect download URL') +        m = re.search(self.LINK_PATTERN, self.html) +        if m is None: +            self.error(_("LINK_PATTERN not found")) +          download_url = m.group(1)          # Set Timer - may be obsolete          m = re.search(self.WAIT_PATTERN, self.html)          if m: -            self.wait(m.group(1)) +            self.wait(int(m.group(1))) -        # Check if reCaptcha is present -        m = re.search(self.RECAPTCHA_PATTERN, self.html) -        if not m:  # No captcha found +        # Load.to is using solvemedia captchas since ~july 2014: +        solvemedia = SolveMedia(self) +        captcha_key = solvemedia.detect_key() + +        if captcha_key is None:              self.download(download_url)          else: -            recaptcha = ReCaptcha(self) -            for _ in xrange(5): -                challenge, response = recaptcha.challenge(self.RECAPTCHA_KEY) -                if not response == '0': -                    break -            else: -                self.fail("No valid captcha solution received") - -            self.download(download_url, -                          post={'recaptcha_challenge_field': challenge, 'recaptcha_response_field': response}) - -            # Verifiy reCaptcha by checking content of file for html 404-error -            check = self.checkDownload({"404": re.compile("\A<h1>404 Not Found</h1>")}) +            challenge, response = solvemedia.challenge(captcha_key) + +            self.download(download_url, post={"adcopy_challenge": challenge, "adcopy_response": response}) + +            check = self.checkDownload({'404': re.compile("\A<h1>404 Not Found</h1>"), 'html': re.compile("html")}) +              if check == "404": -                self.logWarning("The captcha you entered was incorrect. Please try again.")                  self.invalidCaptcha()                  self.retry() +            elif check == "html": +                self.logWarning(_("Downloaded file is an html page, will retry")) +                self.retry()  getInfo = create_getInfo(LoadTo) diff --git a/module/plugins/hoster/LomafileCom.py b/module/plugins/hoster/LomafileCom.py new file mode 100644 index 000000000..475cdacaa --- /dev/null +++ b/module/plugins/hoster/LomafileCom.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class LomafileCom(DeadHoster): +    __name__    = "LomafileCom" +    __type__    = "hoster" +    __version__ = "0.52" + +    __pattern__ = r'http://lomafile\.com/\w{12}' + +    __description__ = """Lomafile.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("nath_schwarz", "nathan.notwhite@gmail.com"), +                       ("guidobelix", "guidobelix@hotmail.it")] + + +getInfo = create_getInfo(LomafileCom) diff --git a/module/plugins/hoster/LuckyShareNet.py b/module/plugins/hoster/LuckyShareNet.py index 75520a6cf..2c33b57e7 100644 --- a/module/plugins/hoster/LuckyShareNet.py +++ b/module/plugins/hoster/LuckyShareNet.py @@ -1,70 +1,72 @@  # -*- coding: utf-8 -*-  import re -from module.lib.bottle import json_loads -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo +from bottle import json_loads +  from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class LuckyShareNet(SimpleHoster): -    __name__ = "LuckyShareNet" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?luckyshare.net/(?P<ID>\d{10,})' -    __version__ = "0.02" +    __name__    = "LuckyShareNet" +    __type__    = "hoster" +    __version__ = "0.04" + +    __pattern__ = r'https?://(?:www\.)?luckyshare\.net/(?P<ID>\d{10,})' +      __description__ = """LuckyShare.net hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __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' -    FILE_INFO_PATTERN = r"<h1 class='file_name'>(?P<N>\S+)</h1>\s*<span class='file_size'>Filesize: (?P<S>[\d.]+)(?P<U>\w+)</span>" -    FILE_OFFLINE_PATTERN = 'There is no such file available' -    RECAPTCHA_KEY = '6LdivsgSAAAAANWh-d7rPE1mus4yVWuSQIJKIYNw'      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: -                waittime = int(m.group(1)) -                self.logDebug('You have to wait %d seconds between free downloads' % waittime) -                self.retry(wait_time=waittime) +                seconds = int(m.group(1)) +                self.logDebug("You have to wait %d seconds between free downloads" % seconds) +                self.retry(wait_time=seconds)              else: -                self.parseError('Unable to detect wait time between free downloads') +                self.error(_("Unable to detect wait time between free downloads"))          elif 'Hash expired' in rep: -            self.retry(reason="Hash expired") +            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): -        file_id = re.match(self.__pattern__, self.pyfile.url).group('ID') -        self.logDebug('File ID: ' + file_id) -        rep = self.load(r"http://luckyshare.net/download/request/type/time/file/" + file_id, decode=True) -        self.logDebug('JSON: ' + rep) +        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(int(json['time']))          recaptcha = ReCaptcha(self) -        for _ in xrange(5): -            challenge, response = recaptcha.challenge(self.RECAPTCHA_KEY) + +        for _i in xrange(5): +            challenge, response = 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) +            self.logDebug("JSON: " + rep)              if 'link' in rep:                  json.update(self.parseJson(rep))                  self.correctCaptcha()                  break              elif 'Verification failed' in rep: -                self.logInfo('Wrong captcha')                  self.invalidCaptcha()              else: -                self.parseError('Unable to get downlaod link') +                self.error(_("Unable to get downlaod link"))          if not json['link']: -            self.fail("No Download url retrieved/all captcha attempts failed") +            self.fail(_("No Download url retrieved/all captcha attempts failed")) -        self.logDebug('Direct URL: ' + json['link'])          self.download(json['link']) diff --git a/module/plugins/hoster/MediafireCom.py b/module/plugins/hoster/MediafireCom.py index 52e38aada..782d78ce1 100644 --- a/module/plugins/hoster/MediafireCom.py +++ b/module/plugins/hoster/MediafireCom.py @@ -1,25 +1,9 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re -from module.plugins.internal.SimpleHoster import SimpleHoster, parseFileInfo +  from module.plugins.internal.CaptchaService import SolveMedia +from module.plugins.internal.SimpleHoster import SimpleHoster, parseFileInfo  from module.network.RequestFactory import getURL @@ -29,68 +13,76 @@ def replace_eval(js_expr):  def checkHTMLHeader(url):      try: -        for _ in xrange(3): +        for _i in xrange(3):              header = getURL(url, just_header=True) +              for line in header.splitlines():                  line = line.lower() +                  if 'location' in line:                      url = line.split(':', 1)[1].strip()                      if 'error.php?errno=320' in url:                          return url, 1 +                      if not url.startswith('http://'):                          url = 'http://www.mediafire.com' + url +                      break +                  elif 'content-disposition' in line:                      return url, 2              else:                  break      except:          return url, 3 - -    return url, 0 +    else: +        return url, 0  def getInfo(urls):      for url in urls:          location, status = checkHTMLHeader(url) +          if status:              file_info = (url, 0, status, url)          else:              file_info = parseFileInfo(MediafireCom, url, getURL(url, decode=True)) +          yield file_info  class MediafireCom(SimpleHoster): -    __name__ = "MediafireCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?mediafire\.com/(file/|(view/?|download.php)?\?)(\w{11}|\w{15})($|/)' -    __version__ = "0.79" +    __name__    = "MediafireCom" +    __type__    = "hoster" +    __version__ = "0.82" + +    __pattern__ = r'http://(?:www\.)?mediafire\.com/(file/|(view/?|download\.php)?\?)(\w{11}|\w{15})($|/)' +      __description__ = """Mediafire.com hoster plugin""" -    __author_name__ = ("zoidberg", "stickell") -    __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it") - -    DOWNLOAD_LINK_PATTERN = r'<div class="download_link"[^>]*(?:z-index:(?P<zindex>\d+))?[^>]*>\s*<a href="(?P<href>http://[^"]+)"' -    JS_KEY_PATTERN = r"DoShow\('mfpromo1'\);[^{]*{((\w+)='';.*?)eval\(\2\);" -    JS_ZMODULO_PATTERN = r"\('z-index'\)\) \% (\d+)\)\);" -    SOLVEMEDIA_PATTERN = r'http://api\.solvemedia\.com/papi/challenge\.noscript\?k=([^"]+)' -    PAGE1_ACTION_PATTERN = r'<link rel="canonical" href="([^"]+)"/>' +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    NAME_PATTERN    = r'<META NAME="description" CONTENT="(?P<N>[^"]+)"/>' +    INFO_PATTERN    = r'oFileSharePopup\.ald\(\'(?P<ID>[^\']*)\',\'(?P<N>[^\']*)\',\'(?P<S>[^\']*)\',\'\',\'(?P<H>[^\']*)\'\)' +    OFFLINE_PATTERN = r'class="error_msg_title"> Invalid or Deleted File. </div>' +      PASSWORD_PATTERN = r'<form name="form_password"' -    FILE_NAME_PATTERN = r'<META NAME="description" CONTENT="(?P<N>[^"]+)"/>' -    FILE_INFO_PATTERN = r"oFileSharePopup\.ald\('(?P<ID>[^']*)','(?P<N>[^']*)','(?P<S>[^']*)','','(?P<sha256>[^']*)'\)" -    FILE_OFFLINE_PATTERN = r'class="error_msg_title"> Invalid or Deleted File. </div>'      def setup(self):          self.multiDL = False +      def process(self, pyfile):          pyfile.url = re.sub(r'/view/?\?', '/?', pyfile.url) -        self.url, result = checkHTMLHeader(pyfile.url) -        self.logDebug('Location (%d): %s' % (result, self.url)) +        self.link, result = checkHTMLHeader(pyfile.url) +        self.logDebug("Location (%d): %s" % (result, self.link))          if result == 0: -            self.html = self.load(self.url, decode=True) +            self.html = self.load(self.link, decode=True)              self.checkCaptcha()              self.multiDL = True              self.check_data = self.getFileInfo() @@ -103,36 +95,34 @@ class MediafireCom(SimpleHoster):              self.offline()          else:              self.multiDL = True -            self.download(self.url, disposition=True) +            self.download(self.link, disposition=True) +      def handleFree(self): -        passwords = self.getPassword().splitlines() -        while self.PASSWORD_PATTERN in self.html: -            if len(passwords): -                password = passwords.pop(0) -                self.logInfo("Password protected link, trying " + password) -                self.html = self.load(self.url, post={"downloadp": password}) +        if self.PASSWORD_PATTERN in self.html: +            password = self.getPassword() + +            if password: +                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"))              else: -                self.fail("No or incorrect password") +                self.fail(_("No password found")) -        found = re.search(r'kNO = "(http://.*?)";', self.html) -        if not found: -            self.parseError("Download URL") -        download_url = found.group(1) -        self.logDebug("DOWNLOAD LINK:", download_url) +        m = re.search(r'kNO = r"(http://.*?)";', self.html) +        if m is None: +            self.error(_("No download URL")) +        download_url = m.group(1)          self.download(download_url) +      def checkCaptcha(self): -        for _ in xrange(5): -            found = re.search(self.SOLVEMEDIA_PATTERN, self.html) -            if found: -                captcha_key = found.group(1) -                solvemedia = SolveMedia(self) -                captcha_challenge, captcha_response = solvemedia.challenge(captcha_key) -                self.html = self.load(self.url, post={"adcopy_challenge": captcha_challenge, -                                                      "adcopy_response": captcha_response}, decode=True) -            else: -                break -        else: -            self.fail("No valid recaptcha solution received") +        solvemedia = SolveMedia(self) +        challenge, response = solvemedia.challenge() +        self.html = self.load(self.link, +                              post={'adcopy_challenge': challenge, +                                    'adcopy_response' : response}, +                              decode=True) diff --git a/module/plugins/hoster/MegaCoNz.py b/module/plugins/hoster/MegaCoNz.py new file mode 100644 index 000000000..00f38ff06 --- /dev/null +++ b/module/plugins/hoster/MegaCoNz.py @@ -0,0 +1,191 @@ +# -*- coding: utf-8 -*- + +import random +import re + +from array import array +from base64 import standard_b64decode +from os import remove + +from Crypto.Cipher import AES +from Crypto.Util import Counter +# from pycurl import SSL_CIPHER_LIST + +from module.common.json_layer import json_loads, json_dumps +from module.plugins.Hoster import Hoster + + +############################ 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.21" + +    __pattern__ = r'https?://(?:www\.)?mega\.co\.nz/#(?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 """ +        key = self.b64_decode(key) + +        k        = key[0] ^ key[4], key[1] ^ key[5], key[2] ^ key[6], key[3] ^ key[7] +        iv       = key[4:6] + (0, 0) +        meta_mac = key[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            = cbc.decrypt(self.b64_decode(data)) + +        self.logDebug("Decrypted Attr: " + 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") + +        file_crypted   = 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(str(e)) + +        chunk_size = 2 ** 15  # buffer size, 32k +        # file_mac   = [0, 0, 0, 0]  # calculate CBC-MAC for checksum + +        while True: +            buf = f.read(chunk_size) +            if not buf: +                break + +            chunk = cipher.decrypt(buf) +            df.write(chunk) + +        f.close() +        df.close() + +        remove(file_crypted) +        self.lastDownload = 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  = 'TYPE' not in pattern + +        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 "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/module/plugins/hoster/MegaDebridEu.py b/module/plugins/hoster/MegaDebridEu.py index 46eb0eb11..6e24cc7ba 100644 --- a/module/plugins/hoster/MegaDebridEu.py +++ b/module/plugins/hoster/MegaDebridEu.py @@ -1,61 +1,46 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################  import re +  from urllib import unquote_plus -from module.plugins.Hoster import Hoster  from module.common.json_layer import json_loads +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo + + +class MegaDebridEu(MultiHoster): +    __name__    = "MegaDebridEu" +    __type__    = "hoster" +    __version__ = "0.45" +    __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^_]+' -class MegaDebridEu(Hoster): -    __name__ = "MegaDebridEu" -    __version__ = "0.3" -    __type__ = "hoster" -    __pattern__ = r'^https?://(?:w{3}\d+\.mega-debrid.eu|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/download/file/[^/]+/.+$'      __description__ = """mega-debrid.eu hoster plugin""" -    __author_name__ = "D.Ducatel" -    __author_mail__ = "dducatel@je-geek.fr" +    __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 getFilename(self, url):          try:              return unquote_plus(url.rsplit("/", 1)[1])          except IndexError:              return "" -    def process(self, pyfile): -        if re.match(self.__pattern__, pyfile.url): -            new_url = pyfile.url -        elif not self.account: -            self.exitOnFail(_("Please enter your %s account or deactivate this plugin") % "Mega-debrid.eu") -        else: -            if not self.connectToApi(): -                self.exitOnFail(_("Impossible to connect to %s") % "Mega-debrid.eu") -            self.logDebug("Old URL: %s" % pyfile.url) -            new_url = self.debridLink(pyfile.url) -            self.logDebug("New URL: " + new_url) +    def handlePremium(self): +        if not self.connectToApi(): +            self.exitOnFail("Unable to connect to Mega-debrid.eu") -        filename = self.getFilename(new_url) +        self.link = self.debridLink(self.pyfile.url) +        self.logDebug("New URL: " + self.link) + +        filename = self.getFilename(self.link)          if filename != "": -            pyfile.name = filename -        self.download(new_url, disposition=True) +            self.pyfile.name = filename +      def connectToApi(self):          """ @@ -64,15 +49,16 @@ class MegaDebridEu(Hoster):          """          user, data = self.account.selectAccount()          jsonResponse = self.load(self.API_URL, -                                 get={'action': 'connectUser', 'login': user, 'password': data["password"]}) -        response = json_loads(jsonResponse) +                                 get={'action': 'connectUser', 'login': user, 'password': data['password']}) +        res = json_loads(jsonResponse) -        if response["response_code"] == "ok": -            self.token = response["token"] +        if res['response_code'] == "ok": +            self.token = res['token']              return True          else:              return False +      def debridLink(self, linkToDebrid):          """          Debrid a link @@ -80,13 +66,14 @@ class MegaDebridEu(Hoster):          """          jsonResponse = self.load(self.API_URL, get={'action': 'getLink', 'token': self.token},                                   post={"link": linkToDebrid}) -        response = json_loads(jsonResponse) +        res = json_loads(jsonResponse) -        if response["response_code"] == "ok": -            debridedLink = response["debridLink"][1:-1] +        if res['response_code'] == "ok": +            debridedLink = res['debridLink'][1:-1]              return debridedLink          else: -            self.exitOnFail(_("Impossible to debrid %s") % linkToDebrid) +            self.exitOnFail("Unable to debrid %s" % linkToDebrid) +      def exitOnFail(self, msg):          """ @@ -94,7 +81,10 @@ class MegaDebridEu(Hoster):          And display the reason of this failure          """          if self.getConfig("unloadFailing"): -            self.logError(msg) +            self.logError(_(msg))              self.resetAccount()          else: -            self.fail(msg) +            self.fail(_(msg)) + + +getInfo = create_getInfo(MegaDebridEu) diff --git a/module/plugins/hoster/MegaFilesSe.py b/module/plugins/hoster/MegaFilesSe.py index 43601bdb1..c3de57914 100644 --- a/module/plugins/hoster/MegaFilesSe.py +++ b/module/plugins/hoster/MegaFilesSe.py @@ -1,21 +1,18 @@  # -*- coding: utf-8 -*- -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo -class MegaFilesSe(XFileSharingPro): -    __name__ = "MegaFilesSe" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?megafiles\.se/\w{12}' -    __version__ = "0.01" -    __description__ = """MegaFiles.se hoster plugin""" -    __author_name__ = "t4skforce" -    __author_mail__ = "t4skforce1337[AT]gmail[DOT]com" +class MegaFilesSe(DeadHoster): +    __name__    = "MegaFilesSe" +    __type__    = "hoster" +    __version__ = "0.02" -    HOSTER_NAME = "megafiles.se" +    __pattern__ = r'http://(?:www\.)?megafiles\.se/\w{12}' -    FILE_OFFLINE_PATTERN = r'<b><font[^>]*>File Not Found</font></b><br><br>' -    FILE_NAME_PATTERN = r'<div[^>]+>\s*<b>(?P<N>[^<]+)</b>\s*</div>' +    __description__ = """MegaFiles.se hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")]  getInfo = create_getInfo(MegaFilesSe) diff --git a/module/plugins/hoster/MegaNz.py b/module/plugins/hoster/MegaNz.py deleted file mode 100644 index a55220bc2..000000000 --- a/module/plugins/hoster/MegaNz.py +++ /dev/null @@ -1,132 +0,0 @@ -# -*- coding: utf-8 -*- - -import re -import random -from array import array -from os import remove -from base64 import standard_b64decode - -from Crypto.Cipher import AES -from Crypto.Util import Counter - -from module.common.json_layer import json_loads, json_dumps -from module.plugins.Hoster import Hoster - -#def getInfo(urls): -#    pass - - -class MegaNz(Hoster): -    __name__ = "MegaNz" -    __type__ = "hoster" -    __pattern__ = r'https?://([a-z0-9]+\.)?mega\.co\.nz/#!([a-zA-Z0-9!_\-]+)' -    __version__ = "0.14" -    __description__ = """Mega.co.nz hoster plugin""" -    __author_name__ = ("RaNaN", ) -    __author_mail__ = ("ranan@pyload.org", ) - -    API_URL = "https://g.api.mega.co.nz/cs?id=%d" -    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", key) -        key_array = array("I", [a[0] ^ a[4], a[1] ^ a[5], a[2] ^ a[6], a[3] ^ a[7]]) -        return key_array - -    def callApi(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) - -        resp = self.load(self.API_URL % uid, post=json_dumps([kwargs])) -        self.logDebug("Api Response: " + resp) -        return json_loads(resp) - -    def decryptAttr(self, data, key): - -        cbc = AES.new(self.getCipherKey(key), AES.MODE_CBC, "\0" * 16) -        attr = cbc.decrypt(self.b64_decode(data)) -        self.logDebug("Decrypted Attr: " + attr) -        if not attr.startswith("MEGA"): -            self.fail(_("Decryption failed")) - -        # Data is padded, 0-bytes must be stripped -        return json_loads(attr.replace("MEGA", "").rstrip("\0").strip()) - -    def decryptFile(self, key): -        """  Decrypts the file at lastDownload` """ - -        # upper 64 bit of counter start -        n = key[16:24] - -        # convert counter to long and shift bytes -        ctr = Counter.new(128, initial_value=long(n.encode("hex"), 16) << 64) -        cipher = AES.new(self.getCipherKey(key), AES.MODE_CTR, counter=ctr) - -        self.pyfile.setStatus("decrypting") - -        file_crypted = self.lastDownload -        file_decrypted = file_crypted.rsplit(self.FILE_SUFFIX)[0] -        f = open(file_crypted, "rb") -        df = open(file_decrypted, "wb") - -        # TODO: calculate CBC-MAC for checksum - -        size = 2 ** 15  # buffer size, 32k -        while True: -            buf = f.read(size) -            if not buf: -                break - -            df.write(cipher.decrypt(buf)) - -        f.close() -        df.close() -        remove(file_crypted) - -        self.lastDownload = file_decrypted - -    def process(self, pyfile): - -        key = None - -        # match is guaranteed because plugin was chosen to handle url -        node = re.match(self.__pattern__, pyfile.url).group(2) -        if "!" in node: -            node, key = node.split("!") - -        self.logDebug("File id: %s | Key: %s" % (node, key)) - -        if not key: -            self.fail(_("No file key provided in the URL")) - -        # g is for requesting a download url -        # this is similar to the calls in the mega js app, documentation is very bad -        dl = self.callApi(a="g", g=1, p=node, ssl=1)[0] - -        if "e" in dl: -            e = dl["e"] -            # ETEMPUNAVAIL (-18): Resource temporarily not available, please try again later -            if e == -18: -                self.retry() -            else: -                self.fail(_("Error code:") + e) - -        # TODO: map other error codes, e.g -        # EACCESS (-11): Access violation (e.g., trying to write to a read-only share) - -        key = self.b64_decode(key) -        attr = self.decryptAttr(dl["at"], key) - -        pyfile.name = attr["n"] + self.FILE_SUFFIX - -        self.download(dl["g"]) -        self.decryptFile(key) - -        # Everything is finished and final name can be set -        pyfile.name = attr["n"] diff --git a/module/plugins/hoster/MegaRapidCz.py b/module/plugins/hoster/MegaRapidCz.py new file mode 100644 index 000000000..37732ade6 --- /dev/null +++ b/module/plugins/hoster/MegaRapidCz.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- + +import re + +from pycurl import HTTPHEADER + +from module.network.RequestFactory import getRequest +from module.plugins.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.55" + +    __pattern__ = r'http://(?:www\.)?(share|mega)rapid\.cz/soubor/\d+/.+' + +    __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_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): +        try: +            self.html = self.load(self.pyfile.url, decode=True) +        except BadHeader, e: +            self.account.relogin(self.user) +            self.retry(wait_time=60, reason=str(e)) + +        m = re.search(self.LINK_PATTERN, self.html) +        if m: +            link = m.group(1) +            self.logDebug("Premium link: %s" % link) +            self.download(link, disposition=True) +        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/module/plugins/hoster/MegacrypterCom.py b/module/plugins/hoster/MegacrypterCom.py index 8b72606ce..4034f7d32 100644 --- a/module/plugins/hoster/MegacrypterCom.py +++ b/module/plugins/hoster/MegacrypterCom.py @@ -3,27 +3,33 @@  import re  from module.common.json_layer import json_loads, json_dumps -from module.plugins.hoster.MegaNz import MegaNz +from module.plugins.hoster.MegaCoNz import MegaCoNz + + +class MegacrypterCom(MegaCoNz): +    __name__    = "MegacrypterCom" +    __type__    = "hoster" +    __version__ = "0.21" + +    __pattern__ = r'(https?://\w{0,10}\.?megacrypter\.com/[\w!-]+)' -class MegacrypterCom(MegaNz): -    __name__ = "MegacrypterCom" -    __type__ = "hoster" -    __pattern__ = r'(https?://[a-z0-9]{0,10}\.?megacrypter\.com/[a-zA-Z0-9!_\-]+)' -    __version__ = "0.2"      __description__ = """Megacrypter.com decrypter plugin""" -    __author_name__ = ("GonzaloSR", ) -    __author_mail__ = ("gonzalo@gonzalosr.com", ) +    __license__     = "GPLv3" +    __authors__     = [("GonzaloSR", "gonzalo@gonzalosr.com")] +      API_URL = "http://megacrypter.com/api"      FILE_SUFFIX = ".crypted" +      def callApi(self, **kwargs):          """ Dispatch a call to the api, see megacrypter.com/api_doc """          self.logDebug("JSON request: " + json_dumps(kwargs)) -        resp = self.load(self.API_URL, post=json_dumps(kwargs)) -        self.logDebug("API Response: " + resp) -        return json_loads(resp) +        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 @@ -36,15 +42,15 @@ class MegacrypterCom(MegaNz):          dl = self.callApi(link=node, m="dl")          # TODO: map error codes, implement password protection -        # if info["pass"] == true: -        #    crypted_file_key, md5_file_key = info["key"].split("#") +        # if info['pass'] is True: +        #    crypted_file_key, md5_file_key = info['key'].split("#") -        key = self.b64_decode(info["key"]) +        key = self.b64_decode(info['key']) -        pyfile.name = info["name"] + self.FILE_SUFFIX +        pyfile.name = info['name'] + self.FILE_SUFFIX -        self.download(dl["url"]) +        self.download(dl['url'])          self.decryptFile(key)          # Everything is finished and final name can be set -        pyfile.name = info["name"] +        pyfile.name = info['name'] diff --git a/module/plugins/hoster/MegareleaseOrg.py b/module/plugins/hoster/MegareleaseOrg.py index cb8c7aa01..60796c1ee 100644 --- a/module/plugins/hoster/MegareleaseOrg.py +++ b/module/plugins/hoster/MegareleaseOrg.py @@ -1,33 +1,19 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo -class MegareleaseOrg(XFileSharingPro): -    __name__ = "MegareleaseOrg" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?megarelease.org/\w{12}' -    __version__ = "0.01" -    __description__ = """Megarelease.org hoster plugin""" -    __author_name__ = ("derek3x", "stickell") -    __author_mail__ = ("derek3x@vmail.me", "l.stickell@yahoo.it") +class MegareleaseOrg(DeadHoster): +    __name__    = "MegareleaseOrg" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'https?://(?:www\.)?megarelease\.org/\w{12}' -    HOSTER_NAME = "megarelease.org" +    __description__ = """Megarelease.org hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("derek3x", "derek3x@vmail.me"), +                       ("stickell", "l.stickell@yahoo.it")] -    FILE_INFO_PATTERN = r'<font color="red">%s/(?P<N>.+)</font> \((?P<S>[^)]+)\)</font>' % __pattern__  getInfo = create_getInfo(MegareleaseOrg) diff --git a/module/plugins/hoster/MegasharesCom.py b/module/plugins/hoster/MegasharesCom.py index b73b4943c..9d8441c6f 100644 --- a/module/plugins/hoster/MegasharesCom.py +++ b/module/plugins/hoster/MegasharesCom.py @@ -1,113 +1,111 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re +  from time import time +  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class MegasharesCom(SimpleHoster): -    __name__ = "MegasharesCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?megashares.com/.*' -    __version__ = "0.24" +    __name__    = "MegasharesCom" +    __type__    = "hoster" +    __version__ = "0.27" + +    __pattern__ = r'http://(?:www\.)?(d\d{2}\.)?megashares\.com/((index\.php)?\?d\d{2}=|dl/)\w+' +      __description__ = """Megashares.com hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" - -    FILE_NAME_PATTERN = '<h1 class="black xxl"[^>]*title="(?P<N>[^"]+)">' -    FILE_SIZE_PATTERN = '<strong><span class="black">Filesize:</span></strong> (?P<S>[0-9.]+) (?P<U>[kKMG])i?B<br />' -    DOWNLOAD_URL_PATTERN = r'<div id="show_download_button_%d"[^>]*>\s*<a href="([^"]+)">' -    PASSPORT_LEFT_PATTERN = r'Your Download Passport is: <[^>]*>(\w+).*\s*You have\s*<[^>]*>\s*([0-9.]+) ([kKMG]i?B)' -    PASSPORT_RENEW_PATTERN = r'Your download passport will renew in\s*<strong>(\d+)</strong>:<strong>(\d+)</strong>:<strong>(\d+)</strong>' +    __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' -    FILE_OFFLINE_PATTERN = r'<dd class="red">(Invalid Link Request|Link has been deleted)' +      def setup(self):          self.resumeDownload = True -        self.multiDL = self.premium +        self.multiDL        = self.premium +      def handlePremium(self):          self.handleDownload(True) -    def handleFree(self): -        self.html = self.load(self.pyfile.url, decode=True) +    def handleFree(self):          if self.NO_SLOTS_PATTERN in self.html:              self.retry(wait_time=5 * 60) -        self.getFileInfo() -        #if self.pyfile.size > 576716800: self.fail("This file is too large for free download") - -        # Reactivate passport if needed -        found = re.search(self.REACTIVATE_PASSPORT_PATTERN, self.html) -        if found: -            passport_num = found.group(1) +        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 _ in xrange(5): +            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?secgfx=gfx&random_num=%s" % random_num) -                self.logInfo("Reactivating passport %s: %s %s" % (passport_num, random_num, verifyinput)) +                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)) -                url = ("http://d01.megashares.com%s&rs=check_passport_renewal" % request_uri + -                       "&rsargs[]=%s&rsargs[]=%s&rsargs[]=%s" % (verifyinput, random_num, passport_num) + -                       "&rsargs[]=replace_sec_pprenewal&rsrnd=%s" % str(int(time() * 1000))) -                self.logDebug(url) -                response = self.load(url) +                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() * 1000))}) -                if 'Thank you for reactivating your passport.' in response: +                if 'Thank you for reactivating your passport.' in res:                      self.correctCaptcha()                      self.retry()                  else:                      self.invalidCaptcha()              else: -                self.fail("Failed to reactivate passport") +                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 -        found = re.search(self.PASSPORT_LEFT_PATTERN, self.html) -        if not found: -            self.fail('Passport not found') -        self.logInfo("Download passport: %s" % found.group(1)) -        data_left = float(found.group(2)) * 1024 ** {'KB': 1, 'MB': 2, 'GB': 3}[found.group(3)] -        self.logInfo("Data left: %s %s (%d MB needed)" % (found.group(2), found.group(3), self.pyfile.size / 1048576)) +        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: -            found = re.search(self.PASSPORT_RENEW_PATTERN, self.html) -            renew = found.group(1) + found.group(2) + found.group(3) * 60 * 60 if found else 10 * 60 -            self.retry(max_tries=15, wait_time=renew, reason="Unable to get passport") +            self.retry(wait_time=600, reason=_("Passport renewal"))          self.handleDownload(False) +      def handleDownload(self, premium=False):          # Find download link; -        found = re.search(self.DOWNLOAD_URL_PATTERN % (1 if premium else 2), self.html) -        msg = '%s download URL' % ('Premium' if premium else 'Free') -        if not found: -            self.parseError(msg) +        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) -        download_url = found.group(1) +        download_url = m.group(1)          self.logDebug("%s: %s" % (msg, download_url))          self.download(download_url) diff --git a/module/plugins/hoster/MegauploadCom.py b/module/plugins/hoster/MegauploadCom.py new file mode 100644 index 000000000..7f51a8a46 --- /dev/null +++ b/module/plugins/hoster/MegauploadCom.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class MegauploadCom(DeadHoster): +    __name__    = "MegauploadCom" +    __type__    = "hoster" +    __version__ = "0.31" + +    __pattern__ = r'http://(?:www\.)?megaupload\.com/\?.*&?(d|v)=\w+' + +    __description__ = """Megaupload.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org")] + + +getInfo = create_getInfo(MegauploadCom) diff --git a/module/plugins/hoster/MegavideoCom.py b/module/plugins/hoster/MegavideoCom.py new file mode 100644 index 000000000..24905ce62 --- /dev/null +++ b/module/plugins/hoster/MegavideoCom.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class MegavideoCom(DeadHoster): +    __name__    = "MegavideoCom" +    __type__    = "hoster" +    __version__ = "0.21" + +    __pattern__ = r'http://(?:www\.)?megavideo\.com/\?.*&?(d|v)=\w+' + +    __description__ = """Megavideo.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("mkaay", "mkaay@mkaay.de")] + + +getInfo = create_getInfo(MegavideoCom) diff --git a/module/plugins/hoster/MovReelCom.py b/module/plugins/hoster/MovReelCom.py index 2dedcf13d..9bb63701c 100644 --- a/module/plugins/hoster/MovReelCom.py +++ b/module/plugins/hoster/MovReelCom.py @@ -1,27 +1,23 @@  # -*- coding: utf-8 -*- -#import re -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo -#from pycurl import FOLLOWLOCATION, LOW_SPEED_TIME +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo -class MovReelCom(XFileSharingPro): -    __name__ = "MovReelCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?movreel.com/.*' -    __version__ = "1.20" +class MovReelCom(XFSHoster): +    __name__    = "MovReelCom" +    __type__    = "hoster" +    __version__ = "1.24" + +    __pattern__ = r'http://(?:www\.)?movreel\.com/\w{12}' +      __description__ = """MovReel.com hoster plugin""" -    __author_name__ = "JorisV83" -    __author_mail__ = "jorisv83-pyload@yahoo.com" +    __license__     = "GPLv3" +    __authors__     = [("JorisV83", "jorisv83-pyload@yahoo.com")] + -    HOSTER_NAME = "movreel.com" +    HOSTER_DOMAIN = "movreel.com" -    #FILE_NAME_PATTERN = r'<b>Filename:</b>(?P<N>.*?)<br>' -    #FILE_SIZE_PATTERN = r'<b>Size:</b>(?P<S>.*?)<br>' -    FILE_INFO_PATTERN = r'<h3>(?P<N>.+?) <small><sup>(?P<S>[\d.]+) (?P<U>..)</sup> </small></h3>' -    FILE_OFFLINE_PATTERN = r'<b>File Not Found</b><br><br>' -    DIRECT_LINK_PATTERN = r'<a href="(http://[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*/.*)">Download Link</a>' -    #OVR_DOWNLOAD_LINK_PATTERN = "var file_link = '(.*)';" +    LINK_PATTERN = r'<a href="([^"]+)">Download Link'  getInfo = create_getInfo(MovReelCom) diff --git a/module/plugins/hoster/MultiDebridCom.py b/module/plugins/hoster/MultiDebridCom.py deleted file mode 100644 index 71f3df908..000000000 --- a/module/plugins/hoster/MultiDebridCom.py +++ /dev/null @@ -1,56 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ - -import re - -from module.plugins.Hoster import Hoster -from module.common.json_layer import json_loads - - -class MultiDebridCom(Hoster): -    __name__ = "MultiDebridCom" -    __version__ = "0.03" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/dl/' -    __description__ = """Multi-debrid.com hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" - -    def setup(self): -        self.chunkLimit = -1 -        self.resumeDownload = True - -    def process(self, pyfile): -        if re.match(self.__pattern__, pyfile.url): -            new_url = pyfile.url -        elif not self.account: -            self.logError(_("Please enter your %s account or deactivate this plugin") % "Multi-debrid.com") -            self.fail("No Multi-debrid.com account provided") -        else: -            self.logDebug("Original URL: %s" % pyfile.url) -            page = self.req.load('http://multi-debrid.com/api.php', -                                 get={'user': self.user, 'pass': self.account.getAccountData(self.user)['password'], -                                      'link': pyfile.url}) -            self.logDebug("JSON data: " + page) -            page = json_loads(page) -            if page['status'] != 'ok': -                self.fail('Unable to unrestrict link') -            new_url = page['link'] - -        if new_url != pyfile.url: -            self.logDebug("Unrestricted URL: " + new_url) - -        self.download(new_url, disposition=True) diff --git a/module/plugins/hoster/MultihostersCom.py b/module/plugins/hoster/MultihostersCom.py new file mode 100644 index 000000000..04192cd8e --- /dev/null +++ b/module/plugins/hoster/MultihostersCom.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +import re +from module.plugins.Hoster import Hoster + +class MultihostersCom(Hoster): +    __name__ = "MultihostersCom" +    __version__ = "0.01" +    __type__ = "hoster" +    __pattern__ = r'http://(?:www\.)?multihosters.com/.*' +    __description__ = """Multihosters.com hoster plugin""" +    __author_name__ = "tjeh" +    __author_mail__ = "tjeh@gmx.net" + +    def setup(self): +        self.resumeDownload = self.multiDL = True +        self.chunkLimit = 1 + +    def process(self, pyfile): +        if re.match(self.__pattern__, pyfile.url): +            new_url = pyfile.url +        elif not self.account: +            self.logError(("Please enter your %s account or deactivate this plugin") % "multihosters.com") +            self.fail("No multihosters.com account provided") +        else: +            self.logDebug("Old URL: %s" % pyfile.url) +            new_url = "http://multihosters.com/getFiles.aspx?ourl=" + pyfile.url +            pyfile.url = new_url +            self.logDebug("New URL: %s" % new_url) + +        if self.account.getAPIData(self.req, cmd="checklink", olink=pyfile.url) != "Alive": +            self.fail("Offline or not downloadable - contact Multihosters support") + +        header = self.account.getAPIData(self.req, just_header=True, cmd="generatedownloaddirect", olink=pyfile.url) +        if not "location" in header: +            self.fail("Unable to initialize download - contact Multihosters support") +        self.download(header['location'], disposition=True) + +        check = self.checkDownload({"error": 'action="ErrorDownload.aspx'}) +        if check == "error": +            self.fail("Error response received - contact Multihosters support") diff --git a/module/plugins/hoster/MultishareCz.py b/module/plugins/hoster/MultishareCz.py index e38bd048f..adbae2da4 100644 --- a/module/plugins/hoster/MultishareCz.py +++ b/module/plugins/hoster/MultishareCz.py @@ -1,84 +1,67 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re +  from random import random +  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class MultishareCz(SimpleHoster): -    __name__ = "MultishareCz" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?multishare.cz/stahnout/(?P<ID>\d+).*' -    __version__ = "0.34" +    __name__    = "MultishareCz" +    __type__    = "hoster" +    __version__ = "0.36" + +    __pattern__ = r'http://(?:www\.)?multishare\.cz/stahnout/(?P<ID>\d+)' +      __description__ = """MultiShare.cz hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" - -    FILE_INFO_PATTERN = ur'(?:<li>Název|Soubor): <strong>(?P<N>[^<]+)</strong><(?:/li><li|br)>Velikost: <strong>(?P<S>[^<]+)</strong>' -    FILE_OFFLINE_PATTERN = ur'<h1>Stáhnout soubor</h1><p><strong>PoÅŸadovanÜ soubor neexistuje.</strong></p>' -    FILE_SIZE_REPLACEMENTS = [(' ', '')] - -    def process(self, pyfile): -        msurl = re.match(self.__pattern__, pyfile.url) -        if msurl: -            self.fileID = msurl.group('ID') -            self.html = self.load(pyfile.url, decode=True) -            self.getFileInfo() - -            if self.premium: -                self.handlePremium() -            else: -                self.handleFree() -        else: -            self.handleOverriden() +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    SIZE_REPLACEMENTS = [(' ', '')] + +    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): -        self.download("http://www.multishare.cz/html/download_free.php?ID=%s" % self.fileID) +        self.download("http://www.multishare.cz/html/download_free.php?ID=%s" % self.info['pattern']['ID']) +      def handlePremium(self):          if not self.checkCredit(): -            self.logWarning("Not enough credit left to download file") +            self.logWarning(_("Not enough credit left to download file"))              self.resetAccount() -        self.download("http://www.multishare.cz/html/download_premium.php?ID=%s" % self.fileID) +        self.download("http://www.multishare.cz/html/download_premium.php?ID=%s" % self.info['pattern']['ID']) -    def handleOverriden(self): -        if not self.premium: -            self.fail("Only premium users can download from other hosters") +    def handleMulti(self):          self.html = self.load('http://www.multishare.cz/html/mms_ajax.php', post={"link": self.pyfile.url}, decode=True) -        self.getFileInfo() + +        self.checkInfo()          if not self.checkCredit(): -            self.fail("Not enough credit left to download file") +            self.fail(_("Not enough credit left to download file")) + +        url    = "http://dl%d.mms.multishare.cz/html/mms_process.php" % round(random() * 10000 * random()) +        params = {"u_ID": self.acc_info['u_ID'], "u_hash": self.acc_info['u_hash'], "link": self.pyfile.url} -        url = "http://dl%d.mms.multishare.cz/html/mms_process.php" % round(random() * 10000 * random()) -        params = {"u_ID": self.acc_info["u_ID"], "u_hash": self.acc_info["u_hash"], "link": self.pyfile.url}          self.logDebug(url, params) + +        self.link = True          self.download(url, get=params) +      def checkCredit(self):          self.acc_info = self.account.getAccountInfo(self.user, True) -        self.logInfo("User %s has %i MB left" % (self.user, self.acc_info["trafficleft"] / 1024)) -        return self.pyfile.size / 1024 <= self.acc_info["trafficleft"] +        self.logInfo(_("User %s has %i MB left") % (self.user, self.acc_info['trafficleft'] / 1024)) + +        return self.pyfile.size / 1024 <= self.acc_info['trafficleft']  getInfo = create_getInfo(MultishareCz) diff --git a/module/plugins/hoster/MyfastfileCom.py b/module/plugins/hoster/MyfastfileCom.py new file mode 100644 index 000000000..b5b9ec820 --- /dev/null +++ b/module/plugins/hoster/MyfastfileCom.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +import re + +from module.common.json_layer import json_loads +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo + + +class MyfastfileCom(MultiHoster): +    __name__    = "MyfastfileCom" +    __type__    = "hoster" +    __version__ = "0.07" + +    __pattern__ = r'http://\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/dl/' + +    __description__ = """Myfastfile.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    def setup(self): +        self.chunkLimit     = -1 +        self.resumeDownload = True + + +    def handlePremium(self): +        self.logDebug("Original URL: %s" % self.pyfile.url) + +        page = self.load('http://myfastfile.com/api.php', +                         get={'user': self.user, 'pass': self.account.getAccountData(self.user)['password'], +                              'link': self.pyfile.url}) +        self.logDebug("JSON data: " + page) +        page = json_loads(page) +        if page['status'] != 'ok': +            self.fail(_("Unable to unrestrict link")) +        self.link = page['link'] + +        if self.link != self.pyfile.url: +            self.logDebug("Unrestricted URL: " + self.link) + + +getInfo = create_getInfo(MyfastfileCom) diff --git a/module/plugins/hoster/MyvideoDe.py b/module/plugins/hoster/MyvideoDe.py index 21d95d092..8af4a9a61 100644 --- a/module/plugins/hoster/MyvideoDe.py +++ b/module/plugins/hoster/MyvideoDe.py @@ -1,18 +1,22 @@  # -*- coding: utf-8 -*-  import re +  from module.plugins.Hoster import Hoster  from module.unescape import unescape  class MyvideoDe(Hoster): -    __name__ = "MyvideoDe" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?myvideo.de/watch/' -    __version__ = "0.9" +    __name__    = "MyvideoDe" +    __type__    = "hoster" +    __version__ = "0.90" + +    __pattern__ = r'http://(?:www\.)?myvideo\.de/watch/' +      __description__ = """Myvideo.de hoster plugin""" -    __author_name__ = "spoob" -    __author_mail__ = "spoob@pyload.org" +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org")] +      def process(self, pyfile):          self.pyfile = pyfile @@ -20,19 +24,23 @@ class MyvideoDe(Hoster):          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>" +        file_name_pattern = r'<h1 class=\'globalHd\'>(.*)</h1>'          return 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) diff --git a/module/plugins/hoster/NahrajCz.py b/module/plugins/hoster/NahrajCz.py new file mode 100644 index 000000000..6b5699408 --- /dev/null +++ b/module/plugins/hoster/NahrajCz.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class NahrajCz(DeadHoster): +    __name__    = "NahrajCz" +    __type__    = "hoster" +    __version__ = "0.21" + +    __pattern__ = r'http://(?:www\.)?nahraj\.cz/content/download/.+' + +    __description__ = """Nahraj.cz hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +getInfo = create_getInfo(NahrajCz) diff --git a/module/plugins/hoster/NarodRu.py b/module/plugins/hoster/NarodRu.py index 6f08e6207..21d4e3e3d 100644 --- a/module/plugins/hoster/NarodRu.py +++ b/module/plugins/hoster/NarodRu.py @@ -1,71 +1,59 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re +  from random import random +  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class NarodRu(SimpleHoster): -    __name__ = "NarodRu" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?narod(\.yandex)?\.ru/(disk|start/[0-9]+\.\w+-narod\.yandex\.ru)/(?P<ID>\d+)/.+' -    __version__ = "0.1" +    __name__    = "NarodRu" +    __type__    = "hoster" +    __version__ = "0.11" + +    __pattern__ = r'http://(?:www\.)?narod(\.yandex)?\.ru/(disk|start/\d+\.\w+-narod\.yandex\.ru)/(?P<ID>\d+)/.+' +      __description__ = """Narod.ru hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] -    FILE_NAME_PATTERN = r'<dt class="name">(?:<[^<]*>)*(?P<N>[^<]+)</dt>' -    FILE_SIZE_PATTERN = r'<dd class="size">(?P<S>\d[^<]*)</dd>' -    FILE_OFFLINE_PATTERN = r'<title>404</title>|Ѐайл ÑЎалеМ Ñ ÑеÑвОÑа|ÐакПМÑОлÑÑ ÑÑПк Ñ
ÑÐ°ÐœÐµÐœÐžÑ Ñайла\.' -    FILE_SIZE_REPLACEMENTS = [(u'ÐÐ', 'KB'), (u'ÐÐ', 'MB'), (u'ÐÐ', 'GB')] -    FILE_URL_REPLACEMENTS = [("narod.yandex.ru/", "narod.ru/"), -                             (r"/start/[0-9]+\.\w+-narod\.yandex\.ru/([0-9]{6,15})/\w+/(\w+)", r"/disk/\1/\2")] +    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>' -    DOWNLOAD_LINK_PATTERN = r'<a class="h-link" rel="yandex_bar" href="(.+?)">' +    LINK_PATTERN = r'<a class="h-link" rel="yandex_bar" href="(.+?)">' +      def handleFree(self): -        for _ in xrange(5): +        for _i in xrange(5):              self.html = self.load('http://narod.ru/disk/getcapchaxml/?rnd=%d' % int(random() * 777)) -            found = re.search(self.CAPTCHA_PATTERN, self.html) -            if not found: -                self.parseError('Captcha') +            m = re.search(self.CAPTCHA_PATTERN, self.html) +            if m is None: +                self.error(_("Captcha"))              post_data = {"action": "sendcapcha"} -            captcha_url, post_data['key'] = found.groups() +            captcha_url, post_data['key'] = m.groups()              post_data['rep'] = self.decryptCaptcha(captcha_url)              self.html = self.load(self.pyfile.url, post=post_data, decode=True) -            found = re.search(self.DOWNLOAD_LINK_PATTERN, self.html) -            if found: -                url = 'http://narod.ru' + found.group(1) +            m = re.search(self.LINK_PATTERN, self.html) +            if m: +                url = 'http://narod.ru' + m.group(1)                  self.correctCaptcha()                  break              elif u'<b class="error-msg"><strong>ÐÑОблОÑÑ?</strong>' in self.html:                  self.invalidCaptcha()              else: -                self.parseError('Download link') +                self.error(_("Download link"))          else: -            self.fail("No valid captcha code entered") +            self.fail(_("No valid captcha code entered")) -        self.logDebug('Download link: ' + url)          self.download(url) diff --git a/module/plugins/hoster/NetloadIn.py b/module/plugins/hoster/NetloadIn.py index 4b8842d18..f5c5ee802 100644 --- a/module/plugins/hoster/NetloadIn.py +++ b/module/plugins/hoster/NetloadIn.py @@ -1,17 +1,20 @@  # -*- coding: utf-8 -*-  import re + +from urlparse import urljoin  from time import sleep, time -from module.plugins.Hoster import Hoster  from module.network.RequestFactory import getURL +from module.plugins.Hoster import Hoster  from module.plugins.Plugin import chunks +from module.plugins.internal.CaptchaService import ReCaptcha  def getInfo(urls):      ##  returns list of tupels (name, size (in bytes), status (see FileDatabase), url) -    apiurl = "http://api.netload.in/info.php?auth=Zf9SnQh9WiReEsb18akjvQGqT0I830e8&bz=1&md5=1&file_id=" +    apiurl = "http://api.netload.in/info.php"      id_regex = re.compile(NetloadIn.__pattern__)      urls_per_query = 80 @@ -22,13 +25,19 @@ def getInfo(urls):              if match:                  ids = ids + match.group(1) + ";" -        api = getURL(apiurl + ids, decode=True) +        api = getURL(apiurl, +                     get={'auth'   : "Zf9SnQh9WiReEsb18akjvQGqT0I830e8", +                          'bz'     : 1, +                          'md5'    : 1, +                          'file_id': ids}, +                     decode=True)          if api is None or len(api) < 10: -            print "Netload prefetch: failed " +            self.logDebug("Prefetch failed")              return +          if api.find("unknown_auth") >= 0: -            print "Netload prefetch: Outdated auth code " +            self.logDebug("Outdated auth code")              return          result = [] @@ -36,45 +45,59 @@ def getInfo(urls):          for i, r in enumerate(api.splitlines()):              try:                  tmp = r.split(";") +                  try:                      size = int(tmp[2]) -                except: +                except Exception:                      size = 0 -                result.append((tmp[1], size, 2 if tmp[3] == "online" else 1, chunk[i])) -            except: -                print "Netload prefetch: Error while processing response: " -                print r + +                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" -    __pattern__ = r'https?://(?:[^/]*\.)?netload\.in/(?:datei(.*?)(?:\.htm|/)|index.php?id=10&file_id=)' -    __version__ = "0.45" +    __name__    = "NetloadIn" +    __type__    = "hoster" +    __version__ = "0.47" + +    __pattern__ = r'https?://(?:[^/]*\.)?netload\.in/(?:datei(.*?)(?:\.htm|/)|index\.php?id=10&file_id=)' +      __description__ = """Netload.in hoster plugin""" -    __author_name__ = ("spoob", "RaNaN", "Gregy") -    __author_mail__ = ("spoob@pyload.org", "ranan@pyload.org", "gregy@gregy.cz") +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org"), +                       ("RaNaN", "ranan@pyload.org"), +                       ("Gregy", "gregy@gregy.cz")] +      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.download_api_data() -        if self.api_data and self.api_data["filename"]: -            self.pyfile.name = self.api_data["filename"] +        if self.api_data and self.api_data['filename']: +            self.pyfile.name = self.api_data['filename']          if self.premium: -            self.logDebug("Netload: Use Premium Account") -            settings = self.load("http://www.netload.in/index.php?id=2&lang=en") +            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 @@ -84,13 +107,14 @@ class NetloadIn(Hoster):          if self.download_html():              return True          else: -            self.fail("Failed") +            self.fail(_("Failed"))              return False +      def download_api_data(self, n=0): -        url = self.url +        url      = self.url          id_regex = re.compile(self.__pattern__) -        match = id_regex.search(url) +        match    = id_regex.search(url)          if match:              #normalize url @@ -101,25 +125,28 @@ class NetloadIn(Hoster):              return          apiurl = "http://api.netload.in/info.php" -        src = self.load(apiurl, cookies=False, +        html = self.load(apiurl, cookies=False,                          get={"file_id": match.group(1), "auth": "Zf9SnQh9WiReEsb18akjvQGqT0I830e8", "bz": "1",                               "md5": "1"}, decode=True).strip() -        if not src and n <= 3: +        if not html and n <= 3:              sleep(0.2)              self.download_api_data(n + 1)              return -        self.logDebug("Netload: APIDATA: " + src) +        self.logDebug("APIDATA: " + html) +          self.api_data = {} -        if src and ";" in src and src not in ("unknown file_data", "unknown_server_data", "No input file specified."): -            lines = src.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() + +        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 @@ -128,20 +155,34 @@ class NetloadIn(Hoster):          else:              self.api_data = False +      def final_wait(self, page):          wait_time = self.get_wait_time(page) +          self.setWait(wait_time) -        self.logDebug("Netload: final wait %d seconds" % 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(page) or 30) +            self.wait() +            return True +        else: +            return False + +      def download_html(self): -        self.logDebug("Netload: Entering download_html")          page = self.load(self.url, decode=True) -        t = time() + 30          if "/share/templates/download_hddcrash.tpl" in page: -            self.logError("Netload HDD Crash") +            self.logError(_("Netload HDD Crash"))              self.fail(_("File temporarily not available"))          if not self.api_data: @@ -150,105 +191,104 @@ class NetloadIn(Hoster):              if "* The file was deleted" in page:                  self.offline() -            name = re.search(r'class="dl_first_filename">([^<]+)', page, re.MULTILINE) -            # the found filename is not truncated  +            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(10): +        for i in xrange(5):              if not page:                  page = self.load(self.url)                  t = time() + 30              if "/share/templates/download_hddcrash.tpl" in page: -                self.logError("Netload HDD Crash") +                self.logError(_("Netload HDD Crash"))                  self.fail(_("File temporarily not available")) -            self.logDebug("Netload: try number %d " % i) +            self.logDebug("Try number %d " % i)              if ">Your download is being prepared.<" in page: -                self.logDebug("Netload: We will prepare your download") +                self.logDebug("We will prepare your download")                  self.final_wait(page)                  return True -            if ">An access request has been made from IP address <" in page: -                wait = self.get_wait_time(page) -                if wait == 0: -                    self.logDebug("Netload: Wait was 0 setting 30") -                    wait = 30 -                self.logInfo(_("Netload: waiting between downloads %d s." % wait)) -                self.wantReconnect = True -                self.setWait(wait) -                self.wait() -                return self.download_html() - -            self.logDebug("Netload: Trying to find captcha") +            self.logDebug("Trying to find captcha")              try: -                url_captcha_html = "http://netload.in/" + re.search('(index.php\?id=10&.*&captcha=1)', -                                                                    page).group(1).replace("amp;", "") -            except: +                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 -                continue -            try: -                page = self.load(url_captcha_html, cookies=True) -                captcha_url = "http://netload.in/" + re.search('(share/includes/captcha.php\?t=\d*)', page).group(1) -            except: -                self.logDebug("Netload: Could not find captcha, try again from beginning") -                captchawaited = False -                continue - -            file_id = re.search('<input name="file_id" type="hidden" value="(.*)" />', page).group(1) -            if not captchawaited: -                wait = self.get_wait_time(page) -                if i == 0: -                    self.pyfile.waitUntil = time()  # dont wait contrary to time on website -                else: -                    self.pyfile.waitUntil = t -                self.logInfo(_("Netload: waiting for captcha %d s.") % (self.pyfile.waitUntil - time())) -                #self.setWait(wait) +            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): +            challenge, response = recaptcha.challenge() + +            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() -                captchawaited = True -            captcha = self.decryptCaptcha(captcha_url) -            page = self.load("http://netload.in/index.php?id=10", post={"file_id": file_id, "captcha_check": captcha}, -                             cookies=True) +                self.url = download_url +                return True -        return False      def get_file_url(self, page):          try: -            file_url_pattern = r"<a class=\"Orange_Link\" href=\"(http://.+)\".?>Or click here" +            file_url_pattern = r'<a class="Orange_Link" href="(http://.+)".?>Or click here'              attempt = re.search(file_url_pattern, page)              if attempt is not None:                  return attempt.group(1)              else: -                self.logDebug("Netload: Backup try for final link") -                file_url_pattern = r"<a href=\"(.+)\" class=\"Orange_Link\">Click here" +                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: -            self.logDebug("Netload: Getting final link failed") + +        except Exception, e: +            self.logDebug("Getting final link failed", e.message)              return None +      def get_wait_time(self, page): -        wait_seconds = int(re.search(r"countdown\((.+),'change\(\)'\)", page).group(1)) / 100 -        return wait_seconds +        return int(re.search(r"countdown\((.+),'change\(\)'\)", page).group(1)) / 100 -    def proceed(self, url): -        self.logDebug("Netload: Downloading..") +    def proceed(self, url):          self.download(url, disposition=True) -        check = self.checkDownload({"empty": re.compile(r"^$"), "offline": re.compile("The file was deleted")}) - +        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/module/plugins/hoster/NoPremiumPl.py b/module/plugins/hoster/NoPremiumPl.py new file mode 100644 index 000000000..f4f7ba56a --- /dev/null +++ b/module/plugins/hoster/NoPremiumPl.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.SimpleHoster import SimpleHoster +from module.common.json_layer import json_loads as loads + + +class NoPremiumPl(SimpleHoster): +    __name__ = "NoPremiumPl" +    __version__ = "0.01" +    __type__ = "hoster" + +    __pattern__ = r"https?://direct\.nopremium\.pl.*" +    __description__ = "NoPremium.pl 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" +    } + +    _usr = False +    _pwd = False + +    def setup(self): +        self.resumeDownload = True +        self.multiDL = True + +    def get_username_password(self): +        if not self.account: +            self.fail(_("Please enter your %s account or deactivate this plugin") % "NoPremium.pl") +        else: +            self._usr = self.account.getAccountData(self.user).get('usr') +            self._pwd = self.account.getAccountData(self.user).get('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 process(self, pyfile): +        self.get_username_password() +        try: +            data = self.runFileQuery(pyfile.url, 'fileinfo') +        except Exception: +            self.logDebug("runFileQuery error") +            self.tempOffline() + +        try: +            parsed = 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 webiste \ +                    directly. Update this plugin." % parsed["hosting"]) + +        pyfile.name = parsed["filename"] +        pyfile.size = parsed["filesize"] + +        try: +            result_dl = self.runFileQuery(pyfile.url, 'filedownload') +        except Exception: +            self.logDebug("runFileQuery error #2") +            self.tempOffline() + +        self.download(result_dl, disposition=True) diff --git a/module/plugins/hoster/NosuploadCom.py b/module/plugins/hoster/NosuploadCom.py index 1de734222..8a03d7090 100644 --- a/module/plugins/hoster/NosuploadCom.py +++ b/module/plugins/hoster/NosuploadCom.py @@ -2,24 +2,29 @@  import re -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo -class NosuploadCom(XFileSharingPro): -    __name__ = "NosuploadCom" -    __type__ = "hoster" -    __version__ = "0.1" +class NosuploadCom(XFSHoster): +    __name__    = "NosuploadCom" +    __type__    = "hoster" +    __version__ = "0.31" +      __pattern__ = r'http://(?:www\.)?nosupload\.com/\?d=\w{12}' +      __description__ = """Nosupload.com hoster plugin""" -    __author_name__ = "igel" -    __author_mail__ = "igelkun@myopera.com" +    __license__     = "GPLv3" +    __authors__     = [("igel", "igelkun@myopera.com")] + -    HOSTER_NAME = "nosupload.com" +    HOSTER_DOMAIN = "nosupload.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>' -    FILE_SIZE_PATTERN = r'<p><strong>Size:</strong> (?P<S>[0-9\.]+) (?P<U>[kKMG]?B)</p>' -    DIRECT_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() @@ -27,13 +32,13 @@ class NosuploadCom(XFileSharingPro):          # stage2: wait some time and press the "Download File" button          data = self.getPostParameters() -        wait_time = re.search(self.WAIT_PATTERN, self.html, re.MULTILINE | re.DOTALL).group(1) -        self.logDebug("hoster told us to wait %s seconds" % wait_time) +        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, ref=True, decode=True)          # stage3: get the download link -        return re.search(self.DIRECT_LINK_PATTERN, self.html, re.S).group(1) +        return re.search(self.LINK_PATTERN, self.html, re.S).group(1)  getInfo = create_getInfo(NosuploadCom) diff --git a/module/plugins/hoster/NovafileCom.py b/module/plugins/hoster/NovafileCom.py index c552c166c..9754ceed1 100644 --- a/module/plugins/hoster/NovafileCom.py +++ b/module/plugins/hoster/NovafileCom.py @@ -1,30 +1,31 @@  # -*- coding: utf-8 -*- - -# Test links (random.bin): +# +# Test links:  # http://novafile.com/vfun4z6o2cit  # http://novafile.com/s6zrr5wemuz4 -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo + +class NovafileCom(XFSHoster): +    __name__    = "NovafileCom" +    __type__    = "hoster" +    __version__ = "0.05" -class NovafileCom(XFileSharingPro): -    __name__ = "NovafileCom" -    __type__ = "hoster"      __pattern__ = r'http://(?:www\.)?novafile\.com/\w{12}' -    __version__ = "0.02" +      __description__ = """Novafile.com hoster plugin""" -    __author_name__ = ("zoidberg", "stickell") -    __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it") +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + -    HOSTER_NAME = "novafile.com" +    HOSTER_DOMAIN = "novafile.com" -    FILE_SIZE_PATTERN = r'<div class="size">(?P<S>.+?)</div>'      ERROR_PATTERN = r'class="alert[^"]*alert-separate"[^>]*>\s*(?:<p>)?(.*?)\s*</' -    DIRECT_LINK_PATTERN = r'<a href="(http://s\d+\.novafile\.com/.*?)" class="btn btn-green">Download File</a>' -    WAIT_PATTERN = r'<p>Please wait <span id="count"[^>]*>(\d+)</span> seconds</p>' +    WAIT_PATTERN  = r'<p>Please wait <span id="count"[^>]*>(\d+)</span> seconds</p>' -    def setup(self): -        self.multiDL = False +    LINK_PATTERN = r'<a href="(http://s\d+\.novafile\.com/.*?)" class="btn btn-green">Download File</a>'  getInfo = create_getInfo(NovafileCom) diff --git a/module/plugins/hoster/NowDownloadEu.py b/module/plugins/hoster/NowDownloadEu.py deleted file mode 100644 index cc1a10da0..000000000 --- a/module/plugins/hoster/NowDownloadEu.py +++ /dev/null @@ -1,72 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" - -import re -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo -from module.utils import fixup - - -class NowDownloadEu(SimpleHoster): -    __name__ = "NowDownloadEu" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?nowdownload\.(ch|co|eu|sx)/(dl/|download\.php\?id=)(?P<ID>\w+)' -    __version__ = "0.05" -    __description__ = """NowDownload.ch hoster plugin""" -    __author_name__ = ("godofdream", "Walter Purcaro") -    __author_mail__ = ("soilfiction@gmail.com", "vuolter@gmail.com") - -    FILE_INFO_PATTERN = r'Downloading</span> <br> (?P<N>.*) (?P<S>[0-9,.]+) (?P<U>[kKMG])i?B </h4>' -    FILE_OFFLINE_PATTERN = r'(This file does not exist!)' -    FILE_TOKEN_PATTERN = r'"(/api/token\.php\?token=[a-z0-9]+)"' -    FILE_CONTINUE_PATTERN = r'"(/dl2/[a-z0-9]+/[a-z0-9]+)"' -    FILE_WAIT_PATTERN = r'\.countdown\(\{until: \+(\d+),' -    FILE_DOWNLOAD_LINK = r'"(http://f\d+\.nowdownload\.ch/dl/[a-z0-9]+/[a-z0-9]+/[^<>"]*?)"' - -    FILE_NAME_REPLACEMENTS = [("&#?\w+;", fixup), (r'<[^>]*>', '')] - -    def setup(self): -        self.multiDL = self.resumeDownload = True -        self.chunkLimit = -1 - -    def handleFree(self): -        tokenlink = re.search(self.FILE_TOKEN_PATTERN, self.html) -        continuelink = re.search(self.FILE_CONTINUE_PATTERN, self.html) -        if not tokenlink or not continuelink: -            self.fail('Plugin out of Date') - -        found = re.search(self.FILE_WAIT_PATTERN, self.html) -        if found: -            wait = int(found.group(1)) -        else: -            wait = 60 - -        baseurl = "http://www.nowdownload.ch" -        self.html = self.load(baseurl + str(tokenlink.group(1))) -        self.wait(wait) - -        self.html = self.load(baseurl + str(continuelink.group(1))) - -        url = re.search(self.FILE_DOWNLOAD_LINK, self.html) -        if not url: -            self.fail('Download Link not Found (Plugin out of Date?)') -        self.logDebug('Download link: ' + str(url.group(1))) -        self.download(str(url.group(1))) - - -getInfo = create_getInfo(NowDownloadEu) diff --git a/module/plugins/hoster/NowDownloadSx.py b/module/plugins/hoster/NowDownloadSx.py new file mode 100644 index 000000000..d2ae08954 --- /dev/null +++ b/module/plugins/hoster/NowDownloadSx.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- + +import re + +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo +from module.utils import fixup + + +class NowDownloadSx(SimpleHoster): +    __name__    = "NowDownloadSx" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?nowdownload\.(at|ch|co|eu|sx)/(dl/|download\.php\?id=)\w+' + +    __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_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): +        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))) + +        url = re.search(self.LINK_PATTERN, self.html) +        if url is None: +            self.error(_("Download link not found")) + +        self.download(str(url.group(1))) + + +getInfo = create_getInfo(NowDownloadSx) diff --git a/module/plugins/hoster/NowVideoSx.py b/module/plugins/hoster/NowVideoSx.py new file mode 100644 index 000000000..d0777ca4b --- /dev/null +++ b/module/plugins/hoster/NowVideoSx.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +import re + +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo + + +class NowVideoSx(SimpleHoster): +    __name__    = "NowVideoSx" +    __type__    = "hoster" +    __version__ = "0.08" + +    __pattern__ = r'http://(?:www\.)?nowvideo\.(at|ch|co|eu|sx)/(video|mobile/#/videos)/(?P<ID>\w+)' + +    __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): +        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.download(m.group(1)) + + +getInfo = create_getInfo(NowVideoSx) diff --git a/module/plugins/hoster/OboomCom.py b/module/plugins/hoster/OboomCom.py index 1e0508d32..588d8f64a 100644 --- a/module/plugins/hoster/OboomCom.py +++ b/module/plugins/hoster/OboomCom.py @@ -1,52 +1,79 @@  # -*- coding: utf-8 -*- - -# Test link: +# +# Test links:  # https://www.oboom.com/B7CYZIEB/10Mio.dat  import re +from module.common.json_layer import json_loads  from module.plugins.Hoster import Hoster  from module.plugins.internal.CaptchaService import ReCaptcha -from module.common.json_layer import json_loads  class OboomCom(Hoster): -    __name__ = "OboomCom" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?oboom\.com/(#(id=|/)?)?(?P<ID>[A-Z0-9]{8})' -    __version__ = "0.1" +    __name__    = "OboomCom" +    __type__    = "hoster" +    __version__ = "0.30" + +    __pattern__ = r'https?://(?:www\.)?oboom\.com/(#(id=|/)?)?(?P<ID>\w{8})' +      __description__ = """oboom.com hoster plugin""" -    __author_name__ = "stanley" -    __author_mail__ = "stanley.foerster@gmail.com" +    __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 not get: +        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"] +                self.sessionToken = accountInfo['session']              else: -                self.fail("Could not retrieve premium session") +                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]) +                self.fail(_("Could not retrieve token for guest session. Error code: %s") % result[0]) +      def solveCaptcha(self):          recaptcha = ReCaptcha(self) -        for _ in xrange(5): + +        for _i in xrange(5):              challenge, response = recaptcha.challenge(self.RECAPTCHA_KEY)              apiUrl = "https://www.oboom.com/1.0/download/ticket"              params = {"recaptcha_challenge_field": challenge, @@ -62,23 +89,26 @@ class OboomCom(Hoster):                  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") +                    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], reconnect=True) +                    self.setWait(result[1], True)                  self.wait()                  self.retry(5)          else:              self.invalidCaptcha() -            self.fail("Received invalid captcha 5 times") +            self.fail(_("Received invalid captcha 5 times")) +      def getFileInfo(self, token, fileId):          apiUrl = "https://api.oboom.com/1.0/info" @@ -87,43 +117,29 @@ class OboomCom(Hoster):          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"] +            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])) +            self.fail(_("Could not retrieve file info. Error code %s: %s") % (result[0], result[1])) +      def getDownloadTicket(self): -        apiUrl = "https://api.oboom.com/1.0/dl" +        apiUrl = "https://api.oboom.com/1/dl"          params = {"item": self.fileId, "http_errors": 0}          if self.premium: -            params["token"] = self.sessionToken +            params['token'] = self.sessionToken          else: -            params["token"] = self.downloadToken -            params["auth"] = self.downloadAuth +            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]) - -    def setup(self): -        self.chunkLimit = 1 -        self.multiDL = 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}) +            self.fail(_("Could not retrieve download ticket. Error code: %s") % result[0]) diff --git a/module/plugins/hoster/OneFichierCom.py b/module/plugins/hoster/OneFichierCom.py index 41f3e4b11..f0e16a101 100644 --- a/module/plugins/hoster/OneFichierCom.py +++ b/module/plugins/hoster/OneFichierCom.py @@ -1,86 +1,60 @@  # -*- coding: utf-8 -*- -# Test links (random.bin): -# http://5pnm24ltcw.1fichier.com/ -  import re  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class OneFichierCom(SimpleHoster): -    __name__ = "OneFichierCom" -    __type__ = "hoster" -    __pattern__ = r'(http://(?P<id>\w+)\.(?P<host>(1fichier|d(es)?fichiers|pjointe)\.(com|fr|net|org)|(cjoint|mesfichiers|piecejointe|oi)\.(org|net)|tenvoi\.(com|org|net)|dl4free\.com|alterupload\.com|megadl.fr))/?' -    __version__ = "0.61" -    __description__ = """1fichier.com hoster plugin""" -    __author_name__ = ("fragonib", "the-razer", "zoidberg", "imclem", "stickell", "Elrick69") -    __author_mail__ = ("fragonib[AT]yahoo[DOT]es", "daniel_ AT gmx DOT net", "zoidberg@mujmail.cz", -                       "imclem on github", "l.stickell@yahoo.it", "elrick69[AT]rocketmail[DOT]com") +    __name__    = "OneFichierCom" +    __type__    = "hoster" +    __version__ = "0.75" -    FILE_NAME_PATTERN = r'">Filename :</th>\s*<td>(?P<N>[^<]+)</td>' -    FILE_SIZE_PATTERN = r'<th>Size :</th>\s*<td>(?P<S>[^<]+)</td>' -    FILE_OFFLINE_PATTERN = r'The (requested)? file (could not be found|has been deleted)' +    __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+))?' -    FILE_URL_REPLACEMENTS = [(__pattern__, r'http://\g<id>.\g<host>/en/')] +    __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", None), +                       ("stickell", "l.stickell@yahoo.it"), +                       ("Elrick69", "elrick69[AT]rocketmail[DOT]com"), +                       ("Walter Purcaro", "vuolter@gmail.com")] -    WAITING_PATTERN = "Warning ! Without premium status, you must wait between each downloads" -    NOT_PARALLEL = r"Warning ! Without premium status, you can download only one file at a time" -    WAIT_TIME = 10 * 60  # Retry time between each free download -    RETRY_TIME = 15 * 60  # Default retry time in seconds (if detected parallel download) -    def setup(self): -        self.multiDL = self.premium -        self.resumeDownload = True +    NAME_PATTERN = r'>FileName :</td>\s*<td.*>(?P<N>.+?)<' +    SIZE_PATTERN = r'>Size :</td>\s*<td.*>(?P<S>[\d.,]+) (?P<U>[\w^_]+)' -    def handleFree(self): -        self.html = self.load(self.pyfile.url, decode=True) +    OFFLINE_PATTERN = r'File not found !\s*<' -        if self.WAITING_PATTERN in self.html: -            self.logInfo('You have to wait been each free download! Retrying in %d seconds.' % self.WAIT_TIME) -            self.waitAndRetry(self.WAIT_TIME) -        else:  # detect parallel download -            found = re.search(self.NOT_PARALLEL, self.html) -            if found: -                self.waitAndRetry(self.RETRY_TIME) +    COOKIES = [("1fichier.com", "LG", "en")] -        url, inputs = self.parseHtmlForm('action="http://%s' % self.file_info['id']) -        if not url: -            self.parseError("Download link not found") +    WAIT_PATTERN = r'>You must wait (\d+) minutes' -        # Check for protection  -        if "pass" in inputs: -            inputs['pass'] = self.getPassword() -        inputs['submit'] = "Download" -        self.download(url, post=inputs) +    def setup(self): +        self.multiDL        = self.premium +        self.resumeDownload = True -        # Check download  -        self.checkDownloadedFile() -    def handlePremium(self): -        url, inputs = self.parseHtmlForm('action="http://%s' % self.file_info['id']) +    def handleFree(self): +        id = self.info['pattern']['ID1'] or self.info['pattern']['ID2'] +        url, inputs = self.parseHtmlForm('action="https://1fichier.com/\?%s' % id) +          if not url: -            self.parseError("Download link not found") +            self.fail(_("Download link not found")) -        # Check for protection          if "pass" in inputs:              inputs['pass'] = self.getPassword() +          inputs['submit'] = "Download"          self.download(url, post=inputs) -        # Check download -        self.checkDownloadedFile() -    def checkDownloadedFile(self): -        check = self.checkDownload({"wait": self.WAITING_PATTERN}) -        if check == "wait": -            self.waitAndRetry(int(self.lastcheck.group(1)) * 60) - -    def waitAndRetry(self, wait_time): -        self.wait(wait_time, True) -        self.retry() +    def handlePremium(self): +        return self.handleFree()  getInfo = create_getInfo(OneFichierCom) diff --git a/module/plugins/hoster/OronCom.py b/module/plugins/hoster/OronCom.py new file mode 100644 index 000000000..7e8423ec9 --- /dev/null +++ b/module/plugins/hoster/OronCom.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class OronCom(DeadHoster): +    __name__    = "OronCom" +    __type__    = "hoster" +    __version__ = "0.14" + +    __pattern__ = r'https?://(?:www\.)?oron\.com/\w{12}' + +    __description__ = """Oron.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("chrox", "chrox@pyload.org"), +                       ("DHMH", "DHMH@pyload.org")] + + +getInfo = create_getInfo(OronCom) diff --git a/module/plugins/hoster/OverLoadMe.py b/module/plugins/hoster/OverLoadMe.py index aaa1442e4..54f869635 100644 --- a/module/plugins/hoster/OverLoadMe.py +++ b/module/plugins/hoster/OverLoadMe.py @@ -1,78 +1,82 @@  # -*- coding: utf-8 -*-  import re -from urllib import unquote +  from random import randrange +from urllib import unquote -from module.plugins.Hoster import Hoster  from module.common.json_layer import json_loads +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo  from module.utils import parseFileSize -class OverLoadMe(Hoster): -    __name__ = "OverLoadMe" -    __version__ = "0.01" -    __type__ = "hoster" -    __pattern__ = r'https?://.*overload\.me.*' +class OverLoadMe(MultiHoster): +    __name__    = "OverLoadMe" +    __type__    = "hoster" +    __version__ = "0.06" + +    __pattern__ = r'https?://.*overload\.me/.+' +      __description__ = """Over-Load.me hoster plugin""" -    __author_name__ = "marley" -    __author_mail__ = "marley@over-load.me" +    __license__     = "GPLv3" +    __authors__     = [("marley", "marley@over-load.me")] +      def getFilename(self, url):          try:              name = unquote(url.rsplit("/", 1)[1])          except IndexError:              name = "Unknown_Filename..." -        if name.endswith("..."):  # incomplete filename, append random stuff +        if name.endswith("..."):  #: incomplete filename, append random stuff              name += "%s.tmp" % randrange(100, 999)          return name +      def setup(self): -        self.chunkLimit = 5 +        self.chunkLimit     = 5          self.resumeDownload = True -    def process(self, pyfile): -        if re.match(self.__pattern__, pyfile.url): -            new_url = pyfile.url -        elif not self.account: -            self.logError(_("Please enter your %s account or deactivate this plugin") % "Over-Load") -            self.fail("No Over-Load account provided") -        else: -            self.logDebug("Old URL: %s" % pyfile.url) -            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) +    def handlePremium(self): +        data = self.account.getAccountData(self.user) + +        page = self.load("https://api.over-load.me/getdownload.php", +                         get={"auth": data['password'], "link": self.pyfile.url}) +        data = json_loads(page) -            self.logDebug("Returned Data: %s" % data) +        self.logDebug("Returned Data: %s" % data) -            if data["err"] == 1: -                self.logWarning(data["msg"]) -                self.tempOffline() -            else: -                if pyfile.name is not None and pyfile.name.endswith('.tmp') and data["filename"]: -                    pyfile.name = data["filename"] -                    pyfile.size = parseFileSize(data["filesize"]) -                new_url = data["downloadlink"] +        if data['error'] == 1: +            self.logWarning(data['msg']) +            self.tempOffline() +        else: +            if self.pyfile.name is not None and self.pyfile.name.endswith('.tmp') and data['filename']: +                self.pyfile.name = data['filename'] +                self.pyfile.size = parseFileSize(data['filesize']) +            self.link = data['downloadlink']          if self.getConfig("https"): -            new_url = new_url.replace("http://", "https://") +            self.link = self.link.replace("http://", "https://")          else: -            new_url = new_url.replace("https://", "http://") +            self.link = self.link.replace("https://", "http://") -        if new_url != pyfile.url: -            self.logDebug("New URL: %s" % new_url) +        if self.link != self.pyfile.url: +            self.logDebug("New URL: %s" % self.link) -        if pyfile.name.startswith("http") or pyfile.name.startswith("Unknown") or pyfile.name.endswith('..'): +        if self.pyfile.name.startswith("http") or self.pyfile.name.startswith("Unknown") or self.pyfile.name.endswith('..'):              # only use when name wasn't already set -            pyfile.name = self.getFilename(new_url) +            self.pyfile.name = self.getFilename(self.link) -        self.download(new_url, disposition=True) + +    def checkFile(self): +        super(OverLoadMe, self).checkFile()          check = self.checkDownload(              {"error": "<title>An error occured while processing your request</title>"})          if check == "error":              # usual this download can safely be retried -            self.retry(reason="An error occured while generating link.", wait_time=60) +            self.retry(wait_time=60, reason=_("An error occured while generating link.")) + + +getInfo = create_getInfo(OverLoadMe) diff --git a/module/plugins/hoster/PandaPlanet.py b/module/plugins/hoster/PandaPlanet.py deleted file mode 100644 index b6aa77b03..000000000 --- a/module/plugins/hoster/PandaPlanet.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- - -# Test links: -# test.bin - 214 B - http://pandapla.net/pew1cz3ot586 -# BigBuckBunny_320x180.mp4 - 61.7 Mb - http://pandapla.net/tz0rgjfyyoh7 - -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo - - -class PandaPlanet(XFileSharingPro): -    __name__ = "PandaPlanet" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?pandapla\.net/\w{12}' -    __version__ = "0.01" -    __description__ = """Pandapla.net hoster plugin""" -    __author_name__ = "t4skforce" -    __author_mail__ = "t4skforce1337[AT]gmail[DOT]com" - -    HOSTER_NAME = "pandapla.net" - -    FILE_SIZE_PATTERN = r'File Size:</b>\s*</td>\s*<td[^>]*>(?P<S>[^<]+)</td>\s*</tr>' -    FILE_NAME_PATTERN = r'File Name:</b>\s*</td>\s*<td[^>]*>(?P<N>[^<]+)</td>\s*</tr>' -    DIRECT_LINK_PATTERN = r'(http://([^/]*?%s|\d+\.\d+\.\d+\.\d+)(:\d+)?(/d/|(?:/files)?/\d+/\w+/)[^"\'<]+\/(?!video\.mp4)[^"\'<]+)' % HOSTER_NAME - - -getInfo = create_getInfo(PandaPlanet) diff --git a/module/plugins/hoster/PandaplaNet.py b/module/plugins/hoster/PandaplaNet.py new file mode 100644 index 000000000..78a1ed177 --- /dev/null +++ b/module/plugins/hoster/PandaplaNet.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class PandaplaNet(DeadHoster): +    __name__    = "PandaplaNet" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?pandapla\.net/\w{12}' + +    __description__ = """Pandapla.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")] + + +getInfo = create_getInfo(PandaplaNet) diff --git a/module/plugins/hoster/PornhostCom.py b/module/plugins/hoster/PornhostCom.py index 61b382c81..71342f3e0 100644 --- a/module/plugins/hoster/PornhostCom.py +++ b/module/plugins/hoster/PornhostCom.py @@ -1,17 +1,21 @@  # -*- coding: utf-8 -*-  import re +  from module.plugins.Hoster import Hoster  class PornhostCom(Hoster): -    __name__ = "PornhostCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?pornhost\.com/([0-9]+/[0-9]+\.html|[0-9]+)' -    __version__ = "0.2" +    __name__    = "PornhostCom" +    __type__    = "hoster" +    __version__ = "0.20" + +    __pattern__ = r'http://(?:www\.)?pornhost\.com/(\d+/\d+\.html|\d+)' +      __description__ = """Pornhost.com hoster plugin""" -    __author_name__ = "jeix" -    __author_mail__ = "jeix@hasnomail.de" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de")] +      def process(self, pyfile):          self.download_html() @@ -21,50 +25,52 @@ class PornhostCom(Hoster):          pyfile.name = self.get_file_name()          self.download(self.get_file_url()) -    ###   old interface + +    # 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 self.html is None: +        if not self.html:              self.download_html() -        file_url = re.search(r'download this file</label>.*?<a href="(.*?)"', self.html) -        if not file_url: -            file_url = re.search(r'"(http://dl[0-9]+\.pornhost\.com/files/.*?/.*?/.*?/.*?/.*?/.*?\..*?)"', self.html) -            if not file_url: -                file_url = re.search(r'width: 894px; height: 675px">.*?<img src="(.*?)"', self.html) -                if not file_url: -                    file_url = re.search(r'"http://file[0-9]+\.pornhost\.com/[0-9]+/.*?"', -                                         self.html)  # TODO: fix this one since it doesn't match +        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 -        file_url = file_url.group(1).strip() +        return url.group(1).strip() -        return file_url      def get_file_name(self): -        if self.html is None: +        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 not name: +        if name is None:              name = re.search(r'id="url" value="http://www\.pornhost\.com/(.*?)/"', self.html) -            if not name: +            if name is None:                  name = re.search(r'<title>pornhost\.com - free file hosting with a twist -(.*?)</title>', self.html) -                if not name: -                    name = re.search(r'"http://file[0-9]+\.pornhost\.com/.*?/(.*?)"', 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 self.html is None: +        if not self.html:              self.download_html()          if (re.search(r'gallery not found', self.html) is not None or diff --git a/module/plugins/hoster/PornhubCom.py b/module/plugins/hoster/PornhubCom.py index 29205b381..1bb787f09 100644 --- a/module/plugins/hoster/PornhubCom.py +++ b/module/plugins/hoster/PornhubCom.py @@ -1,17 +1,21 @@  # -*- coding: utf-8 -*-  import re +  from module.plugins.Hoster import Hoster  class PornhubCom(Hoster): -    __name__ = "PornhubCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?pornhub\.com/view_video\.php\?viewkey=[\w\d]+' -    __version__ = "0.5" +    __name__    = "PornhubCom" +    __type__    = "hoster" +    __version__ = "0.50" + +    __pattern__ = r'http://(?:www\.)?pornhub\.com/view_video\.php\?viewkey=\w+' +      __description__ = """Pornhub.com hoster plugin""" -    __author_name__ = "jeix" -    __author_mail__ = "jeix@hasnomail.de" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de")] +      def process(self, pyfile):          self.download_html() @@ -21,14 +25,16 @@ class PornhubCom(Hoster):          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 self.html is None: +        if not self.html:              self.download_html()          url = "http://www.pornhub.com//gateway.php" @@ -40,7 +46,7 @@ class PornhubCom(Hoster):          post_data += "\x02\x00\x02\x2d\x31\x02\x00\x20"          post_data += "add299463d4410c6d1b1c418868225f7" -        content = self.req.load(url, post=str(post_data)) +        content = self.load(url, post=str(post_data))          new_content = ""          for x in content: @@ -51,17 +57,16 @@ class PornhubCom(Hoster):          content = new_content -        file_url = re.search(r'flv_url.*(http.*?)##post_roll', content).group(1) +        return re.search(r'flv_url.*(http.*?)##post_roll', content).group(1) -        return file_url      def get_file_name(self): -        if self.html is None: +        if not self.html:              self.download_html() -        match = re.search(r'<title[^>]+>([^<]+) - ', self.html) -        if match: -            name = match.group(1) +        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: @@ -71,10 +76,11 @@ class PornhubCom(Hoster):          return name + '.flv' +      def file_exists(self):          """ returns True or False          """ -        if self.html is None: +        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) is not None: diff --git a/module/plugins/hoster/PotloadCom.py b/module/plugins/hoster/PotloadCom.py index ffcfad1a5..d6261af3a 100644 --- a/module/plugins/hoster/PotloadCom.py +++ b/module/plugins/hoster/PotloadCom.py @@ -1,18 +1,18 @@  # -*- coding: utf-8 -*- -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo -class PotloadCom(XFileSharingPro): -    __name__ = "PotloadCom" -    __type__ = "hoster" -    __pattern__ = r"http://(?:www\.)?potload\.com/\w{12}" -    __version__ = "0.01" -    __description__ = """billionuploads.com hoster plugin""" -    __author_name__ = ("stickell") -    __author_mail__ = ("l.stickell@yahoo.it") -    FILE_INFO_PATTERN = r'<h[1-6]>(?P<N>.+) \((?P<S>\d+) (?P<U>\w+)\)</h' -    HOSTER_NAME = "potload.com" +class PotloadCom(DeadHoster): +    __name__    = "PotloadCom" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?potload\.com/\w{12}' + +    __description__ = """Potload.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")]  getInfo = create_getInfo(PotloadCom) diff --git a/module/plugins/hoster/Premium4Me.py b/module/plugins/hoster/Premium4Me.py deleted file mode 100644 index e66e76ce1..000000000 --- a/module/plugins/hoster/Premium4Me.py +++ /dev/null @@ -1,70 +0,0 @@ -# -*- coding: utf-8 -*- - -from urllib import quote -from os.path import exists -from os import remove - -from module.plugins.Hoster import Hoster -from module.utils import fs_encode - - -class Premium4Me(Hoster): -    __name__ = "Premium4Me" -    __version__ = "0.08" -    __type__ = "hoster" - -    __pattern__ = r'http://(?:www\.)?premium.to/.*' -    __description__ = """Premium.to hoster plugin""" -    __author_name__ = ("RaNaN", "zoidberg", "stickell") -    __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz", "l.stickell@yahoo.it") - -    def setup(self): -        self.resumeDownload = True -        self.chunkLimit = 1 - -    def process(self, pyfile): -        if not self.account: -            self.logError(_("Please enter your %s account or deactivate this plugin") % "premium.to") -            self.fail("No premium.to account provided") - -        self.logDebug("premium.to: Old URL: %s" % pyfile.url) - -        tra = self.getTraffic() - -        #raise timeout to 2min -        self.req.setOption("timeout", 120) - -        self.download( -            "http://premium.to/api/getfile.php?authcode=%s&link=%s" % (self.account.authcode, quote(pyfile.url, "")), -            disposition=True) - -        check = self.checkDownload({"nopremium": "No premium account available"}) - -        if check == "nopremium": -            self.retry(60, 5 * 60, "No premium account available") - -        err = '' -        if self.req.http.code == '420': -            # Custom error code send - fail -            lastDownload = fs_encode(self.lastDownload) - -            if exists(lastDownload): -                f = open(lastDownload, "rb") -                err = f.read(256).strip() -                f.close() -                remove(lastDownload) -            else: -                err = 'File does not exist' - -        trb = self.getTraffic() -        self.logInfo("Filesize: %d, Traffic used %d, traffic left %d" % (pyfile.size, tra - trb, trb)) - -        if err: -            self.fail(err) - -    def getTraffic(self): -        try: -            traffic = int(self.load("http://premium.to/api/traffic.php?authcode=%s" % self.account.authcode)) -        except: -            traffic = 0 -        return traffic diff --git a/module/plugins/hoster/PremiumTo.py b/module/plugins/hoster/PremiumTo.py new file mode 100644 index 000000000..fa4caad5a --- /dev/null +++ b/module/plugins/hoster/PremiumTo.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +from os import remove +from os.path import exists +from urllib import quote + +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo +from module.utils import fs_encode + + +class PremiumTo(MultiHoster): +    __name__    = "PremiumTo" +    __type__    = "hoster" +    __version__ = "0.16" + +    __pattern__ = r'https?://(?:www\.)?premium\.to/.+' + +    __description__ = """Premium.to hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org"), +                       ("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    def setup(self): +        self.resumeDownload = True +        self.chunkLimit     = 1 + + +    def handlePremium(self): +        #raise timeout to 2min +        self.req.setOption("timeout", 120) + +        self.link = True +        self.download("http://premium.to/api/getfile.php", +                      get={'username': self.account.username, +                           'password': self.account.password, +                           'link'    : quote(self.pyfile.url, "")}, +                      disposition=True) + + +    def checkFile(self): +        super(PremiumTo, self).checkFile() + +        check = self.checkDownload({"nopremium": "No premium account available"}) + +        if check == "nopremium": +            self.retry(60, 5 * 60, "No premium account available") + +        err = '' +        if self.req.http.code == '420': +            # Custom error code send - fail +            lastDownload = fs_encode(self.lastDownload) +            with open(lastDownload, "rb") as f: +                err = f.read(256).strip() +            remove(lastDownload) + +        if err: +            self.fail(err) + + +getInfo = create_getInfo(PremiumTo) diff --git a/module/plugins/hoster/PremiumizeMe.py b/module/plugins/hoster/PremiumizeMe.py index 7e646fdf9..07536062f 100644 --- a/module/plugins/hoster/PremiumizeMe.py +++ b/module/plugins/hoster/PremiumizeMe.py @@ -1,57 +1,60 @@  # -*- coding: utf-8 -*- -from module.plugins.Hoster import Hoster -  from module.common.json_layer import json_loads +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo -class PremiumizeMe(Hoster): -    __name__ = "PremiumizeMe" -    __version__ = "0.12" -    __type__ = "hoster" -    __description__ = """Premiumize.me hoster plugin""" +class PremiumizeMe(MultiHoster): +    __name__    = "PremiumizeMe" +    __type__    = "hoster" +    __version__ = "0.15" -    # Since we want to allow the user to specify the list of hoster to use we let MultiHoster.coreReady -    # create the regex patterns for us using getHosters in our PremiumizeMe hook. -    __pattern__ = None +    __pattern__ = r'^unmatchable$'  #: Since we want to allow the user to specify the list of hoster to use we let MultiHoster.coreReady -    __author_name__ = "Florian Franzen" -    __author_mail__ = "FlorianFranzen@gmail.com" +    __description__ = """Premiumize.me hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Florian Franzen", "FlorianFranzen@gmail.com")] -    def process(self, pyfile): -        # Check account -        if not self.account or not self.account.canUse(): -            self.logError(_("Please enter your %s account or deactivate this plugin") % "premiumize.me") -            self.fail("No valid premiumize.me account provided") +    def handlePremium(self):          # 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 +        self.pyfile.name = self.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('.') +        temp = self.pyfile.name.split('.')          if temp.pop() in suffix_to_remove: -            pyfile.name = ".".join(temp) +            self.pyfile.name = ".".join(temp)          # Get account data -        (user, data) = self.account.selectAccount() +        user, data = self.account.selectAccount()          # Get rewritten link using the premiumize.me api v1 (see https://secure.premiumize.me/?show=api) -        answer = self.load( -            "https://api.premiumize.me/pm-api/v1.php?method=directdownloadlink¶ms[login]=%s¶ms[pass]=%s¶ms[link]=%s" % ( -            user, data['password'], pyfile.url)) -        data = json_loads(answer) +        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]' : self.pyfile.url}))          # Check status and decide what to do          status = data['status'] +          if status == 200: -            self.download(data['result']['location'], disposition=True) +            self.link = data['result']['location'] +            return +          elif status == 400: -            self.fail("Invalid link") +            self.fail(_("Invalid link")) +          elif status == 404:              self.offline() +          elif status >= 500:              self.tempOffline() +          else:              self.fail(data['statusmessage']) + + +getInfo = create_getInfo(PremiumizeMe) diff --git a/module/plugins/hoster/PromptfileCom.py b/module/plugins/hoster/PromptfileCom.py index 3580a9509..af38c4e15 100644 --- a/module/plugins/hoster/PromptfileCom.py +++ b/module/plugins/hoster/PromptfileCom.py @@ -1,56 +1,45 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ -  import re  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class PromptfileCom(SimpleHoster): -    __name__ = "PromptfileCom" -    __type__ = "hoster" -    __pattern__ = r"https?://(?:www\.)?promptfile\.com/" -    __version__ = "0.1" -    __description__ = """Promptfile.Com File Download Hoster""" -    __author_name__ = ("igel") +    __name__    = "PromptfileCom" +    __type__    = "hoster" +    __version__ = "0.12" + +    __pattern__ = r'https?://(?:www\.)?promptfile\.com/' + +    __description__ = """Promptfile.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("igel", "igelkun@myopera.com")] -    FILE_INFO_PATTERN = r'<span style="[^"]*" title="[^"]*">(?P<N>.*?) \((?P<S>[\d.]+) (?P<U>\w+)\)</span>' -    FILE_OFFLINE_PATTERN = r'<span style="[^"]*" title="File Not Found">File Not Found</span>' + +    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="([^"]*)" />' -    DIRECT_LINK_PATTERN = r"clip: {\s*url: '(https?://(?:www\.)promptfile[^']*)'," +    LINK_PATTERN = r'<a href=\"(.+)\" target=\"_blank\" class=\"view_dl_link\">Download File</a>' +      def handleFree(self):          # STAGE 1: get link to continue          m = re.search(self.CHASH_PATTERN, self.html) -        if not m: -            self.parseError("Unable to detect chash") +        if m is None: +            self.error(_("CHASH_PATTERN not found"))          chash = m.group(1) -        self.logDebug("read chash %s" % chash) +        self.logDebug("Read chash %s" % chash)          # continue to stage2          self.html = self.load(self.pyfile.url, decode=True, post={'chash': chash})          # STAGE 2: get the direct link -        m = re.search(self.DIRECT_LINK_PATTERN, self.html, re.MULTILINE | re.DOTALL) -        if not m: -            self.parseError("Unable to detect direct link") -        direct = m.group(1) -        self.logDebug('found direct link: ' + direct) -        self.download(direct, disposition=True) +        m = re.search(self.LINK_PATTERN, self.html) +        if m is None: +            self.error(_("LINK_PATTERN not found")) + +        self.download(m.group(1), disposition=True)  getInfo = create_getInfo(PromptfileCom) diff --git a/module/plugins/hoster/PrzeklejPl.py b/module/plugins/hoster/PrzeklejPl.py new file mode 100644 index 000000000..3a59a2c9e --- /dev/null +++ b/module/plugins/hoster/PrzeklejPl.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class PrzeklejPl(DeadHoster): +    __name__    = "PrzeklejPl" +    __type__    = "hoster" +    __version__ = "0.11" + +    __pattern__ = r'http://(?:www\.)?przeklej\.pl/plik/.+' + +    __description__ = """Przeklej.pl hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +getInfo = create_getInfo(PrzeklejPl) diff --git a/module/plugins/hoster/PutlockerCom.py b/module/plugins/hoster/PutlockerCom.py deleted file mode 100644 index a453eaf62..000000000 --- a/module/plugins/hoster/PutlockerCom.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################### -#  This program is free software: you can redistribute it and/or modify -#  it under the terms of the GNU Affero General Public License as -#  published by the Free Software Foundation, either version 3 of the -#  License, or (at your option) any later version. -# -#  This program is distributed in the hope that it will be useful, -#  but WITHOUT ANY WARRANTY; without even the implied warranty of -#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -#  GNU Affero General Public License for more details. -# -#  You should have received a copy of the GNU Affero General Public License -#  along with this program.  If not, see <http://www.gnu.org/licenses/>. -# -#  @author: Walter Purcaro -############################################################################### - -import re - -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo - - -class PutlockerCom(SimpleHoster): -    __name__ = "PutlockerCom" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?(firedrive|putlocker)\.com/(mobile/)?(file|embed)/(?P<ID>\w+)' -    __version__ = "0.33" -    __description__ = """Firedrive.com hoster plugin""" -    __author_name__ = "Walter Purcaro" -    __author_mail__ = "vuolter@gmail.com" - -    FILE_NAME_PATTERN = r'<b>Name:</b> (?P<N>.+) <br>' -    FILE_SIZE_PATTERN = r'<b>Size:</b> (?P<S>[\d.]+) (?P<U>[a-zA-Z]+) <br>' -    FILE_OFFLINE_PATTERN = r"<div class=\"sad_face_image\">" - -    FILE_URL_REPLACEMENTS = [(__pattern__, r'http://www.firedrive.com/file/\g<ID>')] - -    def setup(self): -        self.multiDL = self.resumeDownload = True -        self.chunkLimit = -1 - -    def handleFree(self): -        link = self._getLink() -        self.logDebug("Direct link: " + link) -        self.download(link, disposition=True) - -    def _getLink(self): -        self.html = self.load(self.pyfile.url, post={"confirm": re.search(r'name="confirm" value="(.*)"', self.html).group(1)}) -        return re.search(r'<a href="(https?://dl\.firedrive\.com/.*?)"', self.html).group(1) - - -getInfo = create_getInfo(PutlockerCom) diff --git a/module/plugins/hoster/QuickshareCz.py b/module/plugins/hoster/QuickshareCz.py index 46639444e..85c25f6f0 100644 --- a/module/plugins/hoster/QuickshareCz.py +++ b/module/plugins/hoster/QuickshareCz.py @@ -1,47 +1,35 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re +  from pycurl import FOLLOWLOCATION  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class QuickshareCz(SimpleHoster): -    __name__ = "QuickshareCz" -    __type__ = "hoster" -    __pattern__ = r'http://(?:[^/]*\.)?quickshare.cz/stahnout-soubor/.*' -    __version__ = "0.54" +    __name__    = "QuickshareCz" +    __type__    = "hoster" +    __version__ = "0.55" + +    __pattern__ = r'http://(?:[^/]*\.)?quickshare\.cz/stahnout-soubor/.+' +      __description__ = """Quickshare.cz hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __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>' -    FILE_NAME_PATTERN = r'<th width="145px">Název:</th>\s*<td style="word-wrap:break-word;">(?P<N>[^<]+)</td>' -    FILE_SIZE_PATTERN = r'<th>Velikost:</th>\s*<td>(?P<S>[0-9.]+) (?P<U>[kKMG])i?B</td>' -    FILE_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+) = ([0-9.]+|'[^']*')", self.html)) +        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'] @@ -49,11 +37,11 @@ class QuickshareCz(SimpleHoster):          if self.premium:              if 'UU_prihlasen' in self.jsvars:                  if self.jsvars['UU_prihlasen'] == '0': -                    self.logWarning('User not logged in') +                    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.logWarning(_("Not enough credit left"))                      self.premium = False          if self.premium: @@ -63,12 +51,13 @@ class QuickshareCz(SimpleHoster):          check = self.checkDownload({"err": re.compile(r"\AChyba!")}, max_size=100)          if check == "err": -            self.fail("File not found or plugin defect") +            self.fail(_("File not m or plugin defect")) +      def handleFree(self):          # 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')) +        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.req.http.c.setopt(FOLLOWLOCATION, 0) @@ -76,29 +65,29 @@ class QuickshareCz(SimpleHoster):          self.header = self.req.http.header          self.req.http.c.setopt(FOLLOWLOCATION, 1) -        found = re.search("Location\s*:\s*(.*)", self.header, re.I) -        if not found: -            self.fail('File not found') -        download_url = found.group(1) +        m = re.search(r'Location\s*:\s*(.+)', self.header, re.I) +        if m is None: +            self.fail(_("File not found")) +        download_url = m.group(1).rstrip()  #@TODO: Remove .rstrip() in 0.4.10          self.logDebug("FREE URL2:" + download_url)          # check errors -        found = re.search(r'/chyba/(\d+)', download_url) -        if found: -            if found.group(1) == '1': +        m = re.search(r'/chyba/(\d+)', download_url) +        if m: +            if m.group(1) == '1':                  self.retry(60, 2 * 60, "This IP is already downloading") -            elif found.group(1) == '2': +            elif m.group(1) == '2':                  self.retry(60, 60, "No free slots available")              else: -                self.fail('Error %d' % found.group(1)) +                self.fail(_("Error %d") % m.group(1))          # download file          self.download(download_url) +      def handlePremium(self):          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.logDebug("PREMIUM URL:" + download_url, data) +        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/module/plugins/hoster/RPNetBiz.py b/module/plugins/hoster/RPNetBiz.py index 57c22698d..bffa5ef85 100644 --- a/module/plugins/hoster/RPNetBiz.py +++ b/module/plugins/hoster/RPNetBiz.py @@ -2,77 +2,81 @@  import re -from module.plugins.Hoster import Hoster +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo  from module.common.json_layer import json_loads -class RPNetBiz(Hoster): -    __name__ = "RPNetBiz" -    __version__ = "0.1" -    __type__ = "hoster" +class RPNetBiz(MultiHoster): +    __name__    = "RPNetBiz" +    __type__    = "hoster" +    __version__ = "0.13" +      __description__ = """RPNet.biz hoster plugin""" -    __pattern__ = r'https?://.*rpnet\.biz' -    __author_name__ = "Dman" -    __author_mail__ = "dmanugm@gmail.com" +    __license__     = "GPLv3" + +    __pattern__ = r'https?://.+rpnet\.biz' +    __authors__     = [("Dman", "dmanugm@gmail.com")] +      def setup(self): -        self.chunkLimit = -1 +        self.chunkLimit     = -1          self.resumeDownload = True -    def process(self, pyfile): -        if re.match(self.__pattern__, pyfile.url): -            link_status = {'generated': pyfile.url} -        elif not self.account: -            # Check account -            self.logError(_("Please enter your %s account or deactivate this plugin") % "rpnet") -            self.fail("No rpnet account provided") -        else: -            (user, data) = self.account.selectAccount() +    def handlePremium(self): +        user, data = self.account.selectAccount() + +        self.logDebug("Original URL: %s" % self.pyfile.url) +        # Get the download link +        res = self.load("https://premium.rpnet.biz/client_api.php", +                        get={"username": user, +                             "password": data['password'], +                             "action": "generate", +                             "links": self.pyfile.url}) -            self.logDebug("Original URL: %s" % pyfile.url) -            # Get the download link  -            response = 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 -            self.logDebug("JSON data: %s" % response) -            link_status = json_loads(response)['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'] -            # 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 +                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() -                # 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)) -                    response = 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" % response) -                    download_status = json_loads(response)['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") +                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.download(link_status['generated'], disposition=True) +            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") +            self.fail(_("Something went wrong, not supposed to enter here")) + + +getInfo = create_getInfo(RPNetBiz) diff --git a/module/plugins/hoster/RapideoPl.py b/module/plugins/hoster/RapideoPl.py new file mode 100644 index 000000000..9fd20e69e --- /dev/null +++ b/module/plugins/hoster/RapideoPl.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- + +from module.common.json_layer import json_loads as loads +from module.plugins.internal.SimpleHoster import SimpleHoster + + +class RapideoPl(SimpleHoster): +    __name__ = "RapideoPl" +    __version__ = "0.01" +    __type__ = "hoster" +    __description__ = "Rapideo.pl 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" +    } + +    _usr = False +    _pwd = False + +    def setup(self): +        self.resumeDownload = True +        self.multiDL = True + +    def get_username_password(self): +        if not self.account: +            self.fail(_("Please enter your %s account or deactivate this plugin") % "Rapideo.pl") +        else: +            self._usr = self.account.getAccountData(self.user).get('usr') +            self._pwd = self.account.getAccountData(self.user).get('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 process(self, pyfile): +        self.get_username_password() +        try: +            data = self.runFileQuery(pyfile.url, 'fileinfo') +        except Exception: +            self.logDebug("RunFileQuery error") +            self.tempOffline() + +        try: +            parsed = 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 webiste \ +                    directly. Update this plugin." % parsed["hosting"]) + +        pyfile.name = parsed["filename"] +        pyfile.size = parsed["filesize"] + +        try: +            result_dl = self.runFileQuery(pyfile.url, 'filedownload') +        except Exception: +            self.logDebug("runFileQuery error #2") +            self.tempOffline() + +        self.download(result_dl, disposition=True) diff --git a/module/plugins/hoster/RapidfileshareNet.py b/module/plugins/hoster/RapidfileshareNet.py new file mode 100644 index 000000000..14d62ee74 --- /dev/null +++ b/module/plugins/hoster/RapidfileshareNet.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo + + +class RapidfileshareNet(XFSHoster): +    __name__    = "RapidfileshareNet" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?rapidfileshare\.net/\w{12}' + +    __description__ = """Rapidfileshare.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("guidobelix", "guidobelix@hotmail.it")] + + +    HOSTER_DOMAIN = "rapidfileshare.net" + +    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.<' + + +    def handlePremium(self): +        self.fail(_("Premium download not implemented")) + + +getInfo = create_getInfo(RapidfileshareNet) diff --git a/module/plugins/hoster/RapidgatorNet.py b/module/plugins/hoster/RapidgatorNet.py index b966fd1d6..ba6cfcd5c 100644 --- a/module/plugins/hoster/RapidgatorNet.py +++ b/module/plugins/hoster/RapidgatorNet.py @@ -1,93 +1,89 @@  # -*- coding: utf-8 -*- -############################################################################### -#  This program is free software: you can redistribute it and/or modify -#  it under the terms of the GNU Affero General Public License as -#  published by the Free Software Foundation, either version 3 of the -#  License, or (at your option) any later version. -# -#  This program is distributed in the hope that it will be useful, -#  but WITHOUT ANY WARRANTY; without even the implied warranty of -#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -#  GNU Affero General Public License for more details. -# -#  You should have received a copy of the GNU Affero General Public License -#  along with this program.  If not, see <http://www.gnu.org/licenses/>. -# -#  @author: zoidberg -###############################################################################  import re +  from pycurl import HTTPHEADER  from module.common.json_layer import json_loads  from module.network.HTTPRequest import BadHeader -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo -from module.plugins.internal.CaptchaService import ReCaptcha, SolveMedia, AdsCaptcha +from module.plugins.internal.CaptchaService import AdsCaptcha, ReCaptcha, SolveMedia +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, secondsToMidnight  class RapidgatorNet(SimpleHoster): -    __name__ = "RapidgatorNet" -    __type__ = "hoster" +    __name__    = "RapidgatorNet" +    __type__    = "hoster" +    __version__ = "0.27" +      __pattern__ = r'http://(?:www\.)?(rapidgator\.net|rg\.to)/file/\w+' -    __version__ = "0.21" +      __description__ = """Rapidgator.net hoster plugin""" -    __author_name__ = ("zoidberg", "chrox", "stickell", "Walter Purcaro") -    __author_mail__ = ("zoidberg@mujmail.cz", "", "l.stickell@yahoo.it", "vuolter@gmail.com") +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("chrox", None), +                       ("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] -    API_URL = 'http://rapidgator.net/api/file' -    FILE_NAME_PATTERN = r'<title>Download file (?P<N>.*)</title>' -    FILE_SIZE_PATTERN = r'File size:\s*<strong>(?P<S>[\d\.]+) (?P<U>\w+)</strong>' -    FILE_OFFLINE_PATTERN = r'>(File not found|Error 404)' +    API_URL = "http://rapidgator.net/api/file" -    JSVARS_PATTERN = r"\s+var\s*(startTimerUrl|getDownloadUrl|captchaUrl|fid|secs)\s*=\s*'?(.*?)'?;" +    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_ERROR_PATTERN = r'You can download files up to|This file can be downloaded by premium only<'      DOWNLOAD_LIMIT_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)\s*(\d+)\s*(hour|min)' -    DOWNLOAD_LINK_PATTERN = r"return '(http://\w+.rapidgator.net/.*)';" +    LINK_PATTERN = r'return \'(http://\w+.rapidgator.net/.*)\';' -    RECAPTCHA_KEY_PATTERN = r'"http://api\.recaptcha\.net/challenge\?k=(.*?)"' -    ADSCAPTCHA_SRC_PATTERN = r'(http://api\.adscaptcha\.com/Get\.aspx[^"\']*)' +    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): -        self.resumeDownload = self.multiDL = self.premium -        self.sid = None -        self.chunkLimit = 1 -        self.req.setOption("timeout", 120) -    def process(self, pyfile): +    def setup(self):          if self.account:              self.sid = self.account.getAccountData(self.user).get('SID', None) +        else: +            self.sid = None          if self.sid: -            self.handlePremium() -        else: -            self.handleFree() +            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) +            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) +            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):          #self.logDebug("ACCOUNT_DATA", self.account.getAccountData(self.user))          self.api_data = self.api_response('info') @@ -97,9 +93,8 @@ class RapidgatorNet(SimpleHoster):          url = self.api_response('download')['url']          self.download(url) -    def handleFree(self): -        self.html = self.load(self.pyfile.url, decode=True) +    def handleFree(self):          self.checkFree()          jsvars = dict(re.findall(self.JSVARS_PATTERN, self.html)) @@ -109,13 +104,13 @@ class RapidgatorNet(SimpleHoster):          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.get('startTimerUrl', '/download/AjaxStartTimer'), jsvars['fid'])          jsvars.update(self.getJsonResponse(url)) -        self.wait(int(jsvars.get('secs', 45)) + 1, False) +        self.wait(int(jsvars.get('secs', 45)), False)          url = "http://rapidgator.net%s?sid=%s" % ( -            jsvars.get('getDownloadUrl', '/download/AjaxGetDownload'), jsvars["sid"]) +            jsvars.get('getDownloadUrl', '/download/AjaxGetDownload'), jsvars['sid'])          jsvars.update(self.getJsonResponse(url))          self.req.http.lastURL = self.pyfile.url @@ -124,78 +119,80 @@ class RapidgatorNet(SimpleHoster):          url = "http://rapidgator.net%s" % jsvars.get('captchaUrl', '/download/captcha')          self.html = self.load(url) -        for _ in xrange(5): -            found = re.search(self.DOWNLOAD_LINK_PATTERN, self.html) -            if found: -                link = found.group(1) +        for _i in xrange(5): +            m = re.search(self.LINK_PATTERN, self.html) +            if m: +                link = m.group(1)                  self.logDebug(link)                  self.download(link, disposition=True)                  break              else:                  captcha, captcha_key = self.getCaptcha() -                captcha_challenge, captcha_response = captcha.challenge(captcha_key) +                challenge, response  = captcha.challenge(captcha_key) -                self.html = self.load(url, post={ -                    "DownloadCaptchaForm[captcha]": "", -                    "adcopy_challenge": captcha_challenge, -                    "adcopy_response": captcha_response -                }) +                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.parseError("Download link") +            self.error(_("Download link")) +      def getCaptcha(self): -        found = re.search(self.ADSCAPTCHA_SRC_PATTERN, self.html) -        if found: -            captcha_key = found.group(1) +        m = re.search(self.ADSCAPTCHA_PATTERN, self.html) +        if m: +            captcha_key = m.group(1)              captcha = AdsCaptcha(self)          else: -            found = re.search(self.RECAPTCHA_KEY_PATTERN, self.html) -            if found: -                captcha_key = found.group(1) +            m = re.search(self.RECAPTCHA_PATTERN, self.html) +            if m: +                captcha_key = m.group(1)                  captcha = ReCaptcha(self)              else: -                found = re.search(self.SOLVEMEDIA_PATTERN, self.html) -                if found: -                    captcha_key = found.group(1) +                m = re.search(self.SOLVEMEDIA_PATTERN, self.html) +                if m: +                    captcha_key = m.group(1)                      captcha = SolveMedia(self)                  else: -                    self.parseError("Captcha") +                    self.error(_("Captcha"))          return captcha, captcha_key +      def checkFree(self): -        found = re.search(self.PREMIUM_ONLY_ERROR_PATTERN, self.html) -        if found: -            self.fail("Premium account needed for download") +        m = re.search(self.PREMIUM_ONLY_ERROR_PATTERN, self.html) +        if m: +            self.fail(_("Premium account needed for download"))          else: -            found = re.search(self.WAIT_PATTERN, self.html) +            m = re.search(self.WAIT_PATTERN, self.html) -        if found: -            wait_time = int(found.group(1)) * {"hour": 60, "min": 1}[found.group(2)] +        if m: +            wait_time = int(m.group(1)) * {"hour": 60, "min": 1}[m.group(2)]          else: -            found = re.search(self.DOWNLOAD_LIMIT_ERROR_PATTERN, self.html) -            if not found: +            m = re.search(self.DOWNLOAD_LIMIT_ERROR_PATTERN, self.html) +            if m is None:                  return -            elif found.group(1) == "daily": -                wait_time = 60 +            elif m.group(1) == "daily": +                self.logWarning(_("You have reached your daily downloads limit for today")) +                wait_time = secondsToMidnight(gmt=2)              else: -                wait_time = 24 * 60 +                wait_time = 1 * 60 * 60 -        self.logDebug("Waiting %d minutes" % wait_time) -        self.wait(wait_time * 60, True) +        self.logDebug("Waiting %d minutes" % wait_time / 60) +        self.wait(wait_time, True)          self.retry() +      def getJsonResponse(self, url): -        response = self.load(url, decode=True) -        if not response.startswith('{'): +        res = self.load(url, decode=True) +        if not res.startswith('{'):              self.retry() -        self.logDebug(url, response) -        return json_loads(response) +        self.logDebug(url, res) +        return json_loads(res)  getInfo = create_getInfo(RapidgatorNet) diff --git a/module/plugins/hoster/RapidshareCom.py b/module/plugins/hoster/RapidshareCom.py deleted file mode 100644 index e5ab6b445..000000000 --- a/module/plugins/hoster/RapidshareCom.py +++ /dev/null @@ -1,228 +0,0 @@ -# -*- coding: utf-8 -*- - -# v1.36 -# * fixed call checkfiles subroutine -# v1.35 -# * fixed rs-urls in handleFree(..) and freeWait(..) -# * removed getInfo(..) function as it was not used anywhere (in this file) -# * removed some (old?) comment blocks - -import re - -from module.network.RequestFactory import getURL -from module.plugins.Hoster import Hoster - - -def getInfo(urls): -    ids = "" -    names = "" - -    p = re.compile(RapidshareCom.__pattern__) - -    for url in urls: -        r = p.search(url) -        if r.group("name"): -            ids += "," + r.group("id") -            names += "," + r.group("name") -        elif r.group("name_new"): -            ids += "," + r.group("id_new") -            names += "," + r.group("name_new") - -    url = "http://api.rapidshare.com/cgi-bin/rsapi.cgi?sub=checkfiles&files=%s&filenames=%s" % (ids[1:], names[1:]) - -    api = getURL(url) -    result = [] -    i = 0 -    for res in api.split(): -        tmp = res.split(",") -        if tmp[4] in ("0", "4", "5"): -            status = 1 -        elif tmp[4] == "1": -            status = 2 -        else: -            status = 3 - -        result.append((tmp[1], tmp[2], status, urls[i])) -        i += 1 - -    yield result - - -class RapidshareCom(Hoster): -    __name__ = "RapidshareCom" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?rapidshare.com/(?:files/(?P<id>\d*?)/(?P<name>[^?]+)|#!download\|(?:\w+)\|(?P<id_new>\d+)\|(?P<name_new>[^|]+))' -    __version__ = "1.39" -    __description__ = """Rapidshare.com hoster plugin""" -    __config__ = [("server", -                   "Cogent;Deutsche Telekom;Level(3);Level(3) #2;GlobalCrossing;Level(3) #3;Teleglobe;GlobalCrossing #2;TeliaSonera #2;Teleglobe #2;TeliaSonera #3;TeliaSonera", -                   "Preferred Server", "None")] -    __author_name__ = ("spoob", "RaNaN", "mkaay") -    __author_mail__ = ("spoob@pyload.org", "ranan@pyload.org", "mkaay@mkaay.de") - -    def setup(self): -        self.no_download = True -        self.api_data = None -        self.offset = 0 -        self.dl_dict = {} - -        self.id = None -        self.name = None - -        self.chunkLimit = -1 if self.premium else 1 -        self.multiDL = self.resumeDownload = self.premium - -    def process(self, pyfile): -        self.url = pyfile.url -        self.prepare() - -    def prepare(self): -        m = re.match(self.__pattern__, self.url) - -        if m.group("name"): -            self.id = m.group("id") -            self.name = m.group("name") -        else: -            self.id = m.group("id_new") -            self.name = m.group("name_new") - -        self.download_api_data() -        if self.api_data["status"] == "1": -            self.pyfile.name = self.get_file_name() - -            if self.premium: -                self.handlePremium() -            else: -                self.handleFree() - -        elif self.api_data["status"] == "2": -            self.logInfo(_("Rapidshare: Traffic Share (direct download)")) -            self.pyfile.name = self.get_file_name() - -            self.download(self.pyfile.url, get={"directstart": 1}) - -        elif self.api_data["status"] in ("0", "4", "5"): -            self.offline() -        elif self.api_data["status"] == "3": -            self.tempOffline() -        else: -            self.fail("Unknown response code.") - -    def handleFree(self): - -        while self.no_download: -            self.dl_dict = self.freeWait() - -        #tmp = "#!download|%(server)s|%(id)s|%(name)s|%(size)s" -        download = "http://%(host)s/cgi-bin/rsapi.cgi?sub=download&editparentlocation=0&bin=1&fileid=%(id)s&filename=%(name)s&dlauth=%(auth)s" % self.dl_dict - -        self.logDebug("RS API Request: %s" % download) -        self.download(download, ref=False) - -        check = self.checkDownload({"ip": "You need RapidPro to download more files from your IP address", -                                    "auth": "Download auth invalid"}) -        if check == "ip": -            self.setWait(60) -            self.logInfo(_("Already downloading from this ip address, waiting 60 seconds")) -            self.wait() -            self.handleFree() -        elif check == "auth": -            self.logInfo(_("Invalid Auth Code, download will be restarted")) -            self.offset += 5 -            self.handleFree() - -    def handlePremium(self): -        info = self.account.getAccountInfo(self.user, True) -        self.logDebug("%s: Use Premium Account" % self.__name__) -        url = self.api_data["mirror"] -        self.download(url, get={"directstart": 1}) - -    def download_api_data(self, force=False): -        """ -        http://images.rapidshare.com/apidoc.txt -        """ -        if self.api_data and not force: -            return -        api_url_base = "http://api.rapidshare.com/cgi-bin/rsapi.cgi" -        api_param_file = {"sub": "checkfiles", "incmd5": "1", "files": self.id, "filenames": self.name} -        src = self.load(api_url_base, cookies=False, get=api_param_file).strip() -        self.logDebug("RS INFO API: %s" % src) -        if src.startswith("ERROR"): -            return -        fields = src.split(",") - -        # status codes: -        #   0=File not found -        #   1=File OK (Anonymous downloading) -        #   3=Server down -        #   4=File marked as illegal -        #   5=Anonymous file locked, because it has more than 10 downloads already -        #   50+n=File OK (TrafficShare direct download type "n" without any logging.) -        #   100+n=File OK (TrafficShare direct download type "n" with logging. -        #                  Read our privacy policy to see what is logged.) - -        self.api_data = {"fileid": fields[0], "filename": fields[1], "size": int(fields[2]), "serverid": fields[3], -                         "status": fields[4], "shorthost": fields[5], "checksum": fields[6].strip().lower()} - -        if int(self.api_data["status"]) > 100: -            self.api_data["status"] = str(int(self.api_data["status"]) - 100) -        elif int(self.api_data["status"]) > 50: -            self.api_data["status"] = str(int(self.api_data["status"]) - 50) - -        self.api_data["mirror"] = "http://rs%(serverid)s%(shorthost)s.rapidshare.com/files/%(fileid)s/%(filename)s" % self.api_data - -    def freeWait(self): -        """downloads html with the important information -        """ -        self.no_download = True - -        id = self.id -        name = self.name - -        prepare = "https://api.rapidshare.com/cgi-bin/rsapi.cgi?sub=download&fileid=%(id)s&filename=%(name)s&try=1&cbf=RSAPIDispatcher&cbid=1" % { -            "name": name, "id": id} - -        self.logDebug("RS API Request: %s" % prepare) -        result = self.load(prepare, ref=False) -        self.logDebug("RS API Result: %s" % result) - -        between_wait = re.search("You need to wait (\d+) seconds", result) - -        if "You need RapidPro to download more files from your IP address" in result: -            self.setWait(60) -            self.logInfo(_("Already downloading from this ip address, waiting 60 seconds")) -            self.wait() -        elif ("Too many users downloading from this server right now" in result or -              "All free download slots are full" in result): -            self.setWait(120) -            self.logInfo(_("RapidShareCom: No free slots")) -            self.wait() -        elif "This file is too big to download it for free" in result: -            self.fail(_("You need a premium account for this file")) -        elif "Filename invalid." in result: -            self.fail(_("Filename reported invalid")) -        elif between_wait: -            self.setWait(int(between_wait.group(1))) -            self.wantReconnect = True -            self.wait() -        else: -            self.no_download = False - -            tmp, info = result.split(":") -            data = info.split(",") - -            dl_dict = {"id": id, -                       "name": name, -                       "host": data[0], -                       "auth": data[1], -                       "server": self.api_data["serverid"], -                       "size": self.api_data["size"]} -            self.setWait(int(data[2]) + 2 + self.offset) -            self.wait() - -            return dl_dict - -    def get_file_name(self): -        if self.api_data["filename"]: -            return self.api_data["filename"] -        return self.url.split("/")[-1] diff --git a/module/plugins/hoster/RapiduNet.py b/module/plugins/hoster/RapiduNet.py new file mode 100644 index 000000000..a3b2cffcd --- /dev/null +++ b/module/plugins/hoster/RapiduNet.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- + +import re + +from pycurl import HTTPHEADER +from time import time, altzone + +from module.common.json_layer import json_loads +from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo + + +class RapiduNet(SimpleHoster): +    __name__    = "RapiduNet" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'https?://(?:www\.)?rapidu\.net/(?P<ID>\d{10})' + +    __description__ = """Rapidu.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("prOq", None)] + + +    COOKIES = [("rapidu.net", "rapidu_lang", "en")] + +    FILE_INFO_PATTERN = r'<h1 title="(?P<N>.*)">.*</h1>\s*<small>(?P<S>\d+(\.\d+)?)\s(?P<U>\w+)</small>' +    OFFLINE_PATTERN   = r'404 - File not found' + +    ERROR_PATTERN = r'<div class="error">' + +    RECAPTCHA_KEY = r'6Ld12ewSAAAAAHoE6WVP_pSfCdJcBQScVweQh8Io' + + +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True +        self.limitDL        = 0 if self.premium else 2 + + +    def handleFree(self): +        self.req.http.lastURL = self.pyfile.url +        self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With: XMLHttpRequest"]) + +        jsvars = self.getJsonResponse("https://rapidu.net/ajax.php?a=getLoadTimeToDownload", {'_go': None}) + +        if str(jsvars['timeToDownload']) is "stop": +            t = (24 * 60 * 60) - (int(time()) % (24 *60 * 60)) + 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())) + +        recaptcha = ReCaptcha(self) + +        for _i in xrange(10): +            challenge, response = recaptcha.challenge(self.RECAPTCHA_KEY) + +            jsvars = self.getJsonResponse("https://rapidu.net/ajax.php?a=getCheckCaptcha", +                                          {'_go'     : None, +                                           'captcha1': challenge, +                                           'captcha2': response, +                                           'fileId'  : self.info['ID']}) +            if jsvars['message'] == 'success': +                self.download(jsvars['url']) +                break + + +    def getJsonResponse(self, url, post_data): +        res = self.load(url, post=post_data, decode=True) +        if not res.startswith('{'): +            self.retry() + +        self.logDebug(url, res) + +        return json_loads(res) + + +getInfo = create_getInfo(RapiduNet) diff --git a/module/plugins/hoster/RarefileNet.py b/module/plugins/hoster/RarefileNet.py index 8f2aacbcf..2be952efe 100644 --- a/module/plugins/hoster/RarefileNet.py +++ b/module/plugins/hoster/RarefileNet.py @@ -2,35 +2,24 @@  import re -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo -from module.utils import html_unescape +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo -class RarefileNet(XFileSharingPro): -    __name__ = "RarefileNet" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?rarefile.net/\w{12}' -    __version__ = "0.03" -    __description__ = """Rarefile.net hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +class RarefileNet(XFSHoster): +    __name__    = "RarefileNet" +    __type__    = "hoster" +    __version__ = "0.09" + +    __pattern__ = r'http://(?:www\.)?rarefile\.net/\w{12}' -    HOSTER_NAME = "rarefile.net" +    __description__ = """Rarefile.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] -    FILE_NAME_PATTERN = r'<td><font color="red">(?P<N>.*?)</font></td>' -    FILE_SIZE_PATTERN = r'<td>Size : (?P<S>.+?) ' -    DIRECT_LINK_PATTERN = r'<a href="(?P<link>[^"]+)">(?P=link)</a>' -    def setup(self): -        self.resumeDownload = self.multiDL = self.premium +    HOSTER_DOMAIN = "rarefile.net" -    def handleCaptcha(self, inputs): -        captcha_div = re.search(r'<b>Enter code.*?<div.*?>(.*?)</div>', self.html, re.S).group(1) -        self.logDebug(captcha_div) -        numerals = re.findall('<span.*?padding-left\s*:\s*(\d+).*?>(\d)</span>', html_unescape(captcha_div)) -        inputs['code'] = "".join([a[1] for a in sorted(numerals, key=lambda num: int(num[0]))]) -        self.logDebug("CAPTCHA", inputs['code'], numerals) -        return 3 +    LINK_PATTERN = r'<a href="(.+?)">\1</a>'  getInfo = create_getInfo(RarefileNet) diff --git a/module/plugins/hoster/RealdebridCom.py b/module/plugins/hoster/RealdebridCom.py index 36fcd194c..0332ae9da 100644 --- a/module/plugins/hoster/RealdebridCom.py +++ b/module/plugins/hoster/RealdebridCom.py @@ -1,88 +1,86 @@  # -*- coding: utf-8 -*-  import re -from time import time -from urllib import quote, unquote +  from random import randrange +from urllib import quote, unquote +from time import time -from module.utils import parseFileSize  from module.common.json_layer import json_loads -from module.plugins.Hoster import Hoster +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo +from module.utils import parseFileSize + +class RealdebridCom(MultiHoster): +    __name__    = "RealdebridCom" +    __type__    = "hoster" +    __version__ = "0.62" -class RealdebridCom(Hoster): -    __name__ = "RealdebridCom" -    __version__ = "0.53" -    __type__ = "hoster" +    __pattern__ = r'https?://((?:www\.|s\d+\.)?real-debrid\.com/dl/|[\w^_]\.rdb\.so/d/)[\w^_]+' -    __pattern__ = r'https?://(?:[^/]*\.)?real-debrid\..*'      __description__ = """Real-Debrid.com hoster plugin""" -    __author_name__ = "Devirex Hazzard" -    __author_mail__ = "naibaf_11@yahoo.de" +    __license__     = "GPLv3" +    __authors__     = [("Devirex Hazzard", "naibaf_11@yahoo.de")] +      def getFilename(self, url):          try:              name = unquote(url.rsplit("/", 1)[1])          except IndexError:              name = "Unknown_Filename..." -        if not name or name.endswith(".."):  # incomplete filename, append random stuff +        if not name or name.endswith(".."):  #: incomplete filename, append random stuff              name += "%s.tmp" % randrange(100, 999)          return name +      def setup(self): -        self.chunkLimit = 3 +        self.chunkLimit     = 3          self.resumeDownload = True -    def process(self, pyfile): -        if re.match(self.__pattern__, pyfile.url): -            new_url = pyfile.url -        elif not self.account: -            self.logError(_("Please enter your %s account or deactivate this plugin") % "Real-debrid") -            self.fail("No Real-debrid account provided") -        else: -            self.logDebug("Old URL: %s" % pyfile.url) -            password = self.getPassword().splitlines() -            if not password: -                password = "" -            else: -                password = password[0] -            url = "https://real-debrid.com/ajax/unrestrict.php?lang=en&link=%s&password=%s&time=%s" % ( -                quote(pyfile.url, ""), password, int(time() * 1000)) -            page = self.load(url) -            data = json_loads(page) +    def handlePremium(self): +        data = json_loads(self.load("https://real-debrid.com/ajax/unrestrict.php", +                                    get={'lang'    : "en", +                                         'link'    : self.pyfile.url, +                                         'password': self.getPassword(), +                                         'time'    : int(time() * 1000)})) -            self.logDebug("Returned Data: %s" % data) +        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() +        if data['error'] != 0: +            if data['message'] == "Your file is unavailable on the hoster.": +                self.offline()              else: -                if pyfile.name is not None and pyfile.name.endswith('.tmp') and data["file_name"]: -                    pyfile.name = data["file_name"] -                pyfile.size = parseFileSize(data["file_size"]) -                new_url = data['generated_links'][0][-1] +                self.logWarning(data['message']) +                self.tempOffline() +        else: +            if self.pyfile.name is not None and self.pyfile.name.endswith('.tmp') and data['file_name']: +                self.pyfile.name = data['file_name'] +            self.pyfile.size = parseFileSize(data['file_size']) +            self.link = data['generated_links'][0][-1]          if self.getConfig("https"): -            new_url = new_url.replace("http://", "https://") +            self.link = self.link.replace("http://", "https://")          else: -            new_url = new_url.replace("https://", "http://") +            self.link = self.link.replace("https://", "http://") -        if new_url != pyfile.url: -            self.logDebug("New URL: %s" % new_url) +        if self.link != self.pyfile.url: +            self.logDebug("New URL: %s" % self.link) -        if pyfile.name.startswith("http") or pyfile.name.startswith("Unknown") or pyfile.name.endswith('..'): +        if self.pyfile.name.startswith("http") or self.pyfile.name.startswith("Unknown") or self.pyfile.name.endswith('..'):              #only use when name wasnt already set -            pyfile.name = self.getFilename(new_url) +            self.pyfile.name = self.getFilename(self.link) + -        self.download(new_url, disposition=True) +    def checkFile(self): +        super(RealdebridCom, self).checkFile()          check = self.checkDownload(              {"error": "<title>An error occured while processing your request</title>"})          if check == "error":              #usual this download can safely be retried -            self.retry(wait_time=60, reason="An error occured while generating link.") +            self.retry(wait_time=60, reason=_("An error occured while generating link")) + + +getInfo = create_getInfo(RealdebridCom) diff --git a/module/plugins/hoster/RedtubeCom.py b/module/plugins/hoster/RedtubeCom.py index a2bbf3883..d68fbe262 100644 --- a/module/plugins/hoster/RedtubeCom.py +++ b/module/plugins/hoster/RedtubeCom.py @@ -1,18 +1,22 @@  # -*- coding: utf-8 -*-  import re +  from module.plugins.Hoster import Hoster  from module.unescape import unescape  class RedtubeCom(Hoster): -    __name__ = "RedtubeCom" -    __type__ = "hoster" +    __name__    = "RedtubeCom" +    __type__    = "hoster" +    __version__ = "0.20" +      __pattern__ = r'http://(?:www\.)?redtube\.com/\d+' -    __version__ = "0.2" +      __description__ = """Redtube.com hoster plugin""" -    __author_name__ = "jeix" -    __author_mail__ = "jeix@hasnomail.de" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de")] +      def process(self, pyfile):          self.download_html() @@ -22,31 +26,34 @@ class RedtubeCom(Hoster):          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 self.html is None: +        if not self.html:              self.download_html()          file_url = unescape(re.search(r'hashlink=(http.*?)"', self.html).group(1))          return file_url +      def get_file_name(self): -        if self.html is None: +        if not self.html:              self.download_html() -        name = re.search('<title>(.*?)- RedTube - Free Porn Videos</title>', self.html).group(1).strip() + ".flv" -        return name +        return re.search('<title>(.*?)- RedTube - Free Porn Videos</title>', self.html).group(1).strip() + ".flv" +      def file_exists(self):          """ returns True or False          """ -        if self.html is None: +        if not self.html:              self.download_html()          if re.search(r'This video has been removed.', self.html) is not None: diff --git a/module/plugins/hoster/RehostTo.py b/module/plugins/hoster/RehostTo.py index 94e53520a..2971a6781 100644 --- a/module/plugins/hoster/RehostTo.py +++ b/module/plugins/hoster/RehostTo.py @@ -1,37 +1,42 @@  # -*- coding: utf-8 -*-  from urllib import quote, unquote -from module.plugins.Hoster import Hoster +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo + + +class RehostTo(MultiHoster): +    __name__    = "RehostTo" +    __type__    = "hoster" +    __version__ = "0.16" + +    __pattern__ = r'https?://.*rehost\.to\..+' -class RehostTo(Hoster): -    __name__ = "RehostTo" -    __version__ = "0.13" -    __type__ = "hoster" -    __pattern__ = r'https?://.*rehost.to\..*'      __description__ = """Rehost.com hoster plugin""" -    __author_name__ = "RaNaN" -    __author_mail__ = "RaNaN@pyload.org" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org")] +      def getFilename(self, url):          return unquote(url.rsplit("/", 1)[1]) +      def setup(self): -        self.chunkLimit = 1 +        self.chunkLimit     = 1          self.resumeDownload = True -    def process(self, pyfile): -        if not self.account: -            self.logError(_("Please enter your %s account or deactivate this plugin") % "rehost.to") -            self.fail("No rehost.to account provided") +    def handlePremium(self):          data = self.account.getAccountInfo(self.user) -        long_ses = data["long_ses"] - -        self.logDebug("Rehost.to: Old URL: %s" % pyfile.url) -        new_url = "http://rehost.to/process_download.php?user=cookie&pass=%s&dl=%s" % (long_ses, quote(pyfile.url, "")) +        long_ses = data['long_ses']          #raise timeout to 2min          self.req.setOption("timeout", 120) -        self.download(new_url, disposition=True) +        self.link = True +        self.download("http://rehost.to/process_download.php", +                      get={'user': "cookie", 'pass': long_ses, 'dl': quote(self.pyfile.url, "")}, +                      disposition=True) + + +getInfo = create_getInfo(RehostTo) diff --git a/module/plugins/hoster/ReloadCc.py b/module/plugins/hoster/ReloadCc.py deleted file mode 100644 index ed1b21aa3..000000000 --- a/module/plugins/hoster/ReloadCc.py +++ /dev/null @@ -1,115 +0,0 @@ -# -*- coding: utf-8 -*- - -from module.plugins.Hoster import Hoster - -from module.common.json_layer import json_loads - -from module.network.HTTPRequest import BadHeader - - -class ReloadCc(Hoster): -    __name__ = "ReloadCc" -    __version__ = "0.5" -    __type__ = "hoster" -    __description__ = """Reload.cc hoster plugin""" - -    # Since we want to allow the user to specify the list of hoster to use we let MultiHoster.coreReady -    # create the regex patterns for us using getHosters in our ReloadCc hook. -    __pattern__ = None - -    __author_name__ = "Reload Team" -    __author_mail__ = "hello@reload.cc" - -    def process(self, pyfile): -        # Check account -        if not self.account or not self.account.canUse(): -            self.logError(_("Please enter your %s account or deactivate this plugin") % "reload.cc") -            self.fail("No valid reload.cc account provided") - -        # 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() - -        query_params = dict( -            via='pyload', -            v=1, -            user=user, -            uri=pyfile.url -        ) - -        try: -            query_params.update(dict(hash=self.account.infos[user]['pwdhash'])) -        except Exception: -            query_params.update(dict(pwd=data['password'])) - -        try: -            answer = self.load("http://api.reload.cc/dl", get=query_params) -        except BadHeader, e: -            if e.code == 400: -                self.fail("The URI is not supported by Reload.cc.") -            elif e.code == 401: -                self.fail("Wrong username or password") -            elif e.code == 402: -                self.fail("Your account is inactive. A payment is required for downloading!") -            elif e.code == 403: -                self.fail("Your account is disabled. Please contact the Reload.cc support!") -            elif e.code == 409: -                self.logWarning("The hoster seems to be a limited hoster and you've used your daily traffic for this hoster: %s" % pyfile.url) -                # Wait for 6 hours and retry up to 4 times => one day -                self.retry(4, 6 * 60 * 60, "Limited hoster traffic limit exceeded") -            elif e.code == 429: -                # Too many connections, wait 2 minutes and try again -                self.retry(5, 2 * 60, "Too many concurrent connections") -            elif e.code == 503: -                # Retry in 10 minutes -                self.retry(wait_time=10 * 60, -                           reason="Reload.cc is currently in maintenance mode! Please check again later.") -            else: -                self.fail( -                    "Internal error within Reload.cc. Please contact the Reload.cc support for further information.") -            return - -        data = json_loads(answer) - -        # Check status and decide what to do -        status = data.get('status', None) -        if status == "ok": -            conn_limit = data.get('msg', 0) -            # API says these connections are limited -            # Make sure this limit is used - the download will fail if not -            if conn_limit > 0: -                try: -                    self.limitDL = int(conn_limit) -                except ValueError: -                    self.limitDL = 1 -            else: -                self.limitDL = 0 - -            try: -                self.download(data['link'], disposition=True) -            except BadHeader, e: -                if e.code == 404: -                    self.fail("File Not Found") -                elif e.code == 412: -                    self.fail("File access password is wrong") -                elif e.code == 417: -                    self.fail("Password required for file access") -                elif e.code == 429: -                    # Too many connections, wait 2 minutes and try again -                    self.retry(5, 2 * 60, "Too many concurrent connections") -                else: -                    self.fail( -                        "Internal error within Reload.cc. Please contact the Reload.cc support for further information." -                    ) -                return -        else: -            self.fail("Internal error within Reload.cc. Please contact the Reload.cc support for further information.") diff --git a/module/plugins/hoster/RemixshareCom.py b/module/plugins/hoster/RemixshareCom.py new file mode 100644 index 000000000..ed171895e --- /dev/null +++ b/module/plugins/hoster/RemixshareCom.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://remixshare.com/download/p946u +# +# 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/module/network/HTTPRequest.py + +import re + +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo + + +class RemixshareCom(SimpleHoster): +    __name__    = "RemixshareCom" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'https?://remixshare\.com/(download|dl)/\w+' + +    __description__ = """Remixshare.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    INFO_PATTERN = r'title=\'.+?\'>(?P<N>.+?)</span><span class=\'light2\'> \((?P<S>\d+) (?P<U>[\w^_]+)\)<' +    OFFLINE_PATTERN = r'<h1>Ooops!<' + +    LINK_PATTERN = r'(http://remixshare\.com/downloadfinal/.+?)"' +    TOKEN_PATTERN = r'var acc = (\d+)' +    WAIT_PATTERN = r'var XYZ = r"(\d+)"' + + +    def setup(self): +        self.multiDL = True +        self.chunkLimit = 1 + + +    def handleFree(self): +        b = re.search(self.LINK_PATTERN, self.html) +        if not b: +            self.error(_("Cannot parse download url")) +        c = re.search(self.TOKEN_PATTERN, self.html) +        if not c: +            self.error(_("Cannot parse file token")) +        dl_url = b.group(1) + c.group(1) + +        #Check if we have to wait +        seconds = re.search(self.WAIT_PATTERN, self.html) +        if seconds: +            self.logDebug("Wait " + seconds.group(1)) +            self.wait(int(seconds.group(1))) + +        # Finally start downloading... +        self.download(dl_url, disposition=True) + + +getInfo = create_getInfo(RemixshareCom) diff --git a/module/plugins/hoster/RgHostNet.py b/module/plugins/hoster/RgHostNet.py index 9e37ed87b..aa4830563 100644 --- a/module/plugins/hoster/RgHostNet.py +++ b/module/plugins/hoster/RgHostNet.py @@ -1,27 +1,26 @@  # -*- coding: utf-8 -*-  import re +  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class RgHostNet(SimpleHoster): -    __name__ = "RgHostNet" -    __type__ = "hoster" +    __name__    = "RgHostNet" +    __type__    = "hoster" +    __version__ = "0.03" +      __pattern__ = r'http://(?:www\.)?rghost\.net/\d+(?:r=\d+)?' -    __version__ = "0.01" +      __description__ = """RgHost.net hoster plugin""" -    __author_name__ = "z00nx" -    __author_mail__ = "z00nx0@gmail.com" - -    FILE_INFO_PATTERN = r'<h1>\s+(<a[^>]+>)?(?P<N>[^<]+)(</a>)?\s+<small[^>]+>\s+\((?P<S>[^)]+)\)\s+</small>\s+</h1>' -    FILE_OFFLINE_PATTERN = r'File is deleted|this page is not found' -    DOWNLOAD_LINK_PATTERN = '''<a\s+href="([^"]+)"\s+class="btn\s+large\s+download"[^>]+>Download</a>''' - -    def handleFree(self): -        found = re.search(self.DOWNLOAD_LINK_PATTERN, self.html) -        if not found: -            self.parseError("Unable to detect the direct link") -        download_link = found.group(1) -        self.download(download_link, disposition=True) +    __license__     = "GPLv3" +    __authors__     = [("z00nx", "z00nx0@gmail.com")] + + +    INFO_PATTERN    = r'<h1>\s+(<a[^>]+>)?(?P<N>[^<]+)(</a>)?\s+<small[^>]+>\s+\((?P<S>[^)]+)\)\s+</small>\s+</h1>' +    OFFLINE_PATTERN = r'File is deleted|this page is not found' + +    LINK_FREE_PATTERN = r'<a\s+href="([^"]+)"\s+class="btn\s+large\s+download"[^>]+>Download</a>' +  getInfo = create_getInfo(RgHostNet) diff --git a/module/plugins/hoster/RyushareCom.py b/module/plugins/hoster/RyushareCom.py index 4d3e9b7f3..0964c51fc 100644 --- a/module/plugins/hoster/RyushareCom.py +++ b/module/plugins/hoster/RyushareCom.py @@ -1,37 +1,41 @@  # -*- coding: utf-8 -*- - -# Test links (random.bin): +# +# Test links:  # http://ryushare.com/cl0jy8ric2js/random.bin  import re -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo  from module.plugins.internal.CaptchaService import SolveMedia -class RyushareCom(XFileSharingPro): -    __name__ = "RyushareCom" -    __type__ = "hoster" +class RyushareCom(XFSHoster): +    __name__    = "RyushareCom" +    __type__    = "hoster" +    __version__ = "0.20" +      __pattern__ = r'http://(?:www\.)?ryushare\.com/\w+' -    __version__ = "0.15" +      __description__ = """Ryushare.com hoster plugin""" -    __author_name__ = ("zoidberg", "stickell", "quareevo") -    __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it", "quareevo@arcor.de") +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it"), +                       ("quareevo", "quareevo@arcor.de")] -    HOSTER_NAME = "ryushare.com" -    FILE_SIZE_PATTERN = r'You have requested <font color="red">[^<]+</font> \((?P<S>[\d\.]+) (?P<U>\w+)' +    HOSTER_DOMAIN = "ryushare.com" + +    WAIT_PATTERN = r'You have to wait ((?P<H>\d+) hour[s]?, )?((?P<M>\d+) minute[s], )?(?P<S>\d+) second[s]' + +    LINK_PATTERN = r'<a href="([^"]+)">Click here to download<' -    WAIT_PATTERN = r'You have to wait ((?P<hour>\d+) hour[s]?, )?((?P<min>\d+) minute[s], )?(?P<sec>\d+) second[s]' -    DIRECT_LINK_PATTERN = r'(http://([^/]*?ryushare.com|\d+\.\d+\.\d+\.\d+)(:\d+/d/|/files/\w+/\w+/)[^"\'<]+)' -    SOLVEMEDIA_PATTERN = r'http:\/\/api\.solvemedia\.com\/papi\/challenge\.script\?k=(.*?)"'      def getDownloadLink(self):          retry = False          self.html = self.load(self.pyfile.url)          action, inputs = self.parseHtmlForm(input_names={"op": re.compile("^download")})          if "method_premium" in inputs: -            del inputs["method_premium"] +            del inputs['method_premium']          self.html = self.load(self.pyfile.url, post=inputs)          action, inputs = self.parseHtmlForm('F1') @@ -42,10 +46,10 @@ class RyushareCom(XFileSharingPro):              self.setWait(1 * 60 * 60, True)              retry = True -        match = re.search(self.WAIT_PATTERN, self.html) -        if match: -            m = match.groupdict(0) -            waittime = int(m["hour"]) * 60 * 60 + int(m["min"]) * 60 + int(m["sec"]) +        m = re.search(self.WAIT_PATTERN, self.html) +        if m: +            wait = m.groupdict(0) +            waittime = int(wait['H']) * 60 * 60 + int(wait['M']) * 60 + int(wait['S'])              self.setWait(waittime, True)              retry = True @@ -53,31 +57,24 @@ class RyushareCom(XFileSharingPro):          if retry:              self.retry() -        for _ in xrange(5): -            m = re.search(self.SOLVEMEDIA_PATTERN, self.html) -            if not m: -                self.parseError("Error parsing captcha") - -            captchaKey = m.group(1) -            captcha = SolveMedia(self) -            challenge, response = captcha.challenge(captchaKey) +        for _i in xrange(5): +            solvemedia = SolveMedia(self) +            challenge, response = solvemedia.challenge() -            inputs["adcopy_challenge"] = challenge -            inputs["adcopy_response"] = response +            inputs['adcopy_challenge'] = challenge +            inputs['adcopy_response'] = response              self.html = self.load(self.pyfile.url, post=inputs)              if "WRONG CAPTCHA" in self.html:                  self.invalidCaptcha() -                self.logInfo("Invalid Captcha")              else:                  self.correctCaptcha()                  break          else: -            self.fail("You have entered 5 invalid captcha codes") +            self.fail(_("You have entered 5 invalid captcha codes"))          if "Click here to download" in self.html: -            m = re.search(r'<a href="([^"]+)">Click here to download</a>', self.html) -            return m.group(1) +            return re.search(r'<a href="([^"]+)">Click here to download</a>', self.html).group(1)  getInfo = create_getInfo(RyushareCom) diff --git a/module/plugins/hoster/SafesharingEu.py b/module/plugins/hoster/SafesharingEu.py new file mode 100644 index 000000000..bb6e0f646 --- /dev/null +++ b/module/plugins/hoster/SafesharingEu.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo + + +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")] + + +    HOSTER_DOMAIN = "safesharing.eu" + +    ERROR_PATTERN = r'(?:<div class="alert alert-danger">)(.+?)(?:</div>)' + + +getInfo = create_getInfo(SafesharingEu) diff --git a/module/plugins/hoster/SecureUploadEu.py b/module/plugins/hoster/SecureUploadEu.py index 1b11d691d..64e6456a9 100644 --- a/module/plugins/hoster/SecureUploadEu.py +++ b/module/plugins/hoster/SecureUploadEu.py @@ -1,21 +1,23 @@  # -*- coding: utf-8 -*- -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo -class SecureUploadEu(XFileSharingPro): -    __name__ = "SecureUploadEu" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?secureupload\.eu/(\w){12}(/\w+)' -    __version__ = "0.01" +class SecureUploadEu(XFSHoster): +    __name__    = "SecureUploadEu" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'https?://(?:www\.)?secureupload\.eu/\w{12}' +      __description__ = """SecureUpload.eu hoster plugin""" -    __author_name__ = "z00nx" -    __author_mail__ = "z00nx0@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("z00nx", "z00nx0@gmail.com")] + -    HOSTER_NAME = "secureupload.eu" +    HOSTER_DOMAIN = "secureupload.eu" -    FILE_INFO_PATTERN = '<h3>Downloading (?P<N>[^<]+) \((?P<S>[^<]+)\)</h3>' -    FILE_OFFLINE_PATTERN = 'The file was removed|File Not Found' +    INFO_PATTERN = r'<h3>Downloading (?P<N>[^<]+) \((?P<S>[^<]+)\)</h3>'  getInfo = create_getInfo(SecureUploadEu) diff --git a/module/plugins/hoster/SendmywayCom.py b/module/plugins/hoster/SendmywayCom.py index 6de87e2b3..637098b88 100644 --- a/module/plugins/hoster/SendmywayCom.py +++ b/module/plugins/hoster/SendmywayCom.py @@ -1,21 +1,21 @@  # -*- coding: utf-8 -*- -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo -class SendmywayCom(XFileSharingPro): -    __name__ = "SendmywayCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?sendmyway.com/\w{12}' -    __version__ = "0.01" -    __description__ = """SendMyWay hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +class SendmywayCom(XFSHoster): +    __name__    = "SendmywayCom" +    __type__    = "hoster" +    __version__ = "0.04" -    HOSTER_NAME = "sendmyway.com" +    __pattern__ = r'http://(?:www\.)?sendmyway\.com/\w{12}' -    FILE_NAME_PATTERN = r'<p class="file-name" ><.*?>\s*(?P<N>.+)' -    FILE_SIZE_PATTERN = r'<small>\((?P<S>\d+) bytes\)</small>' +    __description__ = """SendMyWay.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +    HOSTER_DOMAIN = "sendmyway.com"  getInfo = create_getInfo(SendmywayCom) diff --git a/module/plugins/hoster/SendspaceCom.py b/module/plugins/hoster/SendspaceCom.py index d6eafac0c..12f966e31 100644 --- a/module/plugins/hoster/SendspaceCom.py +++ b/module/plugins/hoster/SendspaceCom.py @@ -1,60 +1,50 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re +  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class SendspaceCom(SimpleHoster): -    __name__ = "SendspaceCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?sendspace.com/file/.*' -    __version__ = "0.13" +    __name__    = "SendspaceCom" +    __type__    = "hoster" +    __version__ = "0.16" + +    __pattern__ = r'https?://(?:www\.)?sendspace\.com/file/\w+' +      __description__ = """Sendspace.com hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __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_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>' -    DOWNLOAD_URL_PATTERN = r'<a id="download_button" href="([^"]+)"' -    FILE_NAME_PATTERN = r'<h2 class="bgray">\s*<(?:b|strong)>(?P<N>[^<]+)</' -    FILE_SIZE_PATTERN = r'<div class="file_description reverse margin_center">\s*<b>File Size:</b>\s*(?P<S>[0-9.]+)(?P<U>[kKMG])i?B\s*</div>' -    FILE_OFFLINE_PATTERN = r'<div class="msg error" style="cursor: default">Sorry, the file you requested is not available.</div>' -    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):          params = {} -        for _ in xrange(3): -            found = re.search(self.DOWNLOAD_URL_PATTERN, self.html) -            if found: +        for _i in xrange(3): +            m = re.search(self.LINK_PATTERN, self.html) +            if m:                  if 'captcha_hash' in params:                      self.correctCaptcha() -                download_url = found.group(1) +                download_url = m.group(1)                  break -            found = re.search(self.CAPTCHA_PATTERN, self.html) -            if found: +            m = re.search(self.CAPTCHA_PATTERN, self.html) +            if m:                  if 'captcha_hash' in params:                      self.invalidCaptcha() -                captcha_url1 = "http://www.sendspace.com/" + found.group(1) -                found = re.search(self.USER_CAPTCHA_PATTERN, self.html) -                captcha_url2 = "http://www.sendspace.com/" + found.group(1) -                params = {'captcha_hash': found.group(2), +                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: @@ -63,10 +53,9 @@ class SendspaceCom(SimpleHoster):              self.logDebug(params)              self.html = self.load(self.pyfile.url, post=params)          else: -            self.fail("Download link not found") +            self.fail(_("Download link not found")) -        self.logDebug("Download URL: %s" % download_url)          self.download(download_url) -create_getInfo(SendspaceCom) +getInfo = create_getInfo(SendspaceCom) diff --git a/module/plugins/hoster/Share4webCom.py b/module/plugins/hoster/Share4webCom.py index e25216cb8..4634917ad 100644 --- a/module/plugins/hoster/Share4webCom.py +++ b/module/plugins/hoster/Share4webCom.py @@ -5,15 +5,18 @@ from module.plugins.internal.SimpleHoster import create_getInfo  class Share4webCom(UnibytesCom): -    __name__ = "Share4webCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?share4web\.com/get/\w+' -    __version__ = "0.1" +    __name__    = "Share4webCom" +    __type__    = "hoster" +    __version__ = "0.11" + +    __pattern__ = r'https?://(?:www\.)?share4web\.com/get/\w+' +      __description__ = """Share4web.com hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    DOMAIN = 'http://www.share4web.com' +    HOSTER_DOMAIN = "share4web.com"  getInfo = create_getInfo(UnibytesCom) diff --git a/module/plugins/hoster/Share76Com.py b/module/plugins/hoster/Share76Com.py index 558ec2451..1ac8a64e7 100644 --- a/module/plugins/hoster/Share76Com.py +++ b/module/plugins/hoster/Share76Com.py @@ -4,13 +4,15 @@ from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class Share76Com(DeadHoster): -    __name__ = "Share76Com" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?share76.com/\w{12}' +    __name__    = "Share76Com" +    __type__    = "hoster"      __version__ = "0.04" + +    __pattern__ = r'http://(?:www\.)?share76\.com/\w{12}' +      __description__ = """Share76.com hoster plugin""" -    __author_name__ = "me" -    __author_mail__ = "" +    __license__     = "GPLv3" +    __authors__     = []  getInfo = create_getInfo(Share76Com) diff --git a/module/plugins/hoster/ShareFilesCo.py b/module/plugins/hoster/ShareFilesCo.py index 35f21916c..4996014d8 100644 --- a/module/plugins/hoster/ShareFilesCo.py +++ b/module/plugins/hoster/ShareFilesCo.py @@ -4,13 +4,15 @@ from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class ShareFilesCo(DeadHoster): -    __name__ = "ShareFilesCo" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?sharefiles\.co/\w{12}' +    __name__    = "ShareFilesCo" +    __type__    = "hoster"      __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?sharefiles\.co/\w{12}' +      __description__ = """Sharefiles.co hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")]  getInfo = create_getInfo(ShareFilesCo) diff --git a/module/plugins/hoster/ShareRapidCom.py b/module/plugins/hoster/ShareRapidCom.py deleted file mode 100644 index b82a142ea..000000000 --- a/module/plugins/hoster/ShareRapidCom.py +++ /dev/null @@ -1,70 +0,0 @@ -# -*- coding: utf-8 -*- - -import re - -from pycurl import HTTPHEADER -from module.network.HTTPRequest import BadHeader -from module.network.RequestFactory import getRequest -from module.plugins.internal.SimpleHoster import SimpleHoster, parseFileInfo, replace_patterns - - -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) -        file_info = parseFileInfo(ShareRapidCom, replace_patterns(url, ShareRapidCom.FILE_URL_REPLACEMENTS), html) -        yield file_info - - -class ShareRapidCom(SimpleHoster): -    __name__ = "ShareRapidCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?((share(-?rapid\.(biz|com|cz|info|eu|net|org|pl|sk)|-(central|credit|free|net)\.cz|-ms\.net)|(s-?rapid|rapids)\.(cz|sk))|(e-stahuj|mediatack|premium-rapidshare|rapidshare-premium|qiuck)\.cz|kadzet\.com|stahuj-zdarma\.eu|strelci\.net|universal-share\.com)/stahuj/(?P<id>\w+)' -    __version__ = "0.53" -    __description__ = """Share-rapid.com hoster plugin""" -    __author_name__ = ("MikyWoW", "zoidberg", "stickell") -    __author_mail__ = ("mikywow@seznam.cz", "zoidberg@mujmail.cz", "l.stickell@yahoo.it") - -    FILE_NAME_PATTERN = r'<h1[^>]*><span[^>]*>(?:<a[^>]*>)?(?P<N>[^<]+)' -    FILE_SIZE_PATTERN = r'<td class="i">Velikost:</td>\s*<td class="h"><strong>\s*(?P<S>[0-9.]+) (?P<U>[kKMG])i?B</strong></td>' -    FILE_OFFLINE_PATTERN = ur'Nastala chyba 404|Soubor byl smazán' - -    DOWNLOAD_URL_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áš' - -    FILE_URL_REPLACEMENTS = [(__pattern__, r'http://share-rapid.com/stahuj/\g<id>')] - -    def setup(self): -        self.chunkLimit = 1 -        self.resumeDownload = True - -    def process(self, pyfile): -        if not self.account: -            self.fail("User not logged in") - -        try: -            self.html = self.load(pyfile.url, decode=True) -        except BadHeader, e: -            self.account.relogin(self.user) -            self.retry(max_tries=3, reason=str(e)) - -        self.getFileInfo() - -        found = re.search(self.DOWNLOAD_URL_PATTERN, self.html) -        if found: -            link = found.group(1) -            self.logDebug("Premium link: %s" % link) - -            self.download(link, disposition=True) -        else: -            if re.search(self.ERR_LOGIN_PATTERN, self.html): -                self.relogin(self.user) -                self.retry(max_tries=3, 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/module/plugins/hoster/SharebeesCom.py b/module/plugins/hoster/SharebeesCom.py index a4625b6a1..c0fd22c91 100644 --- a/module/plugins/hoster/SharebeesCom.py +++ b/module/plugins/hoster/SharebeesCom.py @@ -4,13 +4,15 @@ from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class SharebeesCom(DeadHoster): -    __name__ = "SharebeesCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?sharebees.com/\w{12}' +    __name__    = "SharebeesCom" +    __type__    = "hoster"      __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?sharebees\.com/\w{12}' +      __description__ = """ShareBees hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")]  getInfo = create_getInfo(SharebeesCom) diff --git a/module/plugins/hoster/ShareonlineBiz.py b/module/plugins/hoster/ShareonlineBiz.py index a5bd9c250..dc3d5093c 100644 --- a/module/plugins/hoster/ShareonlineBiz.py +++ b/module/plugins/hoster/ShareonlineBiz.py @@ -1,195 +1,188 @@  # -*- coding: utf-8 -*-  import re +  from time import time +from urllib import unquote +from urlparse import urlparse -from module.plugins.Hoster import Hoster  from module.network.RequestFactory import getURL -from module.plugins.Plugin import chunks  from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo -def getInfo(urls): -    api_url_base = "http://api.share-online.biz/linkcheck.php" +class ShareonlineBiz(SimpleHoster): +    __name__    = "ShareonlineBiz" +    __type__    = "hoster" +    __version__ = "0.45" -    urls = [url.replace("https://", "http://") for url in urls] +    __pattern__ = r'https?://(?:www\.)?(share-online\.biz|egoshare\.com)/(download\.php\?id=|dl/)(?P<ID>\w+)' -    for chunk in chunks(urls, 90): -        api_param_file = {"links": "\n".join(x.replace("http://www.share-online.biz/dl/", "").rstrip("/") for x in -                                             chunk)}  # api only supports old style links -        src = getURL(api_url_base, post=api_param_file, decode=True) -        result = [] -        for i, res in enumerate(src.split("\n")): -            if not res: -                continue -            fields = res.split(";") +    __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")] -            if fields[1] == "OK": -                status = 2 -            elif fields[1] in ("DELETED", "NOT FOUND"): -                status = 1 -            else: -                status = 3 -            result.append((fields[2], int(fields[3]), status, chunk[i])) -        yield result +    URL_REPLACEMENTS = [(__pattern__ + ".*", "http://www.share-online.biz/dl/\g<ID>")] - -class ShareonlineBiz(Hoster): -    __name__ = "ShareonlineBiz" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?(share-online\.biz|egoshare\.com)/(download.php\?id=|dl/)(?P<ID>\w+)' -    __version__ = "0.40" -    __description__ = """Shareonline.biz hoster plugin""" -    __author_name__ = ("spoob", "mkaay", "zoidberg", "Walter Purcaro") -    __author_mail__ = ("spoob@pyload.org", "mkaay@mkaay.de", "zoidberg@mujmail.cz", "vuolter@gmail.com") +    RECAPTCHA_KEY = "6LdatrsSAAAAAHZrB70txiV5p-8Iv8BtVxlTtjKX"      ERROR_INFO_PATTERN = r'<p class="b">Information:</p>\s*<div>\s*<strong>(.*?)</strong>' -    def setup(self): -        # range request not working? -        #  api supports resume, only one chunk -        #  website isn't supporting resuming in first place -        self.file_id = re.match(self.__pattern__, self.pyfile.url).group("ID") -        self.pyfile.url = "http://www.share-online.biz/dl/" + self.file_id -        self.resumeDownload = self.premium -        self.multiDL = False -        #self.chunkLimit = 1 +    @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} -        self.check_data = None +        if url: +            info['pattern'] = re.match(cls.__pattern__, url).groupdict() -    def process(self, pyfile): -        if self.premium: -            self.handlePremium() -            #web-download fallback removed - didn't work anyway -        else: -            self.handleFree() - -        # check = self.checkDownload({"failure": re.compile(self.ERROR_INFO_PATTERN)}) -        # if check == "failure": -        #     try: -        #         self.retry(reason=self.lastCheck.group(1).decode("utf8")) -        #     except: -        #         self.retry(reason="Unknown error") - -        if self.api_data: -            self.check_data = {"size": int(self.api_data['size']), "md5": self.api_data['md5']} - -    def loadAPIData(self): -        api_url_base = "http://api.share-online.biz/linkcheck.php?md5=1" -        api_param_file = {"links": self.file_id}  # api only supports old style links -        src = self.load(api_url_base, cookies=False, post=api_param_file, decode=True) - -        fields = src.split(";") -        self.api_data = {"fileid": fields[0], -                         "status": fields[1]} -        if not self.api_data["status"] == "OK": -            self.offline() -        else: -            self.api_data["filename"] = fields[2] -            self.api_data["size"] = fields[3]  # in bytes -            self.api_data["md5"] = fields[4].strip().lower().replace("\n\n", "")  # md5 +            field = getURL("http://api.share-online.biz/linkcheck.php", +                           get={'md5': "1"}, +                           post={'links': info['pattern']['ID']}, +                           decode=True).split(";") -    def handleFree(self): -        self.loadAPIData() -        self.pyfile.name = self.api_data["filename"] -        self.pyfile.size = int(self.api_data["size"]) +            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 -        self.html = self.load(self.pyfile.url, cookies=True)  # refer, stuff -        self.setWait(3) -        self.wait() +            elif field[1] in ("DELETED", "NOT FOUND"): +                info['status'] = 1 + +        return info -        self.html = self.load("%s/free/" % self.pyfile.url, post={"dl_free": "1", "choice": "free"}, decode=True) -        self.checkErrors() -        found = re.search(r'var wait=(\d+);', self.html) +    def setup(self): +        self.resumeDownload = self.premium +        self.multiDL        = False + +    def handleCaptcha(self):          recaptcha = ReCaptcha(self) -        for _ in xrange(5): -            challenge, response = recaptcha.challenge("6LdatrsSAAAAAHZrB70txiV5p-8Iv8BtVxlTtjKX") -            self.setWait(int(found.group(1)) if found else 30) -            response = self.load("%s/free/captcha/%d" % (self.pyfile.url, int(time() * 1000)), post={ -                'dl_free': '1', -                'recaptcha_challenge_field': challenge, -                'recaptcha_response_field': response}) - -            if not response == '0': + +        for _i in xrange(5): +            challenge, response = 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() * 1000)), +                            post={'dl_free'                  : "1", +                                  'recaptcha_challenge_field': challenge, +                                  'recaptcha_response_field' : response}) +            if not res == '0':                  self.correctCaptcha() -                break +                return res              else:                  self.invalidCaptcha()          else:              self.invalidCaptcha() -            self.fail("No valid captcha solution received") +            self.fail(_("No valid captcha solution received")) + + +    def handleFree(self): +        self.html = self.load(self.pyfile.url, cookies=True)  #: refer, stuff + +        self.wait(3) + +        self.html = self.load("%s/free/" % self.pyfile.url, post={"dl_free": "1", "choice": "free"}, decode=True) + +        self.checkErrors() + +        res = self.handleCaptcha() + +        download_url = res.decode("base64") -        download_url = response.decode("base64") -        self.logDebug(download_url)          if not download_url.startswith("http://"): -            self.parseError("download url") +            self.error(_("Wrong download url"))          self.wait() +          self.download(download_url) -        # check download + + +    def checkFile(self): +        super(ShareonlineBiz, self).checkFile() +          check = self.checkDownload({ -            "cookie": re.compile(r'<div id="dl_failure"'), -            "fail": re.compile(r"<title>Share-Online") +            '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") +            self.retry(5, 60, _("Cookie failure")) +          elif check == "fail":              self.invalidCaptcha() -            self.retry(5, 5 * 60, "Download failed") -        else: -            self.correctCaptcha() +            self.retry(5, 5 * 60, _("Download failed")) + -    def handlePremium(self):  # should be working better loading (account) api internally +    def handlePremium(self):  #: should be working better loading (account) api internally          self.account.getAccountInfo(self.user, True) -        src = self.load("http://api.share-online.biz/account.php", -                        {"username": self.user, "password": self.account.accounts[self.user]["password"], -                         "act": "download", "lid": self.file_id}) + +        html = self.load("http://api.share-online.biz/account.php", +                        {"username": self.user, "password": self.account.accounts[self.user]['password'], +                         "act": "download", "lid": self.info['fileid']})          self.api_data = dlinfo = {} -        for line in src.splitlines(): + +        for line in html.splitlines():              key, value = line.split(": ")              dlinfo[key.lower()] = value          self.logDebug(dlinfo) -        if not dlinfo["status"] == "online": + +        if not dlinfo['status'] == "online":              self.offline()          else: -            self.pyfile.name = dlinfo["name"] -            self.pyfile.size = int(dlinfo["size"]) +            self.pyfile.name = dlinfo['name'] +            self.pyfile.size = int(dlinfo['size']) + +            dlLink = dlinfo['url'] -            dlLink = dlinfo["url"]              if dlLink == "server_under_maintenance":                  self.tempOffline()              else:                  self.multiDL = True                  self.download(dlLink) +      def checkErrors(self): -        found = re.search(r"/failure/(.*?)/1", self.req.lastEffectiveURL) -        if not found: +        m = re.search(r"/failure/(.*?)/1", self.req.lastEffectiveURL) +        if m is None: +            self.info.pop('error', None)              return -        err = found.group(1) -        found = re.search(self.ERROR_INFO_PATTERN, self.html) -        msg = found.group(1) if found else "" -        self.logError(err, msg or "Unknown error occurred") +        errmsg = m.group(1).lower() + +        try: +            self.logError(errmsg, re.search(self.ERROR_INFO_PATTERN, self.html).group(1)) +        except: +            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) -        if err in ('invalid'): -            self.fail(msg or "File not available") -        elif err in ('freelimit', 'size', 'proxy'): -            self.fail(msg or "Premium account needed")          else: -            if err in 'server': -                self.setWait(600, False) -            elif err in 'expired': -                self.setWait(30, False) -            else: -                self.setWait(300, True) +            self.wantReconnect = True +            self.retry(wait_time=60, reason=errmsg) + -            self.wait() -            self.retry(max_tries=25, reason=msg) +getInfo = create_getInfo(ShareonlineBiz) diff --git a/module/plugins/hoster/ShareplaceCom.py b/module/plugins/hoster/ShareplaceCom.py index ba1b40fb7..ccf4bcda5 100644 --- a/module/plugins/hoster/ShareplaceCom.py +++ b/module/plugins/hoster/ShareplaceCom.py @@ -1,24 +1,30 @@  # -*- coding: utf-8 -*-  import re -import urllib + +from urllib import unquote +  from module.plugins.Hoster import Hoster  class ShareplaceCom(Hoster): -    __name__ = "ShareplaceCom" -    __type__ = "hoster" -    __pattern__ = r'(http://)?(?:www\.)?shareplace\.(com|org)/\?[a-zA-Z0-9]+' +    __name__    = "ShareplaceCom" +    __type__    = "hoster"      __version__ = "0.11" + +    __pattern__ = r'(http://)?(?:www\.)?shareplace\.(com|org)/\?\w+' +      __description__ = """Shareplace.com hoster plugin""" -    __author_name__ = "ACCakut" -    __author_mail__ = "" +    __license__     = "GPLv3" +    __authors__     = [("ACCakut", None)] +      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() @@ -27,11 +33,11 @@ class ShareplaceCom(Hoster):          wait_time = self.get_waiting_time()          self.setWait(wait_time) -        self.logDebug("%s: Waiting %d seconds." % (self.__name__, wait_time))          self.wait() +      def get_waiting_time(self): -        if self.html is None: +        if not self.html:              self.download_html()          #var zzipitime = 15; @@ -43,34 +49,38 @@ class ShareplaceCom(Hoster):          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 = urllib.unquote( +            url = unquote(                  url.replace("http://http:/", "").replace("vvvvvvvvv", "").replace("lllllllll", "").replace(                      "teletubbies", ""))              self.logDebug("URL: %s" % url)              return url          else: -            self.fail("absolute filepath could not be found. offline? ") +            self.error(_("Absolute filepath not found")) +      def get_file_name(self): -        if self.html is None: +        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 self.html is None: +        if not self.html:              self.download_html()          if re.search(r"HTTP Status 404", self.html) is not None: diff --git a/module/plugins/hoster/SharingmatrixCom.py b/module/plugins/hoster/SharingmatrixCom.py new file mode 100644 index 000000000..fa08a4a8f --- /dev/null +++ b/module/plugins/hoster/SharingmatrixCom.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class SharingmatrixCom(DeadHoster): +    __name__    = "SharingmatrixCom" +    __type__    = "hoster" +    __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?sharingmatrix\.com/file/\w+' + +    __description__ = """Sharingmatrix.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("paulking", None)] + + +getInfo = create_getInfo(SharingmatrixCom) diff --git a/module/plugins/hoster/ShragleCom.py b/module/plugins/hoster/ShragleCom.py index a86e66972..bec30f6f2 100644 --- a/module/plugins/hoster/ShragleCom.py +++ b/module/plugins/hoster/ShragleCom.py @@ -4,13 +4,16 @@ from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class ShragleCom(DeadHoster): -    __name__ = "ShragleCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?(cloudnator|shragle).com/files/(?P<ID>.*?)/' +    __name__    = "ShragleCom" +    __type__    = "hoster"      __version__ = "0.22" + +    __pattern__ = r'http://(?:www\.)?(cloudnator|shragle)\.com/files/(?P<ID>.+?)/' +      __description__ = """Cloudnator.com (Shragle.com) hoster plugin""" -    __author_name__ = ("RaNaN", "zoidberg") -    __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz") +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "RaNaN@pyload.org"), +                       ("zoidberg", "zoidberg@mujmail.cz")]  getInfo = create_getInfo(ShragleCom) diff --git a/module/plugins/hoster/SimplyPremiumCom.py b/module/plugins/hoster/SimplyPremiumCom.py index c9fbf2ff1..bf7c43af6 100644 --- a/module/plugins/hoster/SimplyPremiumCom.py +++ b/module/plugins/hoster/SimplyPremiumCom.py @@ -1,103 +1,77 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################ -  import re +  from datetime import datetime, timedelta -from module.plugins.Hoster import Hoster +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo +from module.plugins.internal.SimpleHoster import secondsToMidnight -def secondsToMidnight(): -    # Seconds until 00:10 GMT+2 -    now = datetime.utcnow() + timedelta(hours=2) -    if now.hour is 0 and now.minute < 10: -        midnight = now -    else: -        midnight = now + timedelta(days=1) -    midnight = midnight.replace(hour=0, minute=10, second=0, microsecond=0) -    return int((midnight - now).total_seconds()) +class SimplyPremiumCom(MultiHoster): +    __name__    = "SimplyPremiumCom" +    __type__    = "hoster" +    __version__ = "0.07" +    __pattern__ = r'https?://.+simply-premium\.com' + +    __description__ = """Simply-Premium.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("EvolutionClip", "evolutionclip@live.de")] -class SimplyPremiumCom(Hoster): -    __name__ = "SimplyPremiumCom" -    __version__ = "0.01" -    __type__ = "hoster" -    __pattern__ = r"https?://.*(simply-premium)\.com" -    __description__ = """Simply-Premium.Com hoster plugin""" -    __author_name__ = ("EvolutionClip") -    __author_mail__ = ("evolutionclip@live.de")      def setup(self): -        self.chunkLimit = 16 +        self.chunkLimit     = 16          self.resumeDownload = False -    def process(self, pyfile): -        if re.match(self.__pattern__, pyfile.url): -            new_url = pyfile.url -        elif not self.account: -            self.logError(_("Please enter your %s account or deactivate this plugin") % "Simply-Premium.com") -            self.fail("No Simply-Premium.com account provided") + +    def handlePremium(self): +        for i in xrange(5): +            page = self.load("http://www.simply-premium.com/premium.php", get={'info': "", 'link': self.pyfile.url}) +            self.logDebug("JSON data: " + page) +            if page != '': +                break          else: -            self.logDebug("Old URL: %s" % pyfile.url) -            for i in xrange(5): -                page = self.load('http://www.simply-premium.com/premium.php?info&link=' + pyfile.url) -                self.logDebug("JSON data: " + page) -                if page != '': -                    break -            else: -                self.logInfo("Unable to get API data, waiting 1 minute and retry") -                self.retry(5, 60, "Unable to get API data") - -            if '<valid>0</valid>' in page or ( -                    "You are not allowed to download from this host" in page and self.premium): -                self.account.relogin(self.user) -                self.retry() -            elif "NOTFOUND" in page: -                self.offline() -            elif "downloadlimit" in page: -                self.logInfo("Reached maximum connctions") -                self.retry(5, 60, "Reached maximum connctions") -            elif "trafficlimit" in page: -                self.logInfo("Reached daily limit for this host. Waiting until 00:10 GMT+2") -                self.retry(5, secondsToMidnight(), "Daily limit for this host reached") -            elif "hostererror" in page: -                self.logInfo("Hoster temporarily unavailable, waiting 1 minute and retry") -                self.retry(5, 60, "Hoster is temporarily unavailable") -            #page = json_loads(page) -            #new_url = page.keys()[0] -            #self.api_data = page[new_url] - -            try: -                start = page.index('<name>') + len('<name>') -                end = page.index('</name>', start) -                self.pyfile.name = page[start:end] -            except ValueError: -                self.pyfile.name = "" - -            try: -                start = page.index('<size>') + len('<size>') -                end = page.index('</size>', start) -                self.pyfile.size = int(float(page[start:end])) -            except ValueError: -                self.pyfile.size = 0 - -            new_url = 'http://www.simply-premium.com/premium.php?link=' + pyfile.url - -        if new_url != pyfile.url: -            self.logDebug("New URL: " + new_url) - -        self.download(new_url, disposition=True) +            self.logInfo(_("Unable to get API data, waiting 1 minute and retry")) +            self.retry(5, 60, "Unable to get API data") + +        if '<valid>0</valid>' in page or ( +                "You are not allowed to download from this host" in page and self.premium): +            self.account.relogin(self.user) +            self.retry() + +        elif "NOTFOUND" in page: +            self.offline() + +        elif "downloadlimit" in page: +            self.logWarning(_("Reached maximum connctions")) +            self.retry(5, 60, "Reached maximum connctions") + +        elif "trafficlimit" in page: +            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 page: +            self.logWarning(_("Hoster temporarily unavailable, waiting 1 minute and retry")) +            self.retry(5, 60, "Hoster is temporarily unavailable") + +        try: +            self.pyfile.name = re.search(r'<name>([^<]+)</name>', page).group(1) +        except AttributeError: +            self.pyfile.name = "" + +        try: +            self.pyfile.size = re.search(r'<size>(\d+)</size>', page).group(1) +        except AttributeError: +            self.pyfile.size = 0 + +        try: +            self.link = re.search(r'<download>([^<]+)</download>', page).group(1) +        except AttributeError: +            self.link = 'http://www.simply-premium.com/premium.php?link=' + self.pyfile.url + +        if self.link != self.pyfile.url: +            self.logDebug("New URL: " + self.link) + + +getInfo = create_getInfo(SimplyPremiumCom) diff --git a/module/plugins/hoster/SimplydebridCom.py b/module/plugins/hoster/SimplydebridCom.py index 2aab12e04..5e6b47efc 100644 --- a/module/plugins/hoster/SimplydebridCom.py +++ b/module/plugins/hoster/SimplydebridCom.py @@ -2,58 +2,62 @@  import re -from module.plugins.Hoster import Hoster +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo -class SimplydebridCom(Hoster): -    __name__ = "SimplydebridCom" -    __version__ = "0.1" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/sd.php/*' +class SimplydebridCom(MultiHoster): +    __name__    = "SimplydebridCom" +    __type__    = "hoster" +    __version__ = "0.14" + +    __pattern__ = r'http://\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/sd\.php' +      __description__ = """Simply-debrid.com hoster plugin""" -    __author_name__ = "Kagenoshin" -    __author_mail__ = "kagenoshin@gmx.ch" +    __license__     = "GPLv3" +    __authors__     = [("Kagenoshin", "kagenoshin@gmx.ch")] -    def setup(self): -        self.resumeDownload = self.multiDL = True -        self.chunkLimit = 1 -    def process(self, pyfile): -        if not self.account: -            self.logError(_("Please enter your %s account or deactivate this plugin") % "simply-debrid.com") -            self.fail("No simply-debrid.com account provided") +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True +        self.chunkLimit     = 1 -        self.logDebug("Old URL: %s" % pyfile.url) +    def handlePremium(self):          #fix the links for simply-debrid.com! -        new_url = pyfile.url -        new_url = new_url.replace("clz.to", "cloudzer.net/file") -        new_url = new_url.replace("http://share-online", "http://www.share-online") -        new_url = new_url.replace("ul.to", "uploaded.net/file") -        new_url = new_url.replace("uploaded.com", "uploaded.net") -        new_url = new_url.replace("filerio.com", "filerio.in") -        new_url = new_url.replace("lumfile.com", "lumfile.se") -        if('fileparadox' in new_url): -            new_url = new_url.replace("http://", "https://") - -        if re.match(self.__pattern__, new_url): -            new_url = new_url - -        self.logDebug("New URL: %s" % new_url) - -        if not re.match(self.__pattern__, new_url): -            page = self.load('http://simply-debrid.com/api.php', get={'dl': new_url})  # +'&u='+self.user+'&p='+self.account.getAccountData(self.user)['password']) +        self.link = self.pyfile.url +        self.link = self.link.replace("clz.to", "cloudzer.net/file") +        self.link = self.link.replace("http://share-online", "http://www.share-online") +        self.link = self.link.replace("ul.to", "uploaded.net/file") +        self.link = self.link.replace("uploaded.com", "uploaded.net") +        self.link = self.link.replace("filerio.com", "filerio.in") +        self.link = self.link.replace("lumfile.com", "lumfile.se") + +        if('fileparadox' in self.link): +            self.link = self.link.replace("http://", "https://") + +        if re.match(self.__pattern__, self.link): +            self.link = self.link + +        self.logDebug("New URL: %s" % self.link) + +        if not re.match(self.__pattern__, self.link): +            page = self.load("http://simply-debrid.com/api.php", get={'dl': self.link})  # +'&u='+self.user+'&p='+self.account.getAccountData(self.user)['password'])              if 'tiger Link' in page or 'Invalid Link' in page or ('API' in page and 'ERROR' in page): -                self.fail('Unable to unrestrict link') -            new_url = page +                self.fail(_("Unable to unrestrict link")) +            self.link = page          self.setWait(5)          self.wait() -        self.logDebug("Unrestricted URL: " + new_url) -        self.download(new_url, disposition=True) + +    def checkFile(self): +        super(SimplydebridCom, self).checkFile()          check = self.checkDownload({"bad1": "No address associated with hostname", "bad2": "<html"})          if check == "bad1" or check == "bad2":              self.retry(24, 3 * 60, "Bad file downloaded") + + +getInfo = create_getInfo(SimplydebridCom) diff --git a/module/plugins/hoster/SockshareCom.py b/module/plugins/hoster/SockshareCom.py index 017c8a839..aabb8dcd1 100644 --- a/module/plugins/hoster/SockshareCom.py +++ b/module/plugins/hoster/SockshareCom.py @@ -1,96 +1,20 @@  # -*- coding: utf-8 -*- -############################################################################### -#  This program is free software: you can redistribute it and/or modify -#  it under the terms of the GNU Affero General Public License as -#  published by the Free Software Foundation, either version 3 of the -#  License, or (at your option) any later version. -# -#  This program is distributed in the hope that it will be useful, -#  but WITHOUT ANY WARRANTY; without even the implied warranty of -#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -#  GNU Affero General Public License for more details. -# -#  You should have received a copy of the GNU Affero General Public License -#  along with this program.  If not, see <http://www.gnu.org/licenses/>. -# -#  @author: Walter Purcaro -############################################################################### -import re -from os import rename +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo +class SockshareCom(DeadHoster): +    __name__    = "SockshareCom" +    __type__    = "hoster" +    __version__ = "0.05" -class SockshareCom(SimpleHoster): -    __name__ = "SockshareCom" -    __type__ = "hoster"      __pattern__ = r'http://(?:www\.)?sockshare\.com/(mobile/)?(file|embed)/(?P<ID>\w+)' -    __version__ = "0.02" -    __description__ = """Sockshare.com hoster plugin""" -    __author_name__ = ("jeix", "stickell", "Walter Purcaro") -    __author_mail__ = ("jeix@hasnomail.de", "l.stickell@yahoo.it", "vuolter@gmail.com") - -    FILE_INFO_PATTERN = r'site-content">\s*<h1>(?P<N>.+)<strong>\( (?P<S>[^)]+) \)</strong></h1>' -    FILE_OFFLINE_PATTERN = r'>This file doesn\'t exist, or has been removed.<' - -    FILE_URL_REPLACEMENTS = [(__pattern__, r'http://www.sockshare.com/file/\g<ID>')] - -    def setup(self): -        self.multiDL = self.resumeDownload = True -        self.chunkLimit = -1 - -    def handleFree(self): -        name = self.pyfile.name -        link = self._getLink() -        self.logDebug("Direct link: " + link) -        self.download(link, disposition=True) -        self.processName(name) -    def _getLink(self): -        hash_data = re.search(r'<input type="hidden" value="([a-z0-9]+)" name="hash">', self.html) -        if not hash_data: -            self.parseError("Unable to detect hash") - -        post_data = {"hash": hash_data.group(1), "confirm": "Continue+as+Free+User"} -        self.html = self.load(self.pyfile.url, post=post_data) -        if (">You have exceeded the daily stream limit for your country\\. You can wait until tomorrow" in self.html or -            "(>This content server has been temporarily disabled for upgrades|Try again soon\\. You can still download it below\\.<)" in self.html): -            self.retry(wait_time=60 * 60 * 2, reason="Download limit exceeded or server disabled")  # 2 hours wait - -        patterns = (r'(/get_file\.php\?id=[A-Z0-9]+&key=[a-zA-Z0-9=]+&original=1)', -                    r'(/get_file\.php\?download=[A-Z0-9]+&key=[a-z0-9]+)', -                    r'(/get_file\.php\?download=[A-Z0-9]+&key=[a-z0-9]+&original=1)', -                    r'<a href="/gopro\.php">Tired of ads and waiting\? Go Pro!</a>[\t\n\rn ]+</div>[\t\n\rn ]+<a href="(/.*?)"') -        for pattern in patterns: -            link = re.search(pattern, self.html) -            if link: -                break -        else: -            link = re.search(r"playlist: '(/get_file\.php\?stream=[a-zA-Z0-9=]+)'", self.html) -            if link: -                self.html = self.load("http://www.sockshare.com" + link.group(1)) -                link = re.search(r'media:content url="(http://.*?)"', self.html) -                if not link: -                    link = re.search(r'\"(http://media\\-b\\d+\\.sockshare\\.com/download/\\d+/.*?)\"', self.html) -            else: -                self.parseError('Unable to detect a download link') - -        link = link.group(1).replace("&", "&") -        if link.startswith("http://"): -            return link -        else: -            return "http://www.sockshare.com" + link - -    def processName(self, name_old): -        name = self.pyfile.name -        if name <= name_old: -            return -        name_new = re.sub(r'\.[^.]+$', "", name_old) + name[len(name_old):] -        filename = self.lastDownload -        self.pyfile.name = name_new -        rename(filename, filename.rsplit(name)[0] + name_new) -        self.logInfo("%(name)s renamed to %(newname)s" % {"name": name, "newname": name_new}) +    __description__ = """Sockshare.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")]  getInfo = create_getInfo(SockshareCom) diff --git a/module/plugins/hoster/SoundcloudCom.py b/module/plugins/hoster/SoundcloudCom.py index a1ec1378a..3eb546604 100644 --- a/module/plugins/hoster/SoundcloudCom.py +++ b/module/plugins/hoster/SoundcloudCom.py @@ -1,44 +1,47 @@  # -*- coding: utf-8 -*- -import re  import pycurl +import re  from module.plugins.Hoster import Hoster  class SoundcloudCom(Hoster): -    __name__ = "SoundcloudCom" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?soundcloud\.com/(?P<UID>.*?)/(?P<SID>.*)' -    __version__ = "0.1" +    __name__    = "SoundcloudCom" +    __type__    = "hoster" +    __version__ = "0.10" + +    __pattern__ = r'https?://(?:www\.)?soundcloud\.com/(?P<UID>.+?)/(?P<SID>.+)' +      __description__ = """SoundCloud.com hoster plugin""" -    __author_name__ = "Peekayy" -    __author_mail__ = "peekayy.dev@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("Peekayy", "peekayy.dev@gmail.com")] +      def process(self, pyfile):          # default UserAgent of HTTPRequest fails for this hoster so we use this one          self.req.http.c.setopt(pycurl.USERAGENT, 'Mozilla/5.0')          page = self.load(pyfile.url) -        match = re.search(r'<div class="haudio.*?large.*?" data-sc-track="(?P<ID>[0-9]*)"', page) +        m = re.search(r'<div class="haudio.*?large.*?" data-sc-track="(?P<ID>\d*)"', page)          songId = clientId = "" -        if match: -            songId = match.group("ID") +        if m: +            songId = m.group('ID')          if len(songId) <= 0: -            self.logError("Could not find song id") +            self.logError(_("Could not find song id"))              self.offline()          else: -            match = re.search(r'"clientID":"(?P<CID>.*?)"', page) -            if match: -                clientId = match.group("CID") +            m = re.search(r'"clientID":"(?P<CID>.*?)"', page) +            if m: +                clientId = m.group('CID')              if len(clientId) <= 0:                  clientId = "b45b1aa10f1ac2941910a7f0d10f8e28" -            match = re.search(r'<em itemprop="name">\s(?P<TITLE>.*?)\s</em>', page) -            if match: -                pyfile.name = match.group("TITLE") + ".mp3" +            m = re.search(r'<em itemprop="name">\s(?P<TITLE>.*?)\s</em>', page) +            if m: +                pyfile.name = m.group('TITLE') + ".mp3"              else: -                pyfile.name = re.match(self.__pattern__, pyfile.url).group("SID") + ".mp3" +                pyfile.name = re.match(self.__pattern__, pyfile.url).group('SID') + ".mp3"              # url to retrieve the actual song url              page = self.load("https://api.sndcdn.com/i1/tracks/%s/streams" % songId, get={"client_id": clientId}) @@ -46,7 +49,7 @@ class SoundcloudCom(Hoster):              # for now we choose the first stream found in all cases              # it could be improved if relevant for this hoster              streams = [ -                (result.group("QUALITY"), result.group("URL")) +                (result.group('QUALITY'), result.group('URL'))                  for result in re.finditer(r'"(?P<QUALITY>.*?)":"(?P<URL>.*?)"', page)              ]              self.logDebug("Found Streams", streams) diff --git a/module/plugins/hoster/SpeedLoadOrg.py b/module/plugins/hoster/SpeedLoadOrg.py index 76ab52868..a13220eab 100644 --- a/module/plugins/hoster/SpeedLoadOrg.py +++ b/module/plugins/hoster/SpeedLoadOrg.py @@ -4,13 +4,15 @@ from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class SpeedLoadOrg(DeadHoster): -    __name__ = "SpeedLoadOrg" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?speedload\.org/(?P<ID>\w+)' +    __name__    = "SpeedLoadOrg" +    __type__    = "hoster"      __version__ = "1.02" + +    __pattern__ = r'http://(?:www\.)?speedload\.org/(?P<ID>\w+)' +      __description__ = """Speedload.org hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")]  getInfo = create_getInfo(SpeedLoadOrg) diff --git a/module/plugins/hoster/SpeedfileCz.py b/module/plugins/hoster/SpeedfileCz.py index 63e126f0a..f23c8d4c7 100644 --- a/module/plugins/hoster/SpeedfileCz.py +++ b/module/plugins/hoster/SpeedfileCz.py @@ -1,33 +1,18 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class SpeedfileCz(DeadHoster): -    __name__ = "SpeedFileCz" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?speedfile.cz/.*' +    __name__    = "SpeedFileCz" +    __type__    = "hoster"      __version__ = "0.32" + +    __pattern__ = r'http://(?:www\.)?speedfile\.cz/.+' +      __description__ = """Speedfile.cz hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")]  getInfo = create_getInfo(SpeedfileCz) diff --git a/module/plugins/hoster/SpeedyshareCom.py b/module/plugins/hoster/SpeedyshareCom.py new file mode 100644 index 000000000..fa54d6060 --- /dev/null +++ b/module/plugins/hoster/SpeedyshareCom.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# +# Test links: +# http://speedy.sh/ep2qY/Zapp-Brannigan.jpg + +import re + +from urlparse import urljoin + +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo + + +class SpeedyshareCom(SimpleHoster): +    __name__    = "SpeedyshareCom" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'https?://(?:www\.)?(speedyshare\.com|speedy\.sh)/\w+' + +    __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_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): +        m = re.search(self.LINK_PATTERN, self.html) +        if m is None: +            self.error(_("Download link not found")) + +        dl_link = urljoin("http://www.speedyshare.com", m.group(1)) +        self.download(dl_link, disposition=True) + +        check = self.checkDownload({'html': re.compile("html")}) +        if check == "html": +            self.error(_("Downloaded file is an html page")) + + +getInfo = create_getInfo(SpeedyshareCom) diff --git a/module/plugins/hoster/StorageTo.py b/module/plugins/hoster/StorageTo.py new file mode 100644 index 000000000..bedc2934f --- /dev/null +++ b/module/plugins/hoster/StorageTo.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class StorageTo(DeadHoster): +    __name__    = "StorageTo" +    __type__    = "hoster" +    __version__ = "0.01" + +    __pattern__ = r'http://(?:www\.)?storage\.to/get/.+' + +    __description__ = """Storage.to hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("mkaay", "mkaay@mkaay.de")] + + +getInfo = create_getInfo(StorageTo) diff --git a/module/plugins/hoster/StreamCz.py b/module/plugins/hoster/StreamCz.py index bbef31a67..11d4efcdb 100644 --- a/module/plugins/hoster/StreamCz.py +++ b/module/plugins/hoster/StreamCz.py @@ -1,25 +1,9 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re -from module.plugins.Hoster import Hoster +  from module.network.RequestFactory import getURL +from module.plugins.Hoster import Hoster  def getInfo(urls): @@ -28,7 +12,7 @@ def getInfo(urls):      for url in urls:          html = getURL(url) -        if re.search(StreamCz.FILE_OFFLINE_PATTERN, html): +        if re.search(StreamCz.OFFLINE_PATTERN, html):              # File offline              result.append((url, 0, 1, url))          else: @@ -37,46 +21,51 @@ def getInfo(urls):  class StreamCz(Hoster): -    __name__ = "StreamCz" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?stream.cz/[^/]+/\d+.*' -    __version__ = "0.1" +    __name__    = "StreamCz" +    __type__    = "hoster" +    __version__ = "0.20" + +    __pattern__ = r'https?://(?:www\.)?stream\.cz/[^/]+/\d+' +      __description__ = """Stream.cz hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __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>' -    FILE_OFFLINE_PATTERN = r'<h1 class="commonTitle">Str.nku nebylo mo.n. nal.zt \(404\)</h1>' -    FILE_NAME_PATTERN = r'<link rel="video_src" href="http://www.stream.cz/\w+/(\d+)-([^"]+)" />'      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.multiDL = True          self.resumeDownload = True +        self.multiDL        = True -    def process(self, pyfile): +    def process(self, pyfile):          self.html = self.load(pyfile.url, decode=True) -        if re.search(self.FILE_OFFLINE_PATTERN, self.html): +        if re.search(self.OFFLINE_PATTERN, self.html):              self.offline() -        found = re.search(self.CDN_PATTERN, self.html) -        if found is None: -            self.fail("Parse error (CDN)") -        cdn = found.groupdict() +        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") +            self.fail(_("Stream URL not found")) -        found = re.search(self.FILE_NAME_PATTERN, self.html) -        if found is None: -            self.fail("Parse error (NAME)") -        pyfile.name = "%s-%s.%s.mp4" % (found.group(2), found.group(1), cdnkey[-2:]) +        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): %s" % (cdnkey[-2:], download_url)) +        self.logInfo(_("STREAM: %s") % cdnkey[-2:], download_url)          self.download(download_url) diff --git a/module/plugins/hoster/StreamcloudEu.py b/module/plugins/hoster/StreamcloudEu.py index 388eb7876..4f854a99d 100644 --- a/module/plugins/hoster/StreamcloudEu.py +++ b/module/plugins/hoster/StreamcloudEu.py @@ -1,120 +1,31 @@  # -*- coding: utf-8 -*- -from time import sleep  import re -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo -from module.network.HTTPRequest import HTTPRequest +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo -class StreamcloudEu(XFileSharingPro): -    __name__ = "StreamcloudEu" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?streamcloud\.eu/\S+' -    __version__ = "0.04" -    __description__ = """Streamcloud.eu hoster plugin""" -    __author_name__ = "seoester" -    __author_mail__ = "seoester@googlemail.com" - -    HOSTER_NAME = "streamcloud.eu" - -    DIRECT_LINK_PATTERN = r'file: "(http://(stor|cdn)\d+\.streamcloud.eu:?\d*/.*/video\.(mp4|flv))",' - -    def setup(self): -        super(StreamcloudEu, self).setup() -        self.multiDL = True - -    def getDownloadLink(self): -        found = re.search(self.DIRECT_LINK_PATTERN, self.html, re.S) -        if found: -            return found.group(1) - -        for i in xrange(5): -            self.logDebug("Getting download link: #%d" % i) -            data = self.getPostParameters() -            httpRequest = HTTPRequest(options=self.req.options) -            httpRequest.cj = self.req.cj -            sleep(10) -            self.html = httpRequest.load(self.pyfile.url, post=data, referer=False, cookies=True, decode=True) -            self.header = httpRequest.header - -            found = re.search("Location\s*:\s*(.*)", self.header, re.I) -            if found: -                break - -            found = re.search(self.DIRECT_LINK_PATTERN, self.html, re.S) -            if found: -                break - -        else: -            if self.errmsg and 'captcha' in self.errmsg: -                self.fail("No valid captcha code entered") -            else: -                self.fail("Download link not found") +class StreamcloudEu(XFSHoster): +    __name__    = "StreamcloudEu" +    __type__    = "hoster" +    __version__ = "0.09" -        return found.group(1) +    __pattern__ = r'http://(?:www\.)?streamcloud\.eu/\w{12}' -    def getPostParameters(self): -        for i in xrange(3): -            if not self.errmsg: -                self.checkErrors() - -            if hasattr(self, "FORM_PATTERN"): -                action, inputs = self.parseHtmlForm(self.FORM_PATTERN) -            else: -                action, inputs = self.parseHtmlForm(input_names={"op": re.compile("^download")}) - -            if not inputs: -                action, inputs = self.parseHtmlForm('F1') -                if not inputs: -                    if self.errmsg: -                        self.retry() -                    else: -                        self.parseError("Form not found") - -            self.logDebug(self.HOSTER_NAME, inputs) - -            if 'op' in inputs and inputs['op'] in ('download1', 'download2', 'download3'): -                if "password" in inputs: -                    if self.passwords: -                        inputs['password'] = self.passwords.pop(0) -                    else: -                        self.fail("No or invalid passport") - -                if not self.premium: -                    found = re.search(self.WAIT_PATTERN, self.html) -                    if found: -                        wait_time = int(found.group(1)) + 1 -                        self.setWait(wait_time, False) -                    else: -                        wait_time = 0 - -                    self.captcha = self.handleCaptcha(inputs) - -                    if wait_time: -                        self.wait() +    __description__ = """Streamcloud.eu hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("seoester", "seoester@googlemail.com")] -                self.errmsg = None -                self.logDebug("getPostParameters {0}".format(i)) -                return inputs -            else: -                inputs['referer'] = self.pyfile.url +    HOSTER_DOMAIN = "streamcloud.eu" -                if self.premium: -                    inputs['method_premium'] = "Premium Download" -                    if 'method_free' in inputs: -                        del inputs['method_free'] -                else: -                    inputs['method_free'] = "Free Download" -                    if 'method_premium' in inputs: -                        del inputs['method_premium'] +    LINK_PATTERN = r'file: "(http://(stor|cdn)\d+\.streamcloud\.eu:?\d*/.*/video\.(mp4|flv))",' -                self.html = self.load(self.pyfile.url, post=inputs, ref=False) -                self.errmsg = None -        else: -            self.parseError('FORM: %s' % (inputs['op'] if 'op' in inputs else 'UNKNOWN')) +    def setup(self): +        self.multiDL = True +        self.chunkLimit = 1 +        self.resumeDownload = self.premium  getInfo = create_getInfo(StreamcloudEu) diff --git a/module/plugins/hoster/TurbobitNet.py b/module/plugins/hoster/TurbobitNet.py index f15b8b0c4..258ec7d3e 100644 --- a/module/plugins/hoster/TurbobitNet.py +++ b/module/plugins/hoster/TurbobitNet.py @@ -1,63 +1,49 @@  # -*- coding: utf-8 -*- -""" -    Copyright (C) 2012  pyLoad team -    Copyright (C) 2012  JD-Team support@jdownloader.org - -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" - -import re  import random -from urllib import quote -from binascii import hexlify, unhexlify +import re  import time -from pycurl import HTTPHEADER  from Crypto.Cipher import ARC4 +from binascii import hexlify, unhexlify +from pycurl import HTTPHEADER +from urllib import quote +  from module.network.RequestFactory import getURL -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, timestamp  from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, timestamp  class TurbobitNet(SimpleHoster): -    __name__ = "TurbobitNet" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?(turbobit.net|unextfiles.com)/(?!download/folder/)(?:download/free/)?(?P<ID>\w+).*' -    __version__ = "0.11" -    __description__ = """Turbobit.net plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" - -    # long filenames are shortened -    FILE_INFO_PATTERN = r"<span class='file-icon1[^>]*>(?P<N>[^<]+)</span>\s*\((?P<S>[^\)]+)\)\s*</h1>" -    FILE_NAME_PATTERN = r'<meta name="keywords" content="\s+(?P<N>[^,]+)'  # full name but missing on page2 -    FILE_OFFLINE_PATTERN = r'<h2>File Not Found</h2>|html\(\'File (?:was )?not found' -    FILE_URL_REPLACEMENTS = [(r"http://(?:www\.)?(turbobit.net|unextfiles.com)/(?:download/free/)?(?P<ID>\w+).*", -                              "http://turbobit.net/\g<ID>.html")] -    SH_COOKIES = [("turbobit.net", "user_lang", "en")] - -    CAPTCHA_KEY_PATTERN = r'src="http://api\.recaptcha\.net/challenge\?k=([^"]+)"' -    DOWNLOAD_URL_PATTERN = r'(?P<url>/download/redirect/[^"\']+)' -    LIMIT_WAIT_PATTERN = r'<div id="time-limit-text">\s*.*?<span id=\'timeout\'>(\d+)</span>' -    CAPTCHA_SRC_PATTERN = r'<img alt="Captcha" src="(.*?)"' +    __name__    = "TurbobitNet" +    __type__    = "hoster" +    __version__ = "0.16" + +    __pattern__ = r'http://(?:www\.)?turbobit\.net/(?:download/free/)?(?P<ID>\w+)' + +    __description__ = """ Turbobit.net hoster plugin """ +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("prOq", None)] + + +    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_PATTERN = r'(/download/redirect/[^"\']+)' +    LIMIT_WAIT_PATTERN = r'<div id=\'timeout\'>(\d+)<' + +    CAPTCHA_PATTERN = r'<img alt="Captcha" src="(.+?)"' +      def handleFree(self): -        self.url = "http://turbobit.net/download/free/%s" % self.file_info['ID'] -        self.html = self.load(self.url) +        self.url = "http://turbobit.net/download/free/%s" % self.info['pattern']['ID'] +        self.html = self.load(self.url, ref=True, decode=True)          rtUpdate = self.getRtUpdate() @@ -70,79 +56,81 @@ class TurbobitNet(SimpleHoster):          self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With:"])          self.downloadFile() +      def solveCaptcha(self): -        for _ in xrange(5): -            found = re.search(self.LIMIT_WAIT_PATTERN, self.html) -            if found: -                wait_time = int(found.group(1)) +        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.parseError("captcha form") +                self.error(_("Captcha form not found"))              self.logDebug(inputs)              if inputs['captcha_type'] == 'recaptcha':                  recaptcha = ReCaptcha(self) -                found = re.search(self.CAPTCHA_KEY_PATTERN, self.html) -                captcha_key = found.group(1) if found else '6LcTGLoSAAAAAHCWY9TTIrQfjUlxu6kZlTYP50_c' -                inputs['recaptcha_challenge_field'], inputs['recaptcha_response_field'] = recaptcha.challenge( -                    captcha_key) +                inputs['recaptcha_challenge_field'], inputs['recaptcha_response_field'] = recaptcha.challenge()              else: -                found = re.search(self.CAPTCHA_SRC_PATTERN, self.html) -                if not found: -                    self.parseError('captcha') -                captcha_url = found.group(1) +                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 not "<div class='download-timer-header'>" in self.html: +            if '<div class="captcha-error">Incorrect, try again!<' in self.html:                  self.invalidCaptcha()              else:                  self.correctCaptcha()                  break          else: -            self.fail("Invalid captcha") +            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(): +            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                 +                # 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.logDebug("rtUpdate")                  self.setStorage("rtUpdate", rtUpdate)                  self.setStorage("timestamp", timestamp())                  self.setStorage("version", self.__version__)              else: -                self.logError("Unable to download, wait for update...") +                self.logError(_("Unable to download, wait for update..."))                  self.tempOffline()          return rtUpdate +      def getDownloadUrl(self, rtUpdate):          self.req.http.lastURL = self.url -        found = re.search("(/\w+/timeout\.js\?\w+=)([^\"\'<>]+)", self.html) -        url = "http://turbobit.net%s%s" % (found.groups() if found else ( -        '/files/timeout.js?ver=', ''.join(random.choice('0123456789ABCDEF') for _ in xrange(32)))) +        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.file_info['ID'], b, quote(fun), rtUpdate) +                          self.info['pattern']['ID'], b, quote(fun), rtUpdate)              try:                  out = self.js.eval(self.jscode) @@ -157,26 +145,28 @@ class TurbobitNet(SimpleHoster):                  self.delStorage("rtUpdate")              self.retry() +      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) +      def handlePremium(self):          self.logDebug("Premium download as user %s" % self.user) -        self.html = self.load(self.pyfile.url)  # Useless in 0.5          self.downloadFile() +      def downloadFile(self): -        found = re.search(self.DOWNLOAD_URL_PATTERN, self.html) -        if not found: -            self.parseError("download link") -        self.url = "http://turbobit.net" + found.group('url') -        self.logDebug(self.url) +        m = re.search(self.LINK_PATTERN, self.html) +        if m is None: +            self.error(_("Download link not found")) +        self.url = "http://turbobit.net" + m.group(1)          self.download(self.url) diff --git a/module/plugins/hoster/TurbouploadCom.py b/module/plugins/hoster/TurbouploadCom.py index 3f3deca15..f0893cef6 100644 --- a/module/plugins/hoster/TurbouploadCom.py +++ b/module/plugins/hoster/TurbouploadCom.py @@ -1,33 +1,18 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class TurbouploadCom(DeadHoster): -    __name__ = "TurbouploadCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?turboupload.com/(\w+).*' +    __name__    = "TurbouploadCom" +    __type__    = "hoster"      __version__ = "0.03" + +    __pattern__ = r'http://(?:www\.)?turboupload\.com/(\w+)' +      __description__ = """Turboupload.com hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")]  getInfo = create_getInfo(TurbouploadCom) diff --git a/module/plugins/hoster/TusfilesNet.py b/module/plugins/hoster/TusfilesNet.py index c31b88f20..a4e352956 100644 --- a/module/plugins/hoster/TusfilesNet.py +++ b/module/plugins/hoster/TusfilesNet.py @@ -1,42 +1,35 @@  # -*- coding: utf-8 -*- -############################################################################### -#  This program is free software: you can redistribute it and/or modify -#  it under the terms of the GNU Affero General Public License as -#  published by the Free Software Foundation, either version 3 of the -#  License, or (at your option) any later version. -# -#  This program is distributed in the hope that it will be useful, -#  but WITHOUT ANY WARRANTY; without even the implied warranty of -#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -#  GNU Affero General Public License for more details. -# -#  You should have received a copy of the GNU Affero General Public License -#  along with this program.  If not, see <http://www.gnu.org/licenses/>. -############################################################################### - -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo - - -class TusfilesNet(XFileSharingPro): -    __name__ = "TusfilesNet" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?tusfiles\.net/(?P<ID>\w+)' -    __version__ = "0.03" + +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo + + +class TusfilesNet(XFSHoster): +    __name__    = "TusfilesNet" +    __type__    = "hoster" +    __version__ = "0.08" + +    __pattern__ = r'https?://(?:www\.)?tusfiles\.net/\w{12}' +      __description__ = """Tusfiles.net hoster plugin""" -    __author_name__ = "Walter Purcaro" -    __author_mail__ = "vuolter@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com"), +                       ("guidobelix", "guidobelix@hotmail.it")] + -    HOSTER_NAME = "tusfiles.net" +    HOSTER_DOMAIN = "tusfiles.net" -    FILE_INFO_PATTERN = r'\](?P<N>.+) - (?P<S>[\d.]+) (?P<U>\w+)\[' -    FILE_OFFLINE_PATTERN = r'>File Not Found|<Title>TusFiles - Fast Sharing Files!' +    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' -    SH_COOKIES = [(".tusfiles.net", "lang", "english")]      def setup(self): -        self.multiDL = False -        self.chunkLimit = -1 +        self.multiDL        = False +        self.chunkLimit     = -1          self.resumeDownload = True +    def handlePremium(self): +        return self.handleFree() + +  getInfo = create_getInfo(TusfilesNet) diff --git a/module/plugins/hoster/TwoSharedCom.py b/module/plugins/hoster/TwoSharedCom.py index 7881ca098..1dd67f974 100644 --- a/module/plugins/hoster/TwoSharedCom.py +++ b/module/plugins/hoster/TwoSharedCom.py @@ -6,29 +6,35 @@ from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class TwoSharedCom(SimpleHoster): -    __name__ = "TwoSharedCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?2shared.com/(account/)?(download|get|file|document|photo|video|audio)/.*' -    __version__ = "0.11" +    __name__    = "TwoSharedCom" +    __type__    = "hoster" +    __version__ = "0.12" + +    __pattern__ = r'http://(?:www\.)?2shared\.com/(account/)?(download|get|file|document|photo|video|audio)/.+' +      __description__ = """2Shared.com hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __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_PATTERN = r'window.location =\'(.+?)\';' -    FILE_NAME_PATTERN = r'<h1>(?P<N>.*)</h1>' -    FILE_SIZE_PATTERN = r'<span class="dtitle">File size:</span>\s*(?P<S>[0-9,.]+) (?P<U>[kKMG])i?B' -    FILE_OFFLINE_PATTERN = r'The file link that you requested is not valid\.|This file was deleted\.' -    DOWNLOAD_URL_PATTERN = r"window.location ='([^']+)';"      def setup(self): -        self.resumeDownload = self.multiDL = True +        self.resumeDownload = True +        self.multiDL        = True +      def handleFree(self): -        found = re.search(self.DOWNLOAD_URL_PATTERN, self.html) -        if not found: -            self.parseError('Download link') -        link = found.group(1) -        self.logDebug("Download URL %s" % link) +        m = re.search(self.LINK_PATTERN, self.html) +        if m is None: +            self.error(_("Download link")) +        link = m.group(1)          self.download(link) diff --git a/module/plugins/hoster/UlozTo.py b/module/plugins/hoster/UlozTo.py index 282d4605b..6b84a5e1b 100644 --- a/module/plugins/hoster/UlozTo.py +++ b/module/plugins/hoster/UlozTo.py @@ -1,26 +1,11 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re  import time -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo +  from module.common.json_layer import json_loads +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo +  def convertDecimalPrefix(m):      # decimal prefixes used in filesize and traffic @@ -28,61 +13,71 @@ def convertDecimalPrefix(m):  class UlozTo(SimpleHoster): -    __name__ = "UlozTo" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?(uloz\.to|ulozto\.(cz|sk|net)|bagruj.cz|zachowajto.pl)/(?:live/)?(?P<id>\w+/[^/?]*)' -    __version__ = "0.98" +    __name__    = "UlozTo" +    __type__    = "hoster" +    __version__ = "1.01" + +    __pattern__ = r'http://(?:www\.)?(uloz\.to|ulozto\.(cz|sk|net)|bagruj\.cz|zachowajto\.pl)/(?:live/)?(?P<ID>\w+/[^/?]*)' +      __description__ = """Uloz.to hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" - -    FILE_NAME_PATTERN = r'<title>(?P<N>[^<]+) \| Uloz.to</title>' -    FILE_SIZE_PATTERN = r'<span id="fileSize">.*?(?P<S>[0-9.]+\s[kMG]?B)</span>' -    FILE_INFO_PATTERN = r'<p>File <strong>(?P<N>[^<]+)</strong> is password protected</p>' -    FILE_OFFLINE_PATTERN = r'<title>404 - Page not found</title>|<h1 class="h1">File (has been deleted|was banned)</h1>' -    FILE_SIZE_REPLACEMENTS = [('([0-9.]+)\s([kMG])B', convertDecimalPrefix)] -    FILE_URL_REPLACEMENTS = [(r"(?<=http://)([^/]+)", "www.ulozto.net")] - -    ADULT_PATTERN = r'<form action="(?P<link>[^\"]*)" method="post" id="frm-askAgeForm">' -    PASSWD_PATTERN = r'<div class="passwordProtectedFile">' +    __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 = [('([\d.]+)\s([kMG])B', convertDecimalPrefix)] + +    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">' -    FREE_URL_PATTERN = r'<div class="freeDownloadForm"><form action="([^"]+)"' +    TOKEN_PATTERN   = r'<input type="hidden" name="_token_" .*?value="(.+?)"' + +    FREE_URL_PATTERN    = r'<div class="freeDownloadForm"><form action="([^"]+)"'      PREMIUM_URL_PATTERN = r'<div class="downloadForm"><form action="([^"]+)"' -    TOKEN_PATTERN = r'<input type="hidden" name="_token_" id="[^\"]*" value="(?P<token>[^\"]*)" />' +      def setup(self): -        self.multiDL = self.premium +        self.multiDL        = self.premium          self.resumeDownload = True +      def process(self, pyfile):          pyfile.url = re.sub(r"(?<=http://)([^/]+)", "www.ulozto.net", pyfile.url)          self.html = self.load(pyfile.url, decode=True, cookies=True)          if re.search(self.ADULT_PATTERN, self.html): -            self.logInfo("Adult content confirmation needed. Proceeding..") +            self.logInfo(_("Adult content confirmation needed")) -            found = re.search(self.TOKEN_PATTERN, self.html) -            if not found: -                self.parseError('TOKEN') -            token = found.group(1) +            m = re.search(self.TOKEN_PATTERN, self.html) +            if m is None: +                self.error(_("TOKEN_PATTERN not found")) +            token = m.group(1)              self.html = self.load(pyfile.url, get={"do": "askAgeForm-submit"},                                    post={"agree": "Confirm", "_token_": token}, cookies=True) -        passwords = self.getPassword().splitlines() -        while self.PASSWD_PATTERN in self.html: -            if passwords: -                password = passwords.pop(0) -                self.logInfo("Password protected link, trying " + password) +        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'}, cookies=True) + +                if self.PASSWD_PATTERN in self.html: +                    self.fail(_("Incorrect password"))              else: -                self.fail("No or incorrect password") +                self.fail(_("No password found"))          if re.search(self.VIPLINK_PATTERN, self.html):              self.html = self.load(pyfile.url, get={"disclaimer": "1"}) -        self.file_info = self.getFileInfo() +        self.getFileInfo()          if self.premium and self.checkTrafficLeft():              self.handlePremium() @@ -91,58 +86,62 @@ class UlozTo(SimpleHoster):          self.doCheckDownload() +      def handleFree(self):          action, inputs = self.parseHtmlForm('id="frm-downloadDialog-freeDownloadForm"')          if not action or not inputs: -            self.parseError("free download form") +            self.error(_("Free download form not found")) -        self.logDebug('inputs.keys() = ' + str(inputs.keys())) +        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')): +        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) +            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')): +        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)) +            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) +            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.parseError("CAPTCHA form changed") +            self.error(_("CAPTCHA form changed"))          self.multiDL = True          self.download("http://www.ulozto.net" + action, post=inputs, cookies=True, disposition=True) +      def handlePremium(self):          self.download(self.pyfile.url + "?do=directDownload", disposition=True)          #parsed_url = self.findDownloadURL(premium=True)          #self.download(parsed_url, post={"download": "Download"}) +      def findDownloadURL(self, premium=False): -        msg = "%s link" % ("Premium" if premium else "Free") -        found = re.search(self.PREMIUM_URL_PATTERN if premium else self.FREE_URL_PATTERN, self.html) -        if not found: -            self.parseError(msg) -        parsed_url = "http://www.ulozto.net" + found.group(1) +        msg = _("%s link" % ("Premium" if premium else "Free")) +        m = re.search(self.PREMIUM_URL_PATTERN if premium else self.FREE_URL_PATTERN, self.html) +        if m is None: +            self.error(msg) +        parsed_url = "http://www.ulozto.net" + m.group(1)          self.logDebug("%s: %s" % (msg, parsed_url))          return parsed_url +      def doCheckDownload(self):          check = self.checkDownload({              "wrong_captcha": re.compile(r'<ul class="error">\s*<li>Error rewriting the text.</li>'), -            "offline": re.compile(self.FILE_OFFLINE_PATTERN), +            "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>" @@ -152,18 +151,18 @@ class UlozTo(SimpleHoster):              #self.delStorage("captcha_id")              #self.delStorage("captcha_text")              self.invalidCaptcha() -            self.retry(reason="Wrong captcha code") +            self.retry(reason=_("Wrong captcha code"))          elif check == "offline":              self.offline()          elif check == "passwd": -            self.fail("Wrong password") +            self.fail(_("Wrong password"))          elif check == "server_error": -            self.logError("Server error, try downloading later") +            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") +            self.fail(_("Server error - file not downloadable"))  getInfo = create_getInfo(UlozTo) diff --git a/module/plugins/hoster/UloziskoSk.py b/module/plugins/hoster/UloziskoSk.py index d24e21779..0afa4e5a5 100644 --- a/module/plugins/hoster/UloziskoSk.py +++ b/module/plugins/hoster/UloziskoSk.py @@ -1,75 +1,65 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. +import re -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo -    @author: zoidberg -""" -import re -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, PluginParseError +class UloziskoSk(SimpleHoster): +    __name__    = "UloziskoSk" +    __type__    = "hoster" +    __version__ = "0.24" +    __pattern__ = r'http://(?:www\.)?ulozisko\.sk/.+' -class UloziskoSk(SimpleHoster): -    __name__ = "UloziskoSk" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?ulozisko.sk/.*' -    __version__ = "0.23"      __description__ = """Ulozisko.sk hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] -    URL_PATTERN = r'<form name = "formular" action = "([^"]+)" method = "post">' + +    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_PATTERN = r'<form name = "formular" action = "([^"]+)" method = "post">'      ID_PATTERN = r'<input type = "hidden" name = "id" value = "([^"]+)" />' -    FILE_NAME_PATTERN = r'<div class="down1">(?P<N>[^<]+)</div>' -    FILE_SIZE_PATTERN = ur'VeÄŸkosÅ¥ súboru: <strong>(?P<S>[0-9.]+) (?P<U>[kKMG])i?B</strong><br />' -    CAPTCHA_PATTERN = r'<img src="(/obrazky/obrazky.php\?fid=[^"]+)" alt="" />' -    FILE_OFFLINE_PATTERN = ur'<span class = "red">ZadanÜ súbor neexistuje z jedného z nasledujúcich dÃŽvodov:</span>' +    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() -        found = re.search(self.IMG_PATTERN, self.html) -        if found: -            url = "http://ulozisko.sk" + found.group(1) +        m = re.search(self.IMG_PATTERN, self.html) +        if m: +            url = "http://ulozisko.sk" + m.group(1)              self.download(url)          else:              self.handleFree() +      def handleFree(self): -        found = re.search(self.URL_PATTERN, self.html) -        if found is None: -            raise PluginParseError('URL') -        parsed_url = 'http://www.ulozisko.sk' + found.group(1) +        m = re.search(self.LINK_PATTERN, self.html) +        if m is None: +            self.error(_("LINK_PATTERN not found")) +        parsed_url = 'http://www.ulozisko.sk' + m.group(1) -        found = re.search(self.ID_PATTERN, self.html) -        if found is None: -            raise PluginParseError('ID') -        id = found.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) +        self.logDebug("URL:" + parsed_url + ' ID:' + id) -        found = re.search(self.CAPTCHA_PATTERN, self.html) -        if found is None: -            raise PluginParseError('CAPTCHA') -        captcha_url = 'http://www.ulozisko.sk' + found.group(1) +        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.logDebug("CAPTCHA_URL:" + captcha_url + ' CAPTCHA:' + captcha)          self.download(parsed_url, post={              "antispam": captcha, diff --git a/module/plugins/hoster/UnibytesCom.py b/module/plugins/hoster/UnibytesCom.py index 1a64146c4..8c39cce82 100644 --- a/module/plugins/hoster/UnibytesCom.py +++ b/module/plugins/hoster/UnibytesCom.py @@ -1,53 +1,46 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. +import re -    @author: zoidberg -""" +from urlparse import urljoin -import re  from pycurl import FOLLOWLOCATION +  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class UnibytesCom(SimpleHoster): -    __name__ = "UnibytesCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?unibytes\.com/[a-zA-Z0-9-._ ]{11}B' -    __version__ = "0.1" +    __name__    = "UnibytesCom" +    __type__    = "hoster" +    __version__ = "0.11" + +    __pattern__ = r'https?://(?:www\.)?unibytes\.com/[\w .-]{11}B' +      __description__ = """UniBytes.com hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    FILE_INFO_PATTERN = r'<span[^>]*?id="fileName"[^>]*>(?P<N>[^>]+)</span>\s*\((?P<S>\d.*?)\)' -    DOMAIN = 'http://www.unibytes.com' +    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' -    DOWNLOAD_LINK_PATTERN = r'<a href="([^"]+)">Download</a>' +    LINK_PATTERN = r'<a href="([^"]+)">Download</a>' +      def handleFree(self): +        domain = "http://www.%s/" % self.HOSTER_DOMAIN          action, post_data = self.parseHtmlForm('id="startForm"')          self.req.http.c.setopt(FOLLOWLOCATION, 0) -        for _ in xrange(8): +        for _i in xrange(8):              self.logDebug(action, post_data) -            self.html = self.load(self.DOMAIN + action, post=post_data) +            self.html = self.load(urljoin(domain, action), post=post_data) -            found = re.search(r'location:\s*(\S+)', self.req.http.header, re.I) -            if found: -                url = found.group(1) +            m = re.search(r'location:\s*(\S+)', self.req.http.header, re.I) +            if m: +                url = m.group(1)                  break              if '>Somebody else is already downloading using your IP-address<' in self.html: @@ -55,9 +48,9 @@ class UnibytesCom(SimpleHoster):                  self.retry()              if post_data['step'] == 'last': -                found = re.search(self.DOWNLOAD_LINK_PATTERN, self.html) -                if found: -                    url = found.group(1) +                m = re.search(self.LINK_PATTERN, self.html) +                if m: +                    url = m.group(1)                      self.correctCaptcha()                      break                  else: @@ -67,14 +60,13 @@ class UnibytesCom(SimpleHoster):              action, post_data = self.parseHtmlForm('id="stepForm"')              if last_step == 'timer': -                found = re.search(self.WAIT_PATTERN, self.html) -                self.wait(int(found.group(1)) if found else 60, False) -            elif last_step in ('captcha', 'last'): -                post_data['captcha'] = self.decryptCaptcha(self.DOMAIN + '/captcha.jpg') +                m = re.search(self.WAIT_PATTERN, self.html) +                self.wait(int(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") +            self.fail(_("No valid captcha code entered")) -        self.logDebug('Download link: ' + url)          self.req.http.c.setopt(FOLLOWLOCATION, 1)          self.download(url) diff --git a/module/plugins/hoster/UnrestrictLi.py b/module/plugins/hoster/UnrestrictLi.py index a0447d873..c84f1daa7 100644 --- a/module/plugins/hoster/UnrestrictLi.py +++ b/module/plugins/hoster/UnrestrictLi.py @@ -1,100 +1,88 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################  import re +  from datetime import datetime, timedelta -from module.plugins.Hoster import Hoster  from module.common.json_layer import json_loads +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo +from module.plugins.internal.SimpleHoster import secondsToMidnight -def secondsToMidnight(): -    # Seconds until 00:10 GMT+2 -    now = datetime.utcnow() + timedelta(hours=2) -    if now.hour is 0 and now.minute < 10: -        midnight = now -    else: -        midnight = now + timedelta(days=1) -    midnight = midnight.replace(hour=0, minute=10, second=0, microsecond=0) -    return int((midnight - now).total_seconds()) +class UnrestrictLi(MultiHoster): +    __name__    = "UnrestrictLi" +    __type__    = "hoster" +    __version__ = "0.20" +    __pattern__ = r'https?://(?:www\.)?(unrestrict|unr)\.li/dl/[\w^_]+' -class UnrestrictLi(Hoster): -    __name__ = "UnrestrictLi" -    __version__ = "0.11" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:[^/]*\.)?(unrestrict|unr)\.li'      __description__ = """Unrestrict.li hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it")] + + +    LOGIN_ACCOUNT = False +      def setup(self): -        self.chunkLimit = 16 +        self.chunkLimit     = 16          self.resumeDownload = True -    def process(self, pyfile): -        if re.match(self.__pattern__, pyfile.url): -            new_url = pyfile.url -        elif not self.account: -            self.logError(_("Please enter your %s account or deactivate this plugin") % "Unrestrict.li") -            self.fail("No Unrestrict.li account provided") + +    def handleFree(self): +        for _i in xrange(5): +            page = self.load('https://unrestrict.li/unrestrict.php', +                             post={'link': self.pyfile.url, 'domain': 'long'}) +            self.logDebug("JSON data: " + page) +            if page != '': +                break          else: -            self.logDebug("Old URL: %s" % pyfile.url) -            for _ in xrange(5): -                page = self.req.load('https://unrestrict.li/unrestrict.php', -                                     post={'link': pyfile.url, 'domain': 'long'}) -                self.logDebug("JSON data: " + page) -                if page != '': -                    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 page or ("You are not allowed to " -                                             "download from this host" in page and self.premium): -                self.account.relogin(self.user) -                self.retry() -            elif "File offline" in page: -                self.offline() -            elif "You are not allowed to download from this host" in page: -                self.fail("You are not allowed to download from this host") -            elif "You have reached your daily limit for this host" in page: -                self.logInfo("Reached daily limit for this host. Waiting until 00:10 GMT+2") -                self.retry(5, secondsToMidnight(), "Daily limit for this host reached") -            elif "ERROR_HOSTER_TEMPORARILY_UNAVAILABLE" in page: -                self.logInfo("Hoster temporarily unavailable, waiting 1 minute and retry") -                self.retry(5, 60, "Hoster is temporarily unavailable") -            page = json_loads(page) -            new_url = page.keys()[0] -            self.api_data = page[new_url] - -        if new_url != pyfile.url: -            self.logDebug("New URL: " + new_url) +            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 page or ("You are not allowed to " +                                         "download from this host" in page and self.premium): +            self.account.relogin(self.user) +            self.retry() + +        elif "File offline" in page: +            self.offline() + +        elif "You are not allowed to download from this host" in page: +            self.fail(_("You are not allowed to download from this host")) + +        elif "You have reached your daily limit for this host" in page: +            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 page: +            self.logInfo(_("Hoster temporarily unavailable, waiting 1 minute and retry")) +            self.retry(5, 60, "Hoster is temporarily unavailable") + +        page = json_loads(page) +        self.link = page.keys()[0] +        self.api_data = page[self.link] + +        if self.link != self.pyfile.url: +            self.logDebug("New URL: " + self.link)          if hasattr(self, 'api_data'):              self.setNameSize() -        self.download(new_url, disposition=True) + +    def checkFile(self): +        super(UnrestrictLi, self).checkFile()          if self.getConfig("history"): -            self.load("https://unrestrict.li/history/&delete=all") -            self.logInfo("Download history deleted") +            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'] + + +getInfo = create_getInfo(UnrestrictLi) diff --git a/module/plugins/hoster/UpleaCom.py b/module/plugins/hoster/UpleaCom.py new file mode 100644 index 000000000..7ae46ef38 --- /dev/null +++ b/module/plugins/hoster/UpleaCom.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +import re + +from urlparse import urljoin + +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo + + +class UpleaCom(XFSHoster): +    __name__    = "UpleaCom" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'https?://(?:www\.)?uplea\.com/dl/\w{15}' + +    __description__ = """Uplea.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Redleon", None)] + + +    HOSTER_DOMAIN = "uplea.com" + +    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): +        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(int(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.wait(15) +        self.download(m.group(1), disposition=True) + + +getInfo = create_getInfo(UpleaCom) diff --git a/module/plugins/hoster/UploadStationCom.py b/module/plugins/hoster/UploadStationCom.py index b0229aba4..d987770fe 100644 --- a/module/plugins/hoster/UploadStationCom.py +++ b/module/plugins/hoster/UploadStationCom.py @@ -4,13 +4,16 @@ from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class UploadStationCom(DeadHoster): -    __name__ = "UploadStationCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?uploadstation\.com/file/(?P<id>[A-Za-z0-9]+)' +    __name__    = "UploadStationCom" +    __type__    = "hoster"      __version__ = "0.52" + +    __pattern__ = r'http://(?:www\.)?uploadstation\.com/file/(?P<ID>\w+)' +      __description__ = """UploadStation.com hoster plugin""" -    __author_name__ = ("fragonib", "zoidberg") -    __author_mail__ = ("fragonib[AT]yahoo[DOT]es", "zoidberg@mujmail.cz") +    __license__     = "GPLv3" +    __authors__     = [("fragonib", "fragonib[AT]yahoo[DOT]es"), +                       ("zoidberg", "zoidberg@mujmail.cz")]  getInfo = create_getInfo(UploadStationCom) diff --git a/module/plugins/hoster/UploadableCh.py b/module/plugins/hoster/UploadableCh.py new file mode 100644 index 000000000..b09e3ded4 --- /dev/null +++ b/module/plugins/hoster/UploadableCh.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- + +import re + +from time import sleep + +from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo + + +class UploadableCh(SimpleHoster): +    __name__    = "UploadableCh" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?uploadable\.ch/file/(?P<ID>\w+)' + +    __description__ = """Uploadable.ch hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zapp-brannigan", "fuerst.reinje@web.de"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    FILE_URL_REPLACEMENTS = [(__pattern__ + ".*", r'http://www.uploadable.ch/file/\g<ID>')] + +    FILE_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): +        # Click the "free user" button and wait +        a = self.load(self.pyfile.url, cookies=True, post={'downloadLink': "wait"}, decode=True) +        self.logDebug(a) + +        self.wait(30) + +        # Make the recaptcha appear and show it the pyload interface +        b = self.load(self.pyfile.url, cookies=True, post={'checkDownload': "check"}, decode=True) +        self.logDebug(b)  #: Expected output: {"success":"showCaptcha"} + +        recaptcha = ReCaptcha(self) + +        challenge, response = recaptcha.challenge(self.RECAPTCHA_KEY) + +        # Submit the captcha solution +        self.load("http://www.uploadable.ch/checkReCaptcha.php", +                  cookies=True, +                  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(self.pyfile.url, cookies=True, post={'downloadLink': "show"}, decode=True) + +        self.wait(3) + +        # Download the file +        self.download(self.pyfile.url, cookies=True, post={'download': "normal"}, disposition=True) + + +    def checkFile(self): +        super(UploadableCh, self).checkFile() + +        check = self.checkDownload({'wait_or_reconnect': re.compile("Please wait for"), +                                    'is_html'          : re.compile("<head>")}) + +        if check == "wait_or_reconnect": +            self.logInfo("Downloadlimit reached, please wait or reconnect") +            self.wait(60 * 60, True) +            self.retry() + +        elif check == "is_html": +            self.error("Downloaded file is an html file") + + +getInfo = create_getInfo(UploadableCh) diff --git a/module/plugins/hoster/UploadboxCom.py b/module/plugins/hoster/UploadboxCom.py new file mode 100644 index 000000000..031c5f761 --- /dev/null +++ b/module/plugins/hoster/UploadboxCom.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class UploadboxCom(DeadHoster): +    __name__    = "Uploadbox" +    __type__    = "hoster" +    __version__ = "0.05" + +    __pattern__ = r'http://(?:www\.)?uploadbox\.com/files/.+' + +    __description__ = """UploadBox.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +getInfo = create_getInfo(UploadboxCom) diff --git a/module/plugins/hoster/UploadedTo.py b/module/plugins/hoster/UploadedTo.py index 6d17bcbd9..833468a80 100644 --- a/module/plugins/hoster/UploadedTo.py +++ b/module/plugins/hoster/UploadedTo.py @@ -1,18 +1,19 @@  # -*- coding: utf-8 -*- - -# Test links (random.bin): +# +# Test links:  # http://ul.to/044yug9o  # http://ul.to/gzfhd0xs  import re -from time import sleep -from module.utils import html_unescape, parseFileSize +from time import sleep -from module.plugins.Hoster import Hoster  from module.network.RequestFactory import getURL +from module.plugins.Hoster import Hoster  from module.plugins.Plugin import chunks  from module.plugins.internal.CaptchaService import ReCaptcha +from module.utils import html_unescape, parseFileSize +  key = "bGhGMkllZXByd2VEZnU5Y2NXbHhYVlZ5cEE1bkEzRUw=".decode('base64') @@ -30,10 +31,10 @@ def getAPIData(urls):      for i, url in enumerate(urls):          id = getID(url) -        post["id_%s" % i] = id +        post['id_%s' % i] = id          idMap[id] = url -    for _ in xrange(5): +    for _i in xrange(5):          api = unicode(getURL("http://uploaded.net/api/filemultiple", post=post, decode=False), 'iso-8859-1')          if api != "can't find request":              break @@ -54,17 +55,22 @@ def getAPIData(urls):  def parseFileInfo(self, url='', html=''):      if not html and hasattr(self, "html"):          html = self.html -    name, size, status, found, fileid = url, 0, 3, None, None -    if re.search(self.FILE_OFFLINE_PATTERN, html): +    name = url +    size = 0 +    fileid = None + +    if re.search(self.OFFLINE_PATTERN, html):          # File offline          status = 1      else: -        found = re.search(self.FILE_INFO_PATTERN, html) -        if found: -            name, fileid = html_unescape(found.group('N')), found.group('ID') -            size = parseFileSize(found.group('S')) +        m = re.search(self.INFO_PATTERN, html) +        if m: +            name, fileid = html_unescape(m.group('N')), m.group('ID') +            size = parseFileSize(m.group('S'))              status = 2 +        else: +            status = 3      return name, size, status, fileid @@ -86,26 +92,34 @@ def getInfo(urls):  class UploadedTo(Hoster): -    __name__ = "UploadedTo" -    __type__ = "hoster" +    __name__    = "UploadedTo" +    __type__    = "hoster" +    __version__ = "0.75" +      __pattern__ = r'https?://(?:www\.)?(uploaded\.(to|net)|ul\.to)(/file/|/?\?id=|.*?&id=|/)(?P<ID>\w+)' -    __version__ = "0.72" +      __description__ = """Uploaded.net hoster plugin""" -    __author_name__ = ("spoob", "mkaay", "zoidberg", "netpok", "stickell") -    __author_mail__ = ("spoob@pyload.org", "mkaay@mkaay.de", "zoidberg@mujmail.cz", -                       "netpok@gmail.com", "l.stickell@yahoo.it") +    __license__     = "GPLv3" +    __authors__     = [("spoob", "spoob@pyload.org"), +                       ("mkaay", "mkaay@mkaay.de"), +                       ("zoidberg", "zoidberg@mujmail.cz"), +                       ("netpok", "netpok@gmail.com"), +                       ("stickell", "l.stickell@yahoo.it")] + + +    INFO_PATTERN = r'<a href="file/(?P<ID>\w+)" id="filename">(?P<N>[^<]+)</a>  \s*<small[^>]*>(?P<S>[^<]+)</small>' +    OFFLINE_PATTERN = r'<small class="cL">Error: 404</small>' +    DL_LIMIT_PATTERN = r'You have reached the max. number of possible free downloads for this hour' -    FILE_INFO_PATTERN = r'<a href="file/(?P<ID>\w+)" id="filename">(?P<N>[^<]+)</a>  \s*<small[^>]*>(?P<S>[^<]+)</small>' -    FILE_OFFLINE_PATTERN = r'<small class="cL">Error: 404</small>' -    DL_LIMIT_PATTERN = "You have reached the max. number of possible free downloads for this hour"      def setup(self): -        self.multiDL = self.resumeDownload = self.premium +        self.multiDL    = self.resumeDownload = self.premium          self.chunkLimit = 1  # critical problems with more chunks          self.fileID = getID(self.pyfile.url)          self.pyfile.url = "http://uploaded.net/file/%s" % self.fileID +      def process(self, pyfile):          self.load("http://uploaded.net/language/en", just_header=True) @@ -114,7 +128,7 @@ class UploadedTo(Hoster):          # TODO: fallback to parse from site, because api sometimes delivers wrong status codes          if not api: -            self.logWarning("No response for API call") +            self.logWarning(_("No response for API call"))              self.html = unicode(self.load(pyfile.url, decode=False), 'iso-8859-1')              name, size, status, self.fileID = parseFileInfo(self) @@ -124,7 +138,8 @@ class UploadedTo(Hoster):              elif status == 2:                  pyfile.name, pyfile.size = name, size              else: -                self.fail('Parse error - file info') +                self.error(_("file info")) +          elif api == 'Access denied':              self.fail(_("API key invalid")) @@ -145,83 +160,82 @@ class UploadedTo(Hoster):          else:              self.handleFree() +      def handlePremium(self):          info = self.account.getAccountInfo(self.user, True)          self.logDebug("%(name)s: Use Premium Account (%(left)sGB left)" % {"name": self.__name__, -                                                                           "left": info["trafficleft"] / 1024 / 1024}) -        if int(self.data[1]) / 1024 > info["trafficleft"]: -            self.logInfo(_("%s: Not enough traffic left" % self.__name__)) +                                                                           "left": info['trafficleft'] / 1024 / 1024}) +        if int(self.data[1]) / 1024 > info['trafficleft']: +            self.logInfo(_("Not enough traffic left"))              self.account.empty(self.user)              self.resetAccount()              self.fail(_("Traffic exceeded"))          header = self.load("http://uploaded.net/file/%s" % self.fileID, just_header=True) -        if "location" in header: +        if 'location' in header:              #Direct download -            print "Direct Download: " + header['location'] +            self.logDebug("Direct download link detected")              self.download(header['location'])          else:              #Indirect download              self.html = self.load("http://uploaded.net/file/%s" % self.fileID) -            found = re.search(r'<div class="tfree".*\s*<form method="post" action="(.*?)"', self.html) -            if not found: -                self.fail("Download URL not found. Try to enable direct downloads.") -            url = found.group(1) -            print "Premium URL: " + url +            m = re.search(r'<div class="tfree".*\s*<form method="post" action="(.*?)"', self.html) +            if m is None: +                self.fail(_("Download URL not m. Try to enable direct downloads")) +            url = m.group(1)              self.download(url, post={}) +      def handleFree(self):          self.html = self.load(self.pyfile.url, decode=True)          if 'var free_enabled = false;' in self.html: -            self.logError("Free-download capacities exhausted.") -            self.retry(max_tries=24, wait_time=5 * 60) +            self.logError(_("Free-download capacities exhausted")) +            self.retry(24, 5 * 60) -        found = re.search(r"Current waiting period: <span>(\d+)</span> seconds", self.html) -        if not found: -            self.fail("File not downloadable for free users") -        self.setWait(int(found.group(1))) +        m = re.search(r"Current waiting period: <span>(\d+)</span> seconds", self.html) +        if m is None: +            self.fail(_("File not downloadable for free users")) +        self.setWait(int(m.group(1))) -        js = self.load("http://uploaded.net/js/download.js", decode=True) - -        challengeId = re.search(r'Recaptcha\.create\("([^"]+)', js) +        self.html = self.load("http://uploaded.net/js/download.js", decode=True)          url = "http://uploaded.net/io/ticket/captcha/%s" % self.fileID          downloadURL = "" -        for _ in xrange(5): -            re_captcha = ReCaptcha(self) -            challenge, result = re_captcha.challenge(challengeId.group(1)) -            options = {"recaptcha_challenge_field": challenge, "recaptcha_response_field": result} +        recaptcha = ReCaptcha(self) + +        for _i in xrange(5): +            challenge, response = recaptcha.challenge() +            options = {"recaptcha_challenge_field": challenge, "recaptcha_response_field": response}              self.wait()              result = self.load(url, post=options) -            self.logDebug("result: %s" % result) +            self.logDebug("Result: %s" % result)              if "limit-size" in result: -                self.fail("File too big for free download") +                self.fail(_("File too big for free download"))              elif "limit-slot" in result:  # Temporary restriction so just wait a bit                  self.setWait(30 * 60, True)                  self.wait()                  self.retry()              elif "limit-parallel" in result: -                self.fail("Cannot download in parallel") -            elif self.DL_LIMIT_PATTERN in result:  # limit-dl +                self.fail(_("Cannot download in parallel")) +            elif "limit-dl" in result or self.DL_LIMIT_PATTERN in result:  # limit-dl                  self.setWait(3 * 60 * 60, True)                  self.wait()                  self.retry() -            elif 'err:"captcha"' in result: -                self.logError("ul.net captcha is disabled") +            elif '"err":"captcha"' in result:                  self.invalidCaptcha()              elif "type:'download'" in result:                  self.correctCaptcha()                  downloadURL = re.search("url:'([^']+)", result).group(1)                  break              else: -                self.fail("Unknown error '%s'") +                self.error(_("Unknown error: %s") % result)          if not downloadURL: -            self.fail("No Download url retrieved/all captcha attempts failed") +            self.fail(_("No Download url retrieved/all captcha attempts failed"))          self.download(downloadURL, disposition=True)          check = self.checkDownload({"limit-dl": self.DL_LIMIT_PATTERN}) diff --git a/module/plugins/hoster/UploadhereCom.py b/module/plugins/hoster/UploadhereCom.py new file mode 100644 index 000000000..8da30be46 --- /dev/null +++ b/module/plugins/hoster/UploadhereCom.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class UploadhereCom(DeadHoster): +    __name__    = "UploadhereCom" +    __type__    = "hoster" +    __version__ = "0.12" + +    __pattern__ = r'http://(?:www\.)?uploadhere\.com/\w{10}' + +    __description__ = """Uploadhere.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +getInfo = create_getInfo(UploadhereCom) diff --git a/module/plugins/hoster/UploadheroCom.py b/module/plugins/hoster/UploadheroCom.py index 6ee3dbeba..189079017 100644 --- a/module/plugins/hoster/UploadheroCom.py +++ b/module/plugins/hoster/UploadheroCom.py @@ -1,90 +1,81 @@  # -*- coding: utf-8 -*- - -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" - -# Test link (random.bin): +# +# Test links:  # http://uploadhero.co/dl/wQBRAVSM  import re +  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class UploadheroCom(SimpleHoster): -    __name__ = "UploadheroCom" -    __type__ = "hoster" +    __name__    = "UploadheroCom" +    __type__    = "hoster" +    __version__ = "0.16" +      __pattern__ = r'http://(?:www\.)?uploadhero\.com?/dl/\w+' -    __version__ = "0.15" +      __description__ = """UploadHero.co plugin""" -    __author_name__ = ("mcmyst", "zoidberg") -    __author_mail__ = ("mcmyst@hotmail.fr", "zoidberg@mujmail.cz") +    __license__     = "GPLv3" +    __authors__     = [("mcmyst", "mcmyst@hotmail.fr"), +                       ("zoidberg", "zoidberg@mujmail.cz")] + -    SH_COOKIES = [("http://uploadhero.co", "lang", "en")] -    FILE_NAME_PATTERN = r'<div class="nom_de_fichier">(?P<N>.*?)</div>' -    FILE_SIZE_PATTERN = r'Taille du fichier : </span><strong>(?P<S>.*?)</strong>' -    FILE_OFFLINE_PATTERN = r'<p class="titre_dl_2">|<div class="raison"><strong>Le lien du fichier ci-dessus n\'existe plus.' +    NAME_PATTERN = r'<div class="nom_de_fichier">(?P<N>.*?)</div>' +    SIZE_PATTERN = r'Taille du fichier : </span><strong>(?P<S>.*?)</strong>' +    OFFLINE_PATTERN = r'<p class="titre_dl_2">|<div class="raison"><strong>Le lien du fichier ci-dessus n\'existe plus.' -    DOWNLOAD_URL_PATTERN = r'<a href="([^"]+)" id="downloadnow"' +    COOKIES = [("uploadhero.co", "lang", "en")] -    IP_BLOCKED_PATTERN = r'href="(/lightbox_block_download.php\?min=.*?)"' +    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\?[a-z0-9]+)"' -    FREE_URL_PATTERN = r'var magicomfg = \'<a href="(http://[^<>"]*?)"|"(http://storage\d+\.uploadhero\.co/\?d=[A-Za-z0-9]+/[^<>"/]+)"' +    CAPTCHA_PATTERN = r'"(/captchadl\.php\?\w+)"' +    FREE_URL_PATTERN = r'var magicomfg = \'<a href="(http://[^<>"]*?)"|"(http://storage\d+\.uploadhero\.co/\?d=\w+/[^<>"/]+)"' +    PREMIUM_URL_PATTERN = r'<a href="([^"]+)" id="downloadnow"' +      def handleFree(self):          self.checkErrors() -        found = re.search(self.CAPTCHA_PATTERN, self.html) -        if not found: -            self.parseError("Captcha URL") -        captcha_url = "http://uploadhero.co" + found.group(1) +        m = re.search(self.CAPTCHA_PATTERN, self.html) +        if m is None: +            self.error(_("CAPTCHA_PATTERN not found")) +        captcha_url = "http://uploadhero.co" + m.group(1) -        for _ in xrange(5): +        for _i in xrange(5):              captcha = self.decryptCaptcha(captcha_url)              self.html = self.load(self.pyfile.url, get={"code": captcha}) -            found = re.search(self.FREE_URL_PATTERN, self.html) -            if found: +            m = re.search(self.FREE_URL_PATTERN, self.html) +            if m:                  self.correctCaptcha() -                download_url = found.group(1) or found.group(2) +                download_url = m.group(1) or m.group(2)                  break              else:                  self.invalidCaptcha()          else: -            self.fail("No valid captcha code entered") +            self.fail(_("No valid captcha code entered"))          self.download(download_url) +      def handlePremium(self):          self.logDebug("%s: Use Premium Account" % self.__name__) -        self.html = self.load(self.pyfile.url) -        link = re.search(self.DOWNLOAD_URL_PATTERN, self.html).group(1) -        self.logDebug("Downloading link : '%s'" % link) +        link = re.search(self.PREMIUM_URL_PATTERN, self.html).group(1)          self.download(link) +      def checkErrors(self): -        found = re.search(self.IP_BLOCKED_PATTERN, self.html) -        if found: -            self.html = self.load("http://uploadhero.co%s" % found.group(1)) +        m = re.search(self.IP_BLOCKED_PATTERN, self.html) +        if m: +            self.html = self.load("http://uploadhero.co%s" % m.group(1)) -            found = re.search(self.IP_WAIT_PATTERN, self.html) -            wait_time = (int(found.group(1)) * 60 + int(found.group(2))) if found else 5 * 60 +            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() +        self.info.pop('error', None) +  getInfo = create_getInfo(UploadheroCom) diff --git a/module/plugins/hoster/UploadingCom.py b/module/plugins/hoster/UploadingCom.py index f3cc7a1c6..b163f2252 100644 --- a/module/plugins/hoster/UploadingCom.py +++ b/module/plugins/hoster/UploadingCom.py @@ -1,63 +1,53 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: jeix -""" -  import re +  from pycurl import HTTPHEADER -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, timestamp  from module.common.json_layer import json_loads +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, timestamp  class UploadingCom(SimpleHoster): -    __name__ = "UploadingCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?uploading\.com/files/(?:get/)?(?P<ID>[\w\d]+)' -    __version__ = "0.34" +    __name__    = "UploadingCom" +    __type__    = "hoster" +    __version__ = "0.39" + +    __pattern__ = r'http://(?:www\.)?uploading\.com/files/(?:get/)?(?P<ID>\w+)' +      __description__ = """Uploading.com hoster plugin""" -    __author_name__ = ("jeix", "mkaay", "zoidberg") -    __author_mail__ = ("jeix@hasnomail.de", "mkaay@mkaay.de", "zoidberg@mujmail.cz") +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("mkaay", "mkaay@mkaay.de"), +                       ("zoidberg", "zoidberg@mujmail.cz")] -    FILE_NAME_PATTERN = r'id="file_title">(?P<N>.+)</' -    FILE_SIZE_PATTERN = r'size tip_container">(?P<S>[\d.]+) (?P<U>\w+)<' -    FILE_OFFLINE_PATTERN = r'Page not found!' -    def process(self, pyfile): -        # set lang to english -        self.req.cj.setCookie("uploading.com", "lang", "1") -        self.req.cj.setCookie("uploading.com", "language", "1") -        self.req.cj.setCookie("uploading.com", "setlang", "en") -        self.req.cj.setCookie("uploading.com", "_lang", "en") +    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.file_info = self.getFileInfo() +        self.getFileInfo()          if self.premium:              self.handlePremium()          else:              self.handleFree() +      def handlePremium(self):          postData = {'action': 'get_link', -                    'code': self.file_info['ID'], +                    'code': self.info['pattern']['ID'],                      'pass': 'undefined'}          self.html = self.load('http://uploading.com/files/get/?JsHttpRequest=%d-xml' % timestamp(), post=postData) @@ -66,46 +56,48 @@ class UploadingCom(SimpleHoster):              url = url.group(1).replace("\\/", "/")              self.download(url) -        raise Exception("Plugin defect.") +        raise Exception("Plugin defect") +      def handleFree(self): -        found = re.search('<h2>((Daily )?Download Limit)</h2>', self.html) -        if found: -            self.pyfile.error = found.group(1) +        m = re.search('<h2>((Daily )?Download Limit)</h2>', self.html) +        if m: +            self.pyfile.error = m.group(1)              self.logWarning(self.pyfile.error) -            self.retry(max_tries=6, wait_time=6 * 60 * 60 if found.group(2) else 15 * 60, reason=self.pyfile.error) +            self.retry(6, (6 * 60 if m.group(2) else 15) * 60, self.pyfile.error)          ajax_url = "http://uploading.com/files/get/?ajax"          self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With: XMLHttpRequest"])          self.req.http.lastURL = self.pyfile.url -        response = json_loads(self.load(ajax_url, post={'action': 'second_page', 'code': self.file_info['ID']})) -        if 'answer' in response and 'wait_time' in response['answer']: -            wait_time = int(response['answer']['wait_time']) -            self.logInfo("%s: Waiting %d seconds." % (self.__name__, wait_time)) +        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.pluginParseError("AJAX/WAIT") +            self.error(_("No AJAX/WAIT")) + +        res = json_loads(self.load(ajax_url, post={'action': 'get_link', 'code': self.info['pattern']['ID'], 'pass': 'false'})) -        response = json_loads( -            self.load(ajax_url, post={'action': 'get_link', 'code': self.file_info['ID'], 'pass': 'false'})) -        if 'answer' in response and 'link' in response['answer']: -            url = response['answer']['link'] +        if 'answer' in res and 'link' in res['answer']: +            url = res['answer']['link']          else: -            self.pluginParseError("AJAX/URL") +            self.error(_("No AJAX/URL"))          self.html = self.load(url) -        found = re.search(r'<form id="file_form" action="(.*?)"', self.html) -        if found: -            url = found.group(1) +        m = re.search(r'<form id="file_form" action="(.*?)"', self.html) +        if m: +            url = m.group(1)          else: -            self.pluginParseError("URL") +            self.error(_("No URL"))          self.download(url)          check = self.checkDownload({"html": re.compile("\A<!DOCTYPE html PUBLIC")})          if check == "html": -            self.logWarning("Redirected to a HTML page, wait 10 minutes and retry") +            self.logWarning(_("Redirected to a HTML page, wait 10 minutes and retry"))              self.wait(10 * 60, True) diff --git a/module/plugins/hoster/UploadkingCom.py b/module/plugins/hoster/UploadkingCom.py new file mode 100644 index 000000000..743e700eb --- /dev/null +++ b/module/plugins/hoster/UploadkingCom.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class UploadkingCom(DeadHoster): +    __name__    = "UploadkingCom" +    __type__    = "hoster" +    __version__ = "0.14" + +    __pattern__ = r'http://(?:www\.)?uploadking\.com/\w{10}' + +    __description__ = """UploadKing.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + + +getInfo = create_getInfo(UploadkingCom) diff --git a/module/plugins/hoster/UpstoreNet.py b/module/plugins/hoster/UpstoreNet.py index 9d1c4b3dd..25c424f1f 100644 --- a/module/plugins/hoster/UpstoreNet.py +++ b/module/plugins/hoster/UpstoreNet.py @@ -1,32 +1,38 @@  # -*- coding: utf-8 -*- +  import re -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  from module.plugins.internal.CaptchaService import ReCaptcha +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class UpstoreNet(SimpleHoster): -    __name__ = "UpstoreNet" -    __type__ = "hoster" -    __pattern__ = r"https?://(?:www\.)?upstore\.net/" -    __version__ = "0.02" +    __name__    = "UpstoreNet" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'https?://(?:www\.)?upstore\.net/' +      __description__ = """Upstore.Net File Download Hoster""" -    __author_name__ = ("igel") +    __license__     = "GPLv3" +    __authors__     = [("igel", "igelkun@myopera.com")] + -    FILE_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+)' -    FILE_OFFLINE_PATTERN = r'<span class="error">File not found</span>' +    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+)" +    WAIT_PATTERN = r'var sec = (\d+)'      CHASH_PATTERN = r'<input type="hidden" name="hash" value="([^"]*)">' -    DIRECT_LINK_PATTERN = r'<a href="(https?://.*?)" target="_blank"><b>' +    LINK_PATTERN = r'<a href="(https?://.*?)" target="_blank"><b>' +      def handleFree(self):          # STAGE 1: get link to continue          m = re.search(self.CHASH_PATTERN, self.html) -        if not m: -            self.parseError("could not detect hash") +        if m is None: +            self.error(_("CHASH_PATTERN not found"))          chash = m.group(1) -        self.logDebug("read hash " + chash) +        self.logDebug("Read hash " + chash)          # continue to stage2          post_data = {'hash': chash, 'free': 'Slow download'}          self.html = self.load(self.pyfile.url, post=post_data, decode=True) @@ -34,36 +40,33 @@ class UpstoreNet(SimpleHoster):          # STAGE 2: solv captcha and wait          # first get the infos we need: recaptcha key and wait time          recaptcha = ReCaptcha(self) -        if not recaptcha.detect_key(self.html): -            self.parseError("could not find recaptcha pattern") -        self.logDebug("using captcha key " + recaptcha.recaptcha_key) +          # try the captcha 5 times          for i in xrange(5):              m = re.search(self.WAIT_PATTERN, self.html) -            if not m: -                self.parseError("could not find wait pattern") -            wait_time = m.group(1) +            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 -            challenge, code = recaptcha.challenge() -            post_data['recaptcha_challenge_field'] = challenge -            post_data['recaptcha_response_field'] = code +            challenge, response = recaptcha.challenge() +            post_data.update({'recaptcha_challenge_field': challenge, +                              'recaptcha_response_field' : response})              self.html = self.load(self.pyfile.url, post=post_data, decode=True)              # STAGE 3: get direct link -            m = re.search(self.DIRECT_LINK_PATTERN, self.html, re.DOTALL) +            m = re.search(self.LINK_PATTERN, self.html, re.S)              if m:                  break -        if not m: -            self.parseError("could not detect direct link") +        if m is None: +            self.error(_("Download link not found"))          direct = m.group(1) -        self.logDebug('found direct link: ' + direct)          self.download(direct, disposition=True) diff --git a/module/plugins/hoster/UptoboxCom.py b/module/plugins/hoster/UptoboxCom.py index e403425c1..21d781f55 100644 --- a/module/plugins/hoster/UptoboxCom.py +++ b/module/plugins/hoster/UptoboxCom.py @@ -1,81 +1,34 @@  # -*- coding: utf-8 -*- -############################################################################### -#  This program is free software: you can redistribute it and/or modify -#  it under the terms of the GNU Affero General Public License as -#  published by the Free Software Foundation, either version 3 of the -#  License, or (at your option) any later version. -# -#  This program is distributed in the hope that it will be useful, -#  but WITHOUT ANY WARRANTY; without even the implied warranty of -#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -#  GNU Affero General Public License for more details. -# -#  You should have received a copy of the GNU Affero General Public License -#  along with this program.  If not, see <http://www.gnu.org/licenses/>. -# -#  @author: Walter Purcaro -############################################################################### -import re -from urllib import unquote +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo -from module.plugins.internal.CaptchaService import ReCaptcha, SolveMedia -from module.utils import html_unescape +class UptoboxCom(XFSHoster): +    __name__    = "UptoboxCom" +    __type__    = "hoster" +    __version__ = "0.16" + +    __pattern__ = r'https?://(?:www\.)?uptobox\.com/\w{12}' -class UptoboxCom(XFileSharingPro): -    __name__ = "UptoboxCom" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?uptobox\.com/\w+' -    __version__ = "0.09"      __description__ = """Uptobox.com hoster plugin""" -    __author_name__ = "Walter Purcaro" -    __author_mail__ = "vuolter@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = "uptobox.com" -    HOSTER_NAME = "uptobox.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)' -    FILE_INFO_PATTERN = r'"para_title">(?P<N>.+) \((?P<S>[\d\.]+) (?P<U>\w+)\)' -    FILE_OFFLINE_PATTERN = r'>(File not found|Access Denied|404 Not Found)' -    TEMP_OFFLINE_PATTERN = r'>This server is in maintenance mode' +    LINK_PATTERN = r'"(https?://\w+\.uptobox\.com/d/.*?)"' -    WAIT_PATTERN = r'>(\d+)</span> seconds<' +    ERROR_PATTERN = r'>(You have to wait.+till next download.)<'  #@TODO: Check XFSHoster ERROR_PATTERN -    DIRECT_LINK_PATTERN = r'"(https?://\w+\.uptobox\.com/d/.*?)"' -    def handleCaptcha(self, inputs): -        found = re.search(self.SOLVEMEDIA_PATTERN, self.html) -        if found: -            captcha_key = found.group(1) -            captcha = SolveMedia(self) -            inputs['adcopy_challenge'], inputs['adcopy_response'] = captcha.challenge(captcha_key) -            return 4 -        else: -            found = re.search(self.CAPTCHA_URL_PATTERN, self.html) -            if found: -                captcha_url = found.group(1) -                inputs['code'] = self.decryptCaptcha(captcha_url) -                return 2 -            else: -                found = re.search(self.CAPTCHA_DIV_PATTERN, self.html, re.DOTALL) -                if found: -                    captcha_div = found.group(1) -                    self.logDebug(captcha_div) -                    numerals = re.findall(r'<span.*?padding-left\s*:\s*(\d+).*?>(\d)</span>', -                                          html_unescape(captcha_div)) -                    inputs['code'] = "".join([a[1] for a in sorted(numerals, key=lambda num: int(num[0]))]) -                    self.logDebug("CAPTCHA", inputs['code'], numerals) -                    return 3 -                else: -                    found = re.search(self.RECAPTCHA_URL_PATTERN, self.html) -                    if found: -                        recaptcha_key = unquote(found.group(1)) -                        self.logDebug("RECAPTCHA KEY: %s" % recaptcha_key) -                        recaptcha = ReCaptcha(self) -                        inputs['recaptcha_challenge_field'], inputs['recaptcha_response_field'] = recaptcha.challenge( -                            recaptcha_key) -                        return 1 -        return 0 +    def setup(self): +        self.multiDL = True +        self.chunkLimit = 1 +        self.resumeDownload = True  getInfo = create_getInfo(UptoboxCom) diff --git a/module/plugins/hoster/VeehdCom.py b/module/plugins/hoster/VeehdCom.py index 22fc4b207..d894dab24 100644 --- a/module/plugins/hoster/VeehdCom.py +++ b/module/plugins/hoster/VeehdCom.py @@ -1,27 +1,29 @@  # -*- coding: utf-8 -*-  import re +  from module.plugins.Hoster import Hoster  class VeehdCom(Hoster): -    __name__ = 'VeehdCom' -    __type__ = '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", "_")] -    __version__ = '0.23' +      __description__ = """Veehd.com hoster plugin""" -    __author_name__ = "cat" -    __author_mail__ = "cat@pyload" +    __license__     = "GPLv3" +    __authors__     = [("cat", "cat@pyload")] -    def _debug(self, msg): -        self.logDebug('[%s] %s' % (self.__name__, msg))      def setup(self):          self.multiDL = True          self.req.canContinue = True +      def process(self, pyfile):          self.download_html()          if not self.file_exists(): @@ -30,48 +32,50 @@ class VeehdCom(Hoster):          pyfile.name = self.get_file_name()          self.download(self.get_file_url()) +      def download_html(self):          url = self.pyfile.url -        self._debug("Requesting page: %s" % (repr(url),)) +        self.logDebug("Requesting page: %s" % url)          self.html = self.load(url) +      def file_exists(self): -        if self.html is None: +        if not self.html:              self.download_html()          if '<title>Veehd</title>' in self.html:              return False          return True +      def get_file_name(self): -        if self.html is None: +        if not self.html:              self.download_html() -        match = re.search(r'<title[^>]*>([^<]+) on Veehd</title>', self.html) -        if not match: -            self.fail("video title not found") -        name = match.group(1) +        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 = '[^0-9A-Za-z\.\ ]+' +            pattern = '[^\w ]+'          else: -            pattern = '[^0-9A-Za-z\.]+' +            pattern = '[^\w.]+' + +        return re.sub(pattern, self.getConfig('replacement_char'), name) + '.avi' -        name = re.sub(pattern, self.getConfig('replacement_char'), -                      name) -        return name + '.avi'      def get_file_url(self):          """ returns the absolute downloadable filepath          """ -        if self.html is None: +        if not self.html:              self.download_html() -        match = re.search(r'<embed type="video/divx" src="(http://([^/]*\.)?veehd\.com/dl/[^"]+)"', +        m = re.search(r'<embed type="video/divx" src="(http://([^/]*\.)?veehd\.com/dl/[^"]+)"',                            self.html) -        if not match: -            self.fail("embedded video url not found") -        file_url = match.group(1) +        if m is None: +            self.error(_("Embedded video url not found")) -        return file_url +        return m.group(1) diff --git a/module/plugins/hoster/VeohCom.py b/module/plugins/hoster/VeohCom.py index c1ebffb81..6dbac397b 100644 --- a/module/plugins/hoster/VeohCom.py +++ b/module/plugins/hoster/VeohCom.py @@ -1,18 +1,4 @@  # -*- coding: utf-8 -*- -############################################################################ -#  This program is free software: you can redistribute it and/or modify -#  it under the terms of the GNU Affero General Public License as -#  published by the Free Software Foundation, either version 3 of the -#  License, or (at your option) any later version. -# -#  This program is distributed in the hope that it will be useful, -#  but WITHOUT ANY WARRANTY; without even the implied warranty of -#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -#  GNU Affero General Public License for more details. -# -#  You should have received a copy of the GNU Affero General Public License -#  along with this program.  If not, see <http://www.gnu.org/licenses/>. -############################################################################  import re @@ -20,37 +6,48 @@ from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class VeohCom(SimpleHoster): -    __name__ = "VeohCom" -    __type__ = "hoster" +    __name__    = "VeohCom" +    __type__    = "hoster" +    __version__ = "0.21" +      __pattern__ = r'http://(?:www\.)?veoh\.com/(tv/)?(watch|videos)/(?P<ID>v\w+)' -    __version__ = "0.1" -    __config__ = [("quality", "Low;High", "Quality", "High")] +    __config__ = [("quality", "Low;High;Auto", "Quality", "Auto")] +      __description__ = """Veoh.com hoster plugin""" -    __author_name__ = "Walter Purcaro" -    __author_mail__ = "vuolter@gmail.com" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + -    FILE_NAME_PATTERN = r'<meta name="title" content="(?P<N>.*?)"' -    FILE_OFFLINE_PATTERN = r'>Sorry, we couldn\'t find the video you were looking for' +    NAME_PATTERN    = r'<meta name="title" content="(?P<N>.*?)"' +    OFFLINE_PATTERN = r'>Sorry, we couldn\'t find the video you were looking for' -    FILE_URL_REPLACEMENTS = [(__pattern__, r'http://www.veoh.com/watch/\g<ID>')] +    URL_REPLACEMENTS = [(__pattern__ + ".*", r'http://www.veoh.com/watch/\g<ID>')] + +    COOKIES = [("veoh.com", "lassieLocale", "en")] -    SH_COOKIES = [(".veoh.com", "lassieLocale", "en")]      def setup(self): -        self.resumeDownload = self.multiDL = True -        self.chunkLimit = -1 +        self.resumeDownload = True +        self.multiDL        = True +        self.chunkLimit     = -1 +      def handleFree(self): -        q = self.getConfig("quality") -        pattern = r'"fullPreviewHash%sPath":"(.+?)"' % q -        found = re.search(pattern, self.html) -        if found: -            self.pyfile.name += ".mp4" -            link = found.group(1).replace("\\", "") -            self.logDebug("Download link: " + link) -            self.download(link) +        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: +                self.pyfile.name += ".mp4" +                link = m.group(1).replace("\\", "") +                self.download(link) +                return +            else: +                self.logInfo(_("No %s quality video found") % q.upper())          else: -            self.fail("No %s quality video found" % q.lower()) +            self.fail(_("No video found!"))  getInfo = create_getInfo(VeohCom) diff --git a/module/plugins/hoster/VidPlayNet.py b/module/plugins/hoster/VidPlayNet.py index ade416750..76af05edd 100644 --- a/module/plugins/hoster/VidPlayNet.py +++ b/module/plugins/hoster/VidPlayNet.py @@ -1,25 +1,26 @@  # -*- coding: utf-8 -*- - +#  # Test links:  # BigBuckBunny_320x180.mp4 - 61.7 Mb - http://vidplay.net/38lkev0h3jv0 -from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo + +class VidPlayNet(XFSHoster): +    __name__    = "VidPlayNet" +    __type__    = "hoster" +    __version__ = "0.04" -class VidPlayNet(XFileSharingPro): -    __name__ = "VidPlayNet" -    __type__ = "hoster"      __pattern__ = r'https?://(?:www\.)?vidplay\.net/\w{12}' -    __version__ = "0.01" +      __description__ = """VidPlay.net hoster plugin""" -    __author_name__ = "t4skforce" -    __author_mail__ = "t4skforce1337[AT]gmail[DOT]com" +    __license__     = "GPLv3" +    __authors__     = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")] + -    HOSTER_NAME = "vidplay.net" +    HOSTER_DOMAIN = "vidplay.net" -    FILE_OFFLINE_PATTERN = r'<b>File Not Found</b><br>\s*<br>' -    FILE_NAME_PATTERN = r'<b>Password:</b></div>\s*<h[1-6]>(?P<N>[^<]+)</h[1-6]>' -    DIRECT_LINK_PATTERN = r'(http://([^/]*?%s|\d+\.\d+\.\d+\.\d+)(:\d+)?(/d/|(?:/files)?/\d+/\w+/)[^"\'<&]+)' % HOSTER_NAME +    NAME_PATTERN = r'<b>Password:</b></div>\s*<h[1-6]>(?P<N>[^<]+)</h[1-6]>'  getInfo = create_getInfo(VidPlayNet) diff --git a/module/plugins/hoster/VimeoCom.py b/module/plugins/hoster/VimeoCom.py new file mode 100644 index 000000000..0e42c1674 --- /dev/null +++ b/module/plugins/hoster/VimeoCom.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- + +import re + +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo + + +class VimeoCom(SimpleHoster): +    __name__    = "VimeoCom" +    __type__    = "hoster" +    __version__ = "0.03" + +    __pattern__ = r'https?://(?:www\.)?(player\.)?vimeo\.com/(video/)?(?P<ID>\d+)' +    __config__ = [("quality", "Lowest;Mobile;SD;HD;Highest", "Quality", "Highest"), +                  ("original", "bool", "Try to download the original file first", 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): +        password = self.getPassword() + +        if self.js and 'class="btn iconify_down_b"' in self.html: +            html = self.js.eval(self.load(self.pyfile.url, get={'action': "download", 'password': password}, decode=True)) +            pattern = r'href="(?P<URL>http://vimeo\.com.+?)".*?\>(?P<QL>.+?) ' +        else: +            id = re.match(self.__pattern__, self.pyfile.url).group('ID') +            html = self.load("https://player.vimeo.com/video/" + 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.download(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.download(link[q]) +                return +            else: +                self.logInfo(_("No %s quality video found") % q.upper()) +        else: +            self.fail(_("No video found!")) + + +getInfo = create_getInfo(VimeoCom) diff --git a/module/plugins/hoster/Vipleech4uCom.py b/module/plugins/hoster/Vipleech4uCom.py index 53768a430..340a3feaa 100644 --- a/module/plugins/hoster/Vipleech4uCom.py +++ b/module/plugins/hoster/Vipleech4uCom.py @@ -1,84 +1,18 @@  # -*- coding: utf-8 -*- -import re +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo -from module.plugins.Hoster import Hoster +class Vipleech4uCom(DeadHoster): +    __name__    = "Vipleech4uCom" +    __type__    = "hoster" +    __version__ = "0.20" -class Vipleech4uCom(Hoster): -    __name__ = "Vipleech4uCom" -    __version__ = "0.1" -    __type__ = "hoster" -    __pattern__ = r"http://vipleech4u.com/manager.php" -    __description__ = """Vipleech4u.com hoster plugin""" -    __author_name__ = ("Kagenoshin") -    __author_mail__ = ("kagenoshin@gmx.ch") - -    FILENAME_PATTERN = re.compile(r'name\s*?=\s*?["\']filename["\'][^>]*?value\s*?=\s*?["\'](.*?)["\']', re.I) -    HOST_PATTERN = re.compile(r'name\s*?=\s*?["\']host["\'][^>]*?value\s*?=\s*?["\'](.*?)["\']', re.I) -    PATH_PATTERN = re.compile(r'name\s*?=\s*?["\']path["\'][^>]*?value\s*?=\s*?["\'](.*?)["\']', re.I) -    REFERER_PATTERN = re.compile(r'name\s*?=\s*?["\']referer["\'][^>]*?value\s*?=\s*?["\'](.*?)["\']', re.I) -    LINK_PATTERN = re.compile(r'name\s*?=\s*?["\']link["\'][^>]*?value\s*?=\s*?["\'](.*?)["\']', re.I) -    COOKIE_PATTERN = re.compile(r'name\s*?=\s*?["\']cookie["\'][^>]*?value\s*?=\s*?["\'](.*?)["\']', re.I) - -    def setup(self): -        self.resumeDownload = self.multiDL = True -        self.chunkLimit = 1 - -    def process(self, pyfile): -        if not self.account: -            self.logError(_("Please enter your %s account or deactivate this plugin") % "vipleech4u.com") -            self.fail("No vipleech4u.com account provided") - -        self.logDebug("Old URL: %s" % pyfile.url) - -        new_url = pyfile.url - -        if re.match(self.__pattern__, new_url): -            self.fail("Can't handle vipleech4u links.") - -        #upload the link which has to be loaded -        page = self.load('http://vipleech4u.com/generator.php', post={'links': new_url, 'ddl': 'no'}) +    __pattern__ = r'http://(?:www\.)?vipleech4u\.com/manager\.php' -        #switch to the manager and see what's happening -        page = self.load('http://vipleech4u.com/unrestrict.php', get={'link': new_url, 'premium_acc': 'on'}) - -        if re.search(r'You have generated maximum links available to you today', page, re.I): -            self.fail('Daily limit reached.') - -        filename = self.FILENAME_PATTERN.search(page) -        host = self.HOST_PATTERN.search(page) -        path = self.PATH_PATTERN.search(page) -        referer = self.REFERER_PATTERN.search(page) -        link = self.LINK_PATTERN.search(page) -        cookie = self.COOKIE_PATTERN.search(page) - -        #build the post-dictionary -        post_dict = {} - -        if filename: -            post_dict.update({'filename': filename.group(1)}) -        if host: -            post_dict.update({'host': host.group(1)}) -        if path: -            post_dict.update({'path': path.group(1)}) -        if referer: -            post_dict.update({'referer': referer.group(1)}) -        if link: -            post_dict.update({'link': link.group(1)}) -        if cookie: -            post_dict.update({'cookie': cookie.group(1)}) - -        if not post_dict: -            self.logDebug('Get an empty post_dict. Strange.') - -        self.setWait(5) -        self.wait() -        self.logDebug("Unrestricted URL: " + str(post_dict)) - -        self.download('http://vipleech4u.com/unrestrict.php', post=post_dict, disposition=True) +    __description__ = """Vipleech4u.com hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Kagenoshin", "kagenoshin@gmx.ch")] -        check = self.checkDownload({"bad": "<html"}) -        if check == "bad": -            self.retry(24, 150, 'Bad file downloaded') +getInfo = create_getInfo(Vipleech4uCom) diff --git a/module/plugins/hoster/WarserverCz.py b/module/plugins/hoster/WarserverCz.py index a80349b45..c83d5c03e 100644 --- a/module/plugins/hoster/WarserverCz.py +++ b/module/plugins/hoster/WarserverCz.py @@ -1,77 +1,18 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. +class WarserverCz(DeadHoster): +    __name__    = "WarserverCz" +    __type__    = "hoster" +    __version__ = "0.13" -    @author: zoidberg -""" +    __pattern__ = r'http://(?:www\.)?warserver\.cz/stahnout/\d+' -#similar to coolshare.cz (down) - -import re -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo -from module.network.HTTPRequest import BadHeader -from module.utils import html_unescape - - -class WarserverCz(SimpleHoster): -    __name__ = "WarserverCz" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?warserver.cz/stahnout/(?P<ID>\d+)/.+' -    __version__ = "0.12"      __description__ = """Warserver.cz hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" - -    FILE_NAME_PATTERN = r'<h1.*?>(?P<N>[^<]+)</h1>' -    FILE_SIZE_PATTERN = r'<li>Velikost: <strong>(?P<S>[^<]+)</strong>' -    FILE_OFFLINE_PATTERN = r'<h1>Soubor nenalezen</h1>' - -    PREMIUM_URL_PATTERN = r'href="(http://[^/]+/dwn-premium.php.*?)"' -    DOMAIN = "http://csd01.coolshare.cz" - -    DOMAIN = "http://s01.warserver.cz" - -    def handleFree(self): -        try: -            self.download("%s/dwn-free.php?fid=%s" % (self.DOMAIN, self.file_info['ID'])) -        except BadHeader, e: -            self.logError(e) -            if e.code == 403: -                self.longWait(60, 60) -            else: -                raise -        self.checkDownloadedFile() - -    def handlePremium(self): -        found = re.search(self.PREMIUM_URL_PATTERN, self.html) -        if not found: -            self.parseError("Premium URL") -        url = html_unescape(found.group(1)) -        self.logDebug("Premium URL: " + url) -        if not url.startswith("http://"): -            self.resetAccount() -        self.download(url) -        self.checkDownloadedFile() - -    def checkDownloadedFile(self): -        check = self.checkDownload({ -            "offline": ">404 Not Found<" -        }) - -        if check == "offline": -            self.offline() +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")]  getInfo = create_getInfo(WarserverCz) diff --git a/module/plugins/hoster/WebshareCz.py b/module/plugins/hoster/WebshareCz.py index 53dc3b95c..a08341ff3 100644 --- a/module/plugins/hoster/WebshareCz.py +++ b/module/plugins/hoster/WebshareCz.py @@ -1,71 +1,64 @@  # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify     # -# it under the terms of the GNU Affero General Public License as           # -# published by the Free Software Foundation, either version 3 of the       # -# License, or (at your option) any later version.                          # -#                                                                          # -# This program is distributed in the hope that it will be useful,          # -# but WITHOUT ANY WARRANTY; without even the implied warranty of           # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            # -# GNU Affero General Public License for more details.                      # -#                                                                          # -# You should have received a copy of the GNU Affero General Public License # -# along with this program.  If not, see <http://www.gnu.org/licenses/>.    # -############################################################################  import re -from module.plugins.internal.SimpleHoster import SimpleHoster -from module.network.RequestFactory import getRequest +from module.network.RequestFactory import getURL +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo -def getInfo(urls): -    h = getRequest() -    for url in urls: -        h.load(url) -        fid = re.search(WebshareCz.__pattern__, url).group('ID') -        api_data = h.load('https://webshare.cz/api/file_info/', post={'ident': fid}) -        if 'File not found' in api_data: -            file_info = (url, 0, 1, url) -        else: -            name = re.search('<name>(.+)</name>', api_data).group(1) -            size = re.search('<size>(.+)</size>', api_data).group(1) -            file_info = (name, size, 2, url) -        yield file_info +class WebshareCz(SimpleHoster): +    __name__    = "WebshareCz" +    __type__    = "hoster" +    __version__ = "0.14" +    __pattern__ = r'https?://(?:www\.)?webshare\.cz/(?:#/)?file/(?P<ID>\w+)' -class WebshareCz(SimpleHoster): -    __name__ = "WebshareCz" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:www\.)?webshare.cz/(?:#/)?file/(?P<ID>\w+)' -    __version__ = "0.13"      __description__ = """WebShare.cz hoster plugin""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it"), +                       ("rush", "radek.senfeld@gmail.com")] + + +    @classmethod +    def getInfo(cls, url="", html=""): +        info = super(WebshareCz, self).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): -        api_data = self.load('https://webshare.cz/api/file_link/', post={'ident': self.fid}) +        fid = re.match(self.__pattern__, self.pyfile.url).group('ID') +        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': fid, 'wst': wst}, +                          decode=True) +          self.logDebug("API data: " + api_data) +          m = re.search('<link>(.+)</link>', api_data) -        if not m: -            self.parseError('Unable to detect direct link') -        direct = m.group(1) -        self.logDebug("Direct link: " + direct) -        self.download(direct, disposition=True) +        if m is None: +            self.error(_("Unable to detect direct link")) -    def getFileInfo(self): -        self.logDebug("URL: %s" % self.pyfile.url) +        self.link = m.group(1) -        self.fid = re.match(self.__pattern__, self.pyfile.url).group('ID') -        self.load(self.pyfile.url) -        api_data = self.load('https://webshare.cz/api/file_info/', post={'ident': self.fid}) +    def handlePremium(self): +        return self.handleFree() -        if 'File not found' in api_data: -            self.offline() -        else: -            self.pyfile.name = re.search('<name>(.+)</name>', api_data).group(1) -            self.pyfile.size = re.search('<size>(.+)</size>', api_data).group(1) -        self.logDebug("FILE NAME: %s FILE SIZE: %s" % (self.pyfile.name, self.pyfile.size)) +getInfo = create_getInfo(WebshareCz) diff --git a/module/plugins/hoster/WrzucTo.py b/module/plugins/hoster/WrzucTo.py index 7966b980a..8e9653307 100644 --- a/module/plugins/hoster/WrzucTo.py +++ b/module/plugins/hoster/WrzucTo.py @@ -1,48 +1,38 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re +  from pycurl import HTTPHEADER  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class WrzucTo(SimpleHoster): -    __name__ = "WrzucTo" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?wrzuc\.to/([a-zA-Z0-9]+(\.wt|\.html)|(\w+/?linki/[a-zA-Z0-9]+))' -    __version__ = "0.01" +    __name__    = "WrzucTo" +    __type__    = "hoster" +    __version__ = "0.02" + +    __pattern__ = r'http://(?:www\.)?wrzuc\.to/(\w+(\.wt|\.html)|(\w+/?linki/\w+))' +      __description__ = """Wrzuc.to hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __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")] -    SH_COOKIES = [("http://www.wrzuc.to", "language", "en")] -    FILE_SIZE_PATTERN = r'class="info">\s*<tr>\s*<td>(?P<S>.*?)</td>' -    FILE_NAME_PATTERN = r'id="file_info">\s*<strong>(?P<N>.*?)</strong>'      def setup(self):          self.multiDL = True +      def handleFree(self):          data = dict(re.findall(r'(md5|file): "(.*?)"', self.html))          if len(data) != 2: -            self.parseError('File ID') +            self.error(_("No file ID"))          self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With: XMLHttpRequest"])          self.req.http.lastURL = self.pyfile.url @@ -53,10 +43,9 @@ class WrzucTo(SimpleHoster):          data.update(re.findall(r'"(download_link|server_id)":"(.*?)"', self.html))          if len(data) != 4: -            self.parseError('Download URL') +            self.error(_("No download URL"))          download_url = "http://%s.wrzuc.to/pobierz/%s" % (data['server_id'], data['download_link']) -        self.logDebug("Download URL: %s" % download_url)          self.download(download_url) diff --git a/module/plugins/hoster/WuploadCom.py b/module/plugins/hoster/WuploadCom.py index a0228081c..75ce59353 100644 --- a/module/plugins/hoster/WuploadCom.py +++ b/module/plugins/hoster/WuploadCom.py @@ -4,13 +4,16 @@ from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class WuploadCom(DeadHoster): -    __name__ = "WuploadCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?wupload\..*?/file/(([a-z][0-9]+/)?[0-9]+)(/.*)?' +    __name__    = "WuploadCom" +    __type__    = "hoster"      __version__ = "0.23" + +    __pattern__ = r'http://(?:www\.)?wupload\..+?/file/((\w+/)?\d+)(/.*)?' +      __description__ = """Wupload.com hoster plugin""" -    __author_name__ = ("jeix", "Paul King") -    __author_mail__ = ("jeix@hasnomail.de", "") +    __license__     = "GPLv3" +    __authors__     = [("jeix", "jeix@hasnomail.de"), +                       ("Paul King", None)]  getInfo = create_getInfo(WuploadCom) diff --git a/module/plugins/hoster/X7To.py b/module/plugins/hoster/X7To.py index 810ede911..a4e4b04bd 100644 --- a/module/plugins/hoster/X7To.py +++ b/module/plugins/hoster/X7To.py @@ -4,13 +4,15 @@ from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo  class X7To(DeadHoster): -    __name__ = "X7To" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?x7.to/' +    __name__    = "X7To" +    __type__    = "hoster"      __version__ = "0.41" + +    __pattern__ = r'http://(?:www\.)?x7\.to/' +      __description__ = """X7.to hoster plugin""" -    __author_name__ = "ernieb" -    __author_mail__ = "ernieb" +    __license__     = "GPLv3" +    __authors__     = [("ernieb", "ernieb")]  getInfo = create_getInfo(X7To) diff --git a/module/plugins/hoster/XFileSharingPro.py b/module/plugins/hoster/XFileSharingPro.py index f69283c47..0acad3dba 100644 --- a/module/plugins/hoster/XFileSharingPro.py +++ b/module/plugins/hoster/XFileSharingPro.py @@ -1,335 +1,57 @@  # -*- coding: utf-8 -*- -############################################################################### -#  This program is free software; you can redistribute it and/or modify -#  it under the terms of the GNU General Public License as published by -#  the Free Software Foundation; either version 3 of the License, -#  or (at your option) any later version. -# -#  This program is distributed in the hope that it will be useful, -#  but WITHOUT ANY WARRANTY; without even the implied warranty of -#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -#  See the GNU General Public License for more details. -# -#  You should have received a copy of the GNU General Public License -#  along with this program; if not, see <http://www.gnu.org/licenses/>. -# -#  @author: zoidberg -############################################################################### -  import re -from random import random -from urllib import unquote -from urlparse import urlparse -from pycurl import FOLLOWLOCATION, LOW_SPEED_TIME -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, PluginParseError, replace_patterns -from module.plugins.internal.CaptchaService import ReCaptcha, SolveMedia -from module.utils import html_unescape -from module.network.RequestFactory import getURL - - -class XFileSharingPro(SimpleHoster): -    """ -    Common base for XFileSharingPro hosters like EasybytezCom, CramitIn, FiledinoCom... -    Some hosters may work straight away when added to __pattern__ -    However, most of them will NOT work because they are either down or running a customized version -    """ -    __name__ = "XFileSharingPro" -    __type__ = "hoster" -    __pattern__ = r'^unmatchable$' -    __version__ = "0.31" -    __description__ = """XFileSharingPro base hoster plugin""" -    __author_name__ = ("zoidberg", "stickell") -    __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it") - -    FILE_NAME_PATTERN = r'<input type="hidden" name="fname" value="(?P<N>[^"]+)"' -    FILE_SIZE_PATTERN = r'You have requested .*\((?P<S>[\d\.\,]+) ?(?P<U>\w+)?\)</font>' -    FILE_INFO_PATTERN = r'<tr><td align=right><b>Filename:</b></td><td nowrap>(?P<N>[^<]+)</td></tr>\s*.*?<small>\((?P<S>[^<]+)\)</small>' -    FILE_OFFLINE_PATTERN = r'>\w+ (Not Found|file (was|has been) removed)' - -    WAIT_PATTERN = r'<span id="countdown_str">.*?>(\d+)</span>' -    #LONG_WAIT_PATTERN = r'(?P<H>\d+(?=\s*hour))?.*?(?P<M>\d+(?=\s*minute))?.*?(?P<S>\d+(?=\s*second))?' -    OVR_DOWNLOAD_LINK_PATTERN = r'<h2>Download Link</h2>\s*<textarea[^>]*>([^<]+)' -    OVR_KILL_LINK_PATTERN = r'<h2>Delete Link</h2>\s*<textarea[^>]*>([^<]+)' -    CAPTCHA_URL_PATTERN = r'(http://[^"\']+?/captchas?/[^"\']+)' -    RECAPTCHA_URL_PATTERN = r'http://[^"\']+?recaptcha[^"\']+?\?k=([^"\']+)"' -    CAPTCHA_DIV_PATTERN = r'>Enter code.*?<div.*?>(.*?)</div>' -    SOLVEMEDIA_PATTERN = r'http:\/\/api\.solvemedia\.com\/papi\/challenge\.script\?k=(.*?)"' -    ERROR_PATTERN = r'class=["\']err["\'][^>]*>(.*?)</' - -    def setup(self): -        if self.__name__ == "XFileSharingPro": -            self.__pattern__ = self.core.pluginManager.hosterPlugins[self.__name__]['pattern'] -            self.multiDL = True -        else: -            self.resumeDownload = self.multiDL = self.premium - -        self.chunkLimit = 1 - -    def process(self, pyfile): -        self.prepare() - -        pyfile.url = replace_patterns(pyfile.url, self.FILE_URL_REPLACEMENTS) - -        if not re.match(self.__pattern__, pyfile.url): -            if self.premium: -                self.handleOverriden() -            else: -                self.fail("Only premium users can download from other hosters with %s" % self.HOSTER_NAME) -        else: -            try: -                # Due to a 0.4.9 core bug self.load would use cookies even if -                # cookies=False. Workaround using getURL to avoid cookies. -                # Can be reverted in 0.5 as the cookies bug has been fixed. -                self.html = getURL(pyfile.url, decode=True) -                self.file_info = self.getFileInfo() -            except PluginParseError: -                self.file_info = None - -            self.location = self.getDirectDownloadLink() - -            if not self.file_info: -                pyfile.name = html_unescape(unquote(urlparse( -                    self.location if self.location else pyfile.url).path.split("/")[-1])) - -            if self.location: -                self.startDownload(self.location) -            elif self.premium: -                self.handlePremium() -            else: -                self.handleFree() - -    def prepare(self): -        """ Initialize important variables """ -        if not hasattr(self, "HOSTER_NAME"): -            self.HOSTER_NAME = re.match(self.__pattern__, self.pyfile.url).group(1) -        if not hasattr(self, "DIRECT_LINK_PATTERN"): -            self.DIRECT_LINK_PATTERN = r'(http://([^/]*?%s|\d+\.\d+\.\d+\.\d+)(:\d+)?(/d/|(?:/files)?/\d+/\w+/)[^"\'<]+)' % self.HOSTER_NAME - -        self.captcha = self.errmsg = None -        self.passwords = self.getPassword().splitlines() - -    def getDirectDownloadLink(self): -        """ Get download link for premium users with direct download enabled """ -        self.req.http.lastURL = self.pyfile.url - -        self.req.http.c.setopt(FOLLOWLOCATION, 0) -        self.html = self.load(self.pyfile.url, cookies=True, decode=True) -        self.header = self.req.http.header -        self.req.http.c.setopt(FOLLOWLOCATION, 1) - -        location = None -        found = re.search(r"Location\s*:\s*(.*)", self.header, re.I) -        if found and re.match(self.DIRECT_LINK_PATTERN, found.group(1)): -            location = found.group(1).strip() -        return location +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo -    def handleFree(self): -        url = self.getDownloadLink() -        self.logDebug("Download URL: %s" % url) -        self.startDownload(url) -    def getDownloadLink(self): -        for i in xrange(5): -            self.logDebug("Getting download link: #%d" % i) -            data = self.getPostParameters() +class XFileSharingPro(XFSHoster): +    __name__    = "XFileSharingPro" +    __type__    = "hoster" +    __version__ = "0.43" -            self.req.http.c.setopt(FOLLOWLOCATION, 0) -            self.html = self.load(self.pyfile.url, post=data, ref=True, decode=True) -            self.header = self.req.http.header -            self.req.http.c.setopt(FOLLOWLOCATION, 1) - -            found = re.search(r"Location\s*:\s*(.*)", self.header, re.I) -            if found: -                break - -            found = re.search(self.DIRECT_LINK_PATTERN, self.html, re.S) -            if found: -                break +    __pattern__ = r'^unmatchable$' -        else: -            if self.errmsg and 'captcha' in self.errmsg: -                self.fail("No valid captcha code entered") -            else: -                self.fail("Download link not found") +    __description__ = """XFileSharingPro dummy hoster plugin for hook""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] -        return found.group(1) -    def handlePremium(self): -        self.html = self.load(self.pyfile.url, post=self.getPostParameters()) -        found = re.search(self.DIRECT_LINK_PATTERN, self.html) -        if not found: -            self.parseError('DIRECT LINK') -        self.startDownload(found.group(1)) +    URL_REPLACEMENTS = [("/embed-", "/")] -    def handleOverriden(self): -        #only tested with easybytez.com -        self.html = self.load("http://www.%s/" % self.HOSTER_NAME) -        action, inputs = self.parseHtmlForm('') -        upload_id = "%012d" % int(random() * 10 ** 12) -        action += upload_id + "&js_on=1&utype=prem&upload_type=url" -        inputs['tos'] = '1' -        inputs['url_mass'] = self.pyfile.url -        inputs['up1oad_type'] = 'url' -        self.logDebug(self.HOSTER_NAME, action, inputs) -        #wait for file to upload to easybytez.com -        self.req.http.c.setopt(LOW_SPEED_TIME, 600) -        self.html = self.load(action, post=inputs) +    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()))) -        action, inputs = self.parseHtmlForm('F1') -        if not inputs: -            self.parseError('TEXTAREA') -        self.logDebug(self.HOSTER_NAME, inputs) -        if inputs['st'] == 'OK': -            self.html = self.load(action, post=inputs) -        elif inputs['st'] == 'Can not leech file': -            self.retry(max_tries=20, wait_time=3 * 60, reason=inputs['st']) -        else: -            self.fail(inputs['st']) -        #get easybytez.com link for uploaded file -        found = re.search(self.OVR_DOWNLOAD_LINK_PATTERN, self.html) -        if not found: -            self.parseError('DIRECT LINK (OVR)') -        self.pyfile.url = found.group(1) -        header = self.load(self.pyfile.url, just_header=True) -        if 'location' in header:  # Direct link -            self.startDownload(self.pyfile.url) -        else: -            self.retry() +    def init(self): +        super(XFileSharingPro, self).init() -    def startDownload(self, link): -        link = link.strip() -        if self.captcha: -            self.correctCaptcha() -        self.logDebug('DIRECT LINK: %s' % link) -        self.download(link, disposition=True) +        self.__pattern__ = self.core.pluginManager.hosterPlugins[self.__name__]['pattern'] -    def checkErrors(self): -        found = re.search(self.ERROR_PATTERN, self.html) -        if found: -            self.errmsg = found.group(1) -            self.logWarning(re.sub(r"<.*?>", " ", self.errmsg)) +        self.HOSTER_DOMAIN = re.match(self.__pattern__, self.pyfile.url).group(1).lower() +        self.HOSTER_NAME = "".join([str.capitalize() for str in self.HOSTER_DOMAIN.split('.')]) -            if 'wait' in self.errmsg: -                wait_time = sum([int(v) * {"hour": 3600, "minute": 60, "second": 1}[u] for v, u in -                                 re.findall(r'(\d+)\s*(hour|minute|second)?', self.errmsg)]) -                self.wait(wait_time, True) -            elif 'captcha' in self.errmsg: -                self.invalidCaptcha() -            elif 'premium' in self.errmsg and 'require' in self.errmsg: -                self.fail("File can be downloaded by premium users only") -            elif 'limit' in self.errmsg: -                self.wait(1 * 60 * 60, True) -                self.retry(25) -            elif 'countdown' in self.errmsg or 'Expired' in self.errmsg: -                self.retry() -            elif 'maintenance' in self.errmsg: -                self.tempOffline() -            elif 'download files up to' in self.errmsg: -                self.fail("File too large for free download") -            else: -                self.fail(self.errmsg) +        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: -            self.errmsg = None - -        return self.errmsg - -    def getPostParameters(self): -        for _ in xrange(3): -            if not self.errmsg: -                self.checkErrors() - -            if hasattr(self, "FORM_PATTERN"): -                action, inputs = self.parseHtmlForm(self.FORM_PATTERN) -            else: -                action, inputs = self.parseHtmlForm(input_names={"op": re.compile("^download")}) - -            if not inputs: -                action, inputs = self.parseHtmlForm('F1') -                if not inputs: -                    if self.errmsg: -                        self.retry() -                    else: -                        self.parseError("Form not found") - -            self.logDebug(self.HOSTER_NAME, inputs) +            return -            if 'op' in inputs and inputs['op'] in ('download2', 'download3'): -                if "password" in inputs: -                    if self.passwords: -                        inputs['password'] = self.passwords.pop(0) -                    else: -                        self.fail("No or invalid passport") +        self.user, data = self.account.selectAccount() +        self.req = self.account.getAccountRequest(self.user) +        self.premium = self.account.isPremium(self.user) -                if not self.premium: -                    found = re.search(self.WAIT_PATTERN, self.html) -                    if found: -                        wait_time = int(found.group(1)) + 1 -                        self.setWait(wait_time, False) -                    else: -                        wait_time = 0 -                    self.captcha = self.handleCaptcha(inputs) - -                    if wait_time: -                        self.wait() - -                self.errmsg = None -                return inputs - -            else: -                inputs['referer'] = self.pyfile.url - -                if self.premium: -                    inputs['method_premium'] = "Premium Download" -                    if 'method_free' in inputs: -                        del inputs['method_free'] -                else: -                    inputs['method_free'] = "Free Download" -                    if 'method_premium' in inputs: -                        del inputs['method_premium'] - -                self.html = self.load(self.pyfile.url, post=inputs, ref=True) -                self.errmsg = None - -        else: -            self.parseError('FORM: %s' % (inputs['op'] if 'op' in inputs else 'UNKNOWN')) - -    def handleCaptcha(self, inputs): -        found = re.search(self.RECAPTCHA_URL_PATTERN, self.html) -        if found: -            recaptcha_key = unquote(found.group(1)) -            self.logDebug("RECAPTCHA KEY: %s" % recaptcha_key) -            recaptcha = ReCaptcha(self) -            inputs['recaptcha_challenge_field'], inputs['recaptcha_response_field'] = recaptcha.challenge(recaptcha_key) -            return 1 -        else: -            found = re.search(self.CAPTCHA_URL_PATTERN, self.html) -            if found: -                captcha_url = found.group(1) -                inputs['code'] = self.decryptCaptcha(captcha_url) -                return 2 -            else: -                found = re.search(self.CAPTCHA_DIV_PATTERN, self.html, re.DOTALL) -                if found: -                    captcha_div = found.group(1) -                    self.logDebug(captcha_div) -                    numerals = re.findall(r'<span.*?padding-left\s*:\s*(\d+).*?>(\d)</span>', html_unescape(captcha_div)) -                    inputs['code'] = "".join([a[1] for a in sorted(numerals, key=lambda num: int(num[0]))]) -                    self.logDebug("CAPTCHA", inputs['code'], numerals) -                    return 3 -                else: -                    found = re.search(self.SOLVEMEDIA_PATTERN, self.html) -                    if found: -                        captcha_key = found.group(1) -                        captcha = SolveMedia(self) -                        inputs['adcopy_challenge'], inputs['adcopy_response'] = captcha.challenge(captcha_key) -                        return 4 -        return 0 +    def setup(self): +        self.chunkLimit = 1 +        self.resumeDownload = self.premium +        self.multiDL = True  getInfo = create_getInfo(XFileSharingPro) diff --git a/module/plugins/hoster/XHamsterCom.py b/module/plugins/hoster/XHamsterCom.py index 0f0371f21..c6e789fa8 100644 --- a/module/plugins/hoster/XHamsterCom.py +++ b/module/plugins/hoster/XHamsterCom.py @@ -1,10 +1,11 @@  # -*- coding: utf-8 -*-  import re +  from urllib import unquote -from module.plugins.Hoster import Hoster  from module.common.json_layer import json_loads +from module.plugins.Hoster import Hoster  def clean_json(json_expr): @@ -16,12 +17,17 @@ def clean_json(json_expr):  class XHamsterCom(Hoster): -    __name__ = "XHamsterCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?xhamster\.com/movies/.+' +    __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 @@ -35,81 +41,87 @@ class XHamsterCom(Hoster):          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 self.html is None: +        if not self.html:              self.download_html() -        flashvar_pattern = re.compile('flashvars = ({.*?});', re.DOTALL) +        flashvar_pattern = re.compile('flashvars = ({.*?});', re.S)          json_flashvar = flashvar_pattern.search(self.html) -        if json_flashvar is None: -            self.fail("Parse error (flashvars)") +        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"] + '/' +        if flashvars['srv']: +            srv_url = flashvars['srv'] + '/'          else: -            self.fail("Parse error (srv_url)") +            self.error(_("srv_url not found")) + +        if flashvars['url_mode']: +            url_mode = flashvars['url_mode'] + -        if flashvars["url_mode"]: -            url_mode = flashvars["url_mode"]          else: -            self.fail("Parse error (url_mode)") +            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.fail("Parse error (file_url)") +                self.error(_("file_url not found"))              file_url = file_url.group(1)              long_url = srv_url + file_url -            self.logDebug("long_url: %s" % long_url) +            self.logDebug("long_url = " + long_url)          else: -            if flashvars["file"]: -                file_url = unquote(flashvars["file"]) +            if flashvars['file']: +                file_url = unquote(flashvars['file'])              else: -                self.fail("Parse error (file_url)") +                self.error(_("file_url not found"))              if url_mode == '3':                  long_url = file_url -                self.logDebug("long_url: %s" % long_url) +                self.logDebug("long_url = " + long_url)              else:                  long_url = srv_url + "key=" + file_url -                self.logDebug("long_url: %s" % long_url) +                self.logDebug("long_url = " + long_url)          return long_url +      def get_file_name(self): -        if self.html is None: +        if not self.html:              self.download_html() -        file_name_pattern = r"<title>(.*?) - xHamster\.com</title>" -        file_name = re.search(file_name_pattern, self.html) -        if file_name is None: -            file_name_pattern = r"<h1 >(.*)</h1>" -            file_name = re.search(file_name_pattern, self.html) -            if file_name is None: -                file_name_pattern = r"http://[www.]+xhamster\.com/movies/.*/(.*?)\.html?" -                file_name = re.match(file_name_pattern, self.pyfile.url) -                if file_name is None: -                    file_name_pattern = r"<div id=\"element_str_id\" style=\"display:none;\">(.*)</div>" -                    file_name = re.search(file_name_pattern, self.html) -                    if file_name is None: +        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 file_name.group(1) +        return name.group(1) +      def file_exists(self):          """ returns True or False          """ -        if self.html is None: +        if not self.html:              self.download_html()          if re.search(r"(.*Video not found.*)", self.html) is not None:              return False diff --git a/module/plugins/hoster/XVideosCom.py b/module/plugins/hoster/XVideosCom.py index b2fa5243f..9bb2da424 100644 --- a/module/plugins/hoster/XVideosCom.py +++ b/module/plugins/hoster/XVideosCom.py @@ -1,18 +1,23 @@  # -*- coding: utf-8 -*-  import re -import urllib + +from urllib import unquote  from module.plugins.Hoster import Hoster  class XVideosCom(Hoster): -    __name__ = "XVideos.com" -    __version__ = "0.1" -    __pattern__ = r'http://(?:www\.)?xvideos\.com/video([0-9]+)/.*' +    __name__    = "XVideos.com" +    __type__    = "hoster" +    __version__ = "0.10" + +    __pattern__ = r'http://(?:www\.)?xvideos\.com/video(\d+)' +      __description__ = """XVideos.com hoster plugin""" -    __author_name__ = "" -    __author_mail__ = "" +    __license__     = "GPLv3" +    __authors__     = [] +      def process(self, pyfile):          site = self.load(pyfile.url) @@ -20,4 +25,4 @@ class XVideosCom(Hoster):              re.search(r"<h2>([^<]+)<span", site).group(1),              re.match(self.__pattern__, pyfile.url).group(1),          ) -        self.download(urllib.unquote(re.search(r"flv_url=([^&]+)&", site).group(1))) +        self.download(unquote(re.search(r"flv_url=([^&]+)&", site).group(1))) diff --git a/module/plugins/hoster/Xdcc.py b/module/plugins/hoster/Xdcc.py index 53f61836b..ef6da4a71 100644 --- a/module/plugins/hoster/Xdcc.py +++ b/module/plugins/hoster/Xdcc.py @@ -1,62 +1,48 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: jeix -""" - -from os.path import join -from os.path import exists -from os import makedirs  import re -import sys -import time  import socket  import struct +import sys +import time + +from os import makedirs +from os.path import exists, join  from select import select -from module.utils import save_join  from module.plugins.Hoster import Hoster +from module.utils import save_join  class Xdcc(Hoster): -    __name__ = "Xdcc" +    __name__    = "Xdcc" +    __type__    = "hoster"      __version__ = "0.32" -    __pattern__ = r'xdcc://([^/]*?)(/#?.*?)?/.*?/#?\d+/?'  # xdcc://irc.Abjects.net/#channel/[XDCC]|Shit/#0004/ -    __type__ = "hoster" +      __config__ = [("nick", "str", "Nickname", "pyload"),                    ("ident", "str", "Ident", "pyloadident"),                    ("realname", "str", "Realname", "pyloadreal")] +      __description__ = """Download from IRC XDCC bot""" -    __author_name__ = "jeix" -    __author_mail__ = "jeix@hasnomail.com" +    __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 _ in xrange(0, 3): +        for _i in xrange(0, 3):              try:                  nmn = self.doDownload(pyfile.url) -                self.logDebug("%s: Download of %s finished." % (self.__name__, nmn)) +                self.logDebug("Download of %s finished." % nmn)                  return              except socket.error, e:                  if hasattr(e, "errno"): @@ -64,24 +50,20 @@ class Xdcc(Hoster):                  else:                      errno = e.args[0] -                if errno in (10054,): -                    self.logDebug("XDCC: Server blocked our ip, retry in 5 min") +                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(_("Failed due to socket errors. Code: %d") % errno) + +        self.fail(_("Server blocked our ip, retry again later manually")) -        self.fail("Server blocked our ip, retry again later manually")      def doDownload(self, url):          self.pyfile.setStatus("waiting")  # real link -        download_folder = self.config['general']['download_folder'] -        location = join(download_folder, self.pyfile.package().folder.decode(sys.getfilesystemencoding())) -        if not exists(location): -            makedirs(location) -          m = re.match(r'xdcc://(.*?)/#?(.*?)/(.*?)/#?(\d+)/?', url)          server = m.group(1)          chan = m.group(2) @@ -98,7 +80,7 @@ class Xdcc(Hoster):          elif ln == 1:              host, port = temp[0], 6667          else: -            self.fail("Invalid hostname for IRC Server (%s)" % server) +            self.fail(_("Invalid hostname for IRC Server: %s") % server)          #######################          # CONNECT TO IRC AND IDLE FOR REAL LINK @@ -135,7 +117,7 @@ class Xdcc(Hoster):                  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") +                    self.fail(_("XDCC Bot did not answer"))              fdset = select([sock], [], [], 0)              if sock not in fdset[0]: @@ -155,7 +137,7 @@ class Xdcc(Hoster):                      sock.send("PONG %s\r\n" % first[1])                  if first[0] == "ERROR": -                    self.fail("IRC-Error: %s" % line) +                    self.fail(_("IRC-Error: %s") % line)                  msg = line.split(None, 3)                  if len(msg) != 4: @@ -168,31 +150,31 @@ class Xdcc(Hoster):                      "text": msg[3][1:]                  } -                if nick == msg["target"][0:len(nick)] and "PRIVMSG" == msg["action"]: -                    if msg["text"] == "\x01VERSION\x01": -                        self.logDebug("XDCC: Sending CTCP VERSION.") +                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.") +                    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": +                    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")): +                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"]) +                    print "%s: %s" % (msg['origin'], msg['text']) -                if "You already requested that pack" in 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") +                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"]) +                m = re.match('\x01DCC SEND (.*?) (\d+) (\d+)(?: (\d+))?\x01', msg['text'])                  if m:                      done = True @@ -205,13 +187,16 @@ class Xdcc(Hoster):              self.req.filesize = int(m.group(4))          self.pyfile.name = packname -        filename = save_join(location, packname) -        self.logInfo("XDCC: Downloading %s from %s:%d" % (packname, ip, port)) + +        download_folder = self.config['general']['download_folder'] +        filename = save_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}) +            self.logInfo(_("%(name)s saved as %(newname)s") % {"name": self.pyfile.name, "newname": newname})              filename = newname          # kill IRC socket diff --git a/module/plugins/hoster/YibaishiwuCom.py b/module/plugins/hoster/YibaishiwuCom.py index e51937924..3b4692933 100644 --- a/module/plugins/hoster/YibaishiwuCom.py +++ b/module/plugins/hoster/YibaishiwuCom.py @@ -1,60 +1,55 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -  import re -from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo +  from module.common.json_layer import json_loads +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class YibaishiwuCom(SimpleHoster): -    __name__ = "YibaishiwuCom" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?(?:u\.)?115.com/file/(?P<ID>\w+)' -    __version__ = "0.12" +    __name__    = "YibaishiwuCom" +    __type__    = "hoster" +    __version__ = "0.13" + +    __pattern__ = r'http://(?:www\.)?(?:u\.)?115\.com/file/(?P<ID>\w+)' +      __description__ = """115.com hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] + -    FILE_NAME_PATTERN = r"file_name: '(?P<N>[^']+)'" -    FILE_SIZE_PATTERN = r"file_size: '(?P<S>[^']+)'" -    FILE_OFFLINE_PATTERN = ur'<h3><i style="color:red;">ååïŒæåç äžååšïŒäžåŠšææçå§ïŒ</i></h3>' +    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_PATTERN = r'(/\?ct=(pickcode|download)[^"\']+)' -    AJAX_URL_PATTERN = r'(/\?ct=(pickcode|download)[^"\']+)'      def handleFree(self): -        found = re.search(self.AJAX_URL_PATTERN, self.html) -        if not found: -            self.parseError("AJAX URL") -        url = found.group(1) -        self.logDebug(('FREEUSER' if found.group(2) == 'download' else 'GUEST') + ' URL', url) - -        response = json_loads(self.load("http://115.com" + url, decode=False)) -        for mirror in (response['urls'] if 'urls' in response else response['data'] if 'data' in response else []): +        m = re.search(self.LINK_PATTERN, self.html) +        if m is None: +            self.error(_("LINK_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: -                url = mirror['url'].replace('\\', '') +                url = mr['url'].replace("\\", "")                  self.logDebug("Trying URL: " + url)                  self.download(url)                  break              except:                  continue          else: -            self.fail('No working link found') +            self.fail(_("No working link found"))  getInfo = create_getInfo(YibaishiwuCom) diff --git a/module/plugins/hoster/YoupornCom.py b/module/plugins/hoster/YoupornCom.py index 9dc1dc6e9..4bb2520e6 100644 --- a/module/plugins/hoster/YoupornCom.py +++ b/module/plugins/hoster/YoupornCom.py @@ -1,17 +1,21 @@  # -*- coding: utf-8 -*-  import re +  from module.plugins.Hoster import Hoster  class YoupornCom(Hoster): -    __name__ = "YoupornCom" -    __type__ = "hoster" +    __name__    = "YoupornCom" +    __type__    = "hoster" +    __version__ = "0.20" +      __pattern__ = r'http://(?:www\.)?youporn\.com/watch/.+' -    __version__ = "0.2" +      __description__ = """Youporn.com hoster plugin""" -    __author_name__ = "willnix" -    __author_mail__ = "willnix@pyload.org" +    __license__     = "GPLv3" +    __authors__     = [("willnix", "willnix@pyload.org")] +      def process(self, pyfile):          self.pyfile = pyfile @@ -22,30 +26,33 @@ class YoupornCom(Hoster):          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 self.html is None: +        if not self.html:              self.download_html() -        file_url = re.search(r'(http://download\.youporn\.com/download/\d+\?save=1)">', self.html).group(1) -        return file_url +        return re.search(r'(http://download\.youporn\.com/download/\d+\?save=1)">', self.html).group(1) +      def get_file_name(self): -        if self.html is None: +        if not self.html:              self.download_html() -        file_name_pattern = r"<title>(.*) - Free Porn Videos - YouPorn</title>" +        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 self.html is None: +        if not self.html:              self.download_html()          if re.search(r"(.*invalid video_id.*)", self.html) is not None:              return False diff --git a/module/plugins/hoster/YourfilesTo.py b/module/plugins/hoster/YourfilesTo.py index c8c5c523c..83ab89110 100644 --- a/module/plugins/hoster/YourfilesTo.py +++ b/module/plugins/hoster/YourfilesTo.py @@ -1,24 +1,31 @@  # -*- coding: utf-8 -*-  import re -import urllib + +from urllib import unquote +  from module.plugins.Hoster import Hoster  class YourfilesTo(Hoster): -    __name__ = "YourfilesTo" -    __type__ = "hoster" -    __pattern__ = r'(http://)?(?:www\.)?yourfiles\.(to|biz)/\?d=[a-zA-Z0-9]+' +    __name__    = "YourfilesTo" +    __type__    = "hoster"      __version__ = "0.21" + +    __pattern__ = r'(http://)?(?:www\.)?yourfiles\.(to|biz)/\?d=\w+' +      __description__ = """Youfiles.to hoster plugin""" -    __author_name__ = ("jeix", "skydancer") -    __author_mail__ = ("jeix@hasnomail.de", "skydancer@hasnomail.de") +    __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() @@ -27,11 +34,11 @@ class YourfilesTo(Hoster):          wait_time = self.get_waiting_time()          self.setWait(wait_time) -        self.logDebug("%s: Waiting %d seconds." % (self.__name__, wait_time))          self.wait() +      def get_waiting_time(self): -        if self.html is None: +        if not self.html:              self.download_html()          #var zzipitime = 15; @@ -43,31 +50,35 @@ class YourfilesTo(Hoster):          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 = urllib.unquote(url.replace("http://http:/http://", "http://").replace("dumdidum", "")) +            url = unquote(url.replace("http://http:/http://", "http://").replace("dumdidum", ""))              return url          else: -            self.fail("absolute filepath could not be found. offline? ") +            self.error(_("Absolute filepath not found")) +      def get_file_name(self): -        if self.html is None: +        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 self.html is None: +        if not self.html:              self.download_html()          if re.search(r"HTTP Status 404", self.html) is not None: diff --git a/module/plugins/hoster/YoutubeCom.py b/module/plugins/hoster/YoutubeCom.py index 330aae190..90c6df3c2 100644 --- a/module/plugins/hoster/YoutubeCom.py +++ b/module/plugins/hoster/YoutubeCom.py @@ -1,12 +1,14 @@  # -*- coding: utf-8 -*- +import os  import re  import subprocess -import os +  from urllib import unquote -from module.utils import html_unescape  from module.plugins.Hoster import Hoster +from module.plugins.internal.SimpleHoster import replace_patterns +from module.utils import html_unescape  def which(program): @@ -18,11 +20,12 @@ def which(program):          return os.path.isfile(fpath) and os.access(fpath, os.X_OK)      fpath, fname = os.path.split(program) +      if fpath:          if is_exe(program):              return program      else: -        for path in os.environ["PATH"].split(os.pathsep): +        for path in os.environ['PATH'].split(os.pathsep):              path = path.strip('"')              exe_file = os.path.join(path, program)              if is_exe(exe_file): @@ -32,10 +35,11 @@ def which(program):  class YoutubeCom(Hoster): -    __name__ = "YoutubeCom" -    __type__ = "hoster" -    __pattern__ = r'https?://(?:[^/]*\.)?youtube\.com/watch.*?[?&]v=.*' -    __version__ = "0.39" +    __name__    = "YoutubeCom" +    __type__    = "hoster" +    __version__ = "0.40" + +    __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 (5-102, 0 for auto)", 0),                    (".mp4", "bool", "Allow .mp4", True), @@ -43,37 +47,49 @@ class YoutubeCom(Hoster):                    (".webm", "bool", "Allow .webm", False),                    (".3gp", "bool", "Allow .3gp", False),                    ("3d", "bool", "Prefer 3D", False)] +      __description__ = """Youtube.com hoster plugin""" -    __author_name__ = ("spoob", "zoidberg") -    __author_mail__ = ("spoob@pyload.org", "zoidberg@mujmail.cz") +    __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)} +    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 = self.multiDL = True +        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): @@ -92,7 +108,7 @@ class YoutubeCom(Hoster):                         "480p": 35, "720p": 22, "1080p": 37, "3072p": 38}          desired_fmt = self.getConfig("fmt")          if desired_fmt and desired_fmt not in self.formats: -            self.logWarning("FMT %d unknown - using default." % desired_fmt) +            self.logWarning(_("FMT %d unknown, using default") % desired_fmt)              desired_fmt = 0          if not desired_fmt:              desired_fmt = quality.get(self.getConfig("quality"), 18) @@ -109,7 +125,7 @@ class YoutubeCom(Hoster):          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") +            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" % @@ -138,6 +154,8 @@ class YoutubeCom(Hoster):          # 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) @@ -145,7 +163,7 @@ class YoutubeCom(Hoster):          ffmpeg = which("ffmpeg")          if ffmpeg and time:              m, s = time.groups()[1:] -            if not m: +            if m is None:                  m = "0"              pyfile.name += " (starting at %s:%s)" % (m, s) diff --git a/module/plugins/hoster/ZDF.py b/module/plugins/hoster/ZDF.py index e718f283e..8d3de5b26 100644 --- a/module/plugins/hoster/ZDF.py +++ b/module/plugins/hoster/ZDF.py @@ -1,22 +1,26 @@  # -*- coding: utf-8 -*-  import re +  from xml.etree.ElementTree import fromstring  from module.plugins.Hoster import Hoster -XML_API = "http://www.zdf.de/ZDFmediathek/xmlservice/web/beitragsDetails?id=%i" - +# Based on zdfm by Roland Beermann (http://github.com/enkore/zdfm/)  class ZDF(Hoster): -    # Based on zdfm by Roland Beermann -    # http://github.com/enkore/zdfm/ -    __name__ = "ZDF Mediathek" -    __version__ = "0.8" -    __pattern__ = r'http://(?:www\.)?zdf\.de/ZDFmediathek/[^0-9]*([0-9]+)[^0-9]*' +    __name__    = "ZDF Mediathek" +    __type__    = "hoster" +    __version__ = "0.80" + +    __pattern__ = r'http://(?:www\.)?zdf\.de/ZDFmediathek/\D*(\d+)\D*' +      __description__ = """ZDF.de hoster plugin""" -    __author_name__ = "" -    __author_mail__ = "" +    __license__     = "GPLv3" +    __authors__     = [] + +    XML_API = "http://www.zdf.de/ZDFmediathek/xmlservice/web/beitragsDetails?id=%i" +      @staticmethod      def video_key(video): @@ -25,21 +29,24 @@ class ZDF(Hoster):              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"[^0-9]*([0-9]{4,})[^0-9]*", url).group(1)) +        return int(re.search(r"\D*(\d{4,})\D*", url).group(1)) +      def process(self, pyfile): -        xml = fromstring(self.load(XML_API % self.get_id(pyfile.url))) +        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.") +            self.fail(_("Error retrieving manifest"))          video = xml.find("video")          title = video.findtext("information/title") diff --git a/module/plugins/hoster/ZShareNet.py b/module/plugins/hoster/ZShareNet.py new file mode 100644 index 000000000..dc96facbe --- /dev/null +++ b/module/plugins/hoster/ZShareNet.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo + + +class ZShareNet(DeadHoster): +    __name__    = "ZShareNet" +    __type__    = "hoster" +    __version__ = "0.21" + +    __pattern__ = r'https?://(?:ww[2w]\.)?zshares?\.net/.+' + +    __description__ = """ZShare.net hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("espes", None), +                       ("Cptn Sandwich", None)] + + +getInfo = create_getInfo(ZShareNet) diff --git a/module/plugins/hoster/ZeveraCom.py b/module/plugins/hoster/ZeveraCom.py index d3d67bedc..40ef5de0f 100644 --- a/module/plugins/hoster/ZeveraCom.py +++ b/module/plugins/hoster/ZeveraCom.py @@ -1,105 +1,42 @@  # -*- coding: utf-8 -*- -from module.plugins.Hoster import Hoster +from module.plugins.internal.MultiHoster import MultiHoster, create_getInfo -class ZeveraCom(Hoster): -    __name__ = "ZeveraCom" -    __version__ = "0.21" -    __type__ = "hoster" -    __pattern__ = r'http://(?:www\.)?zevera.com/.*' +class ZeveraCom(MultiHoster): +    __name__    = "ZeveraCom" +    __type__    = "hoster" +    __version__ = "0.25" + +    __pattern__ = r'http://(?:www\.)?zevera\.com/.+' +      __description__ = """Zevera.com hoster plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz")] -    def setup(self): -        self.resumeDownload = self.multiDL = True -        self.chunkLimit = 1 -    def process(self, pyfile): -        if not self.account: -            self.logError(_("Please enter your %s account or deactivate this plugin") % "zevera.com") -            self.fail("No zevera.com account provided") +    def setup(self): +        self.resumeDownload = True +        self.multiDL        = True +        self.chunkLimit     = 1 -        self.logDebug("zevera.com: Old URL: %s" % pyfile.url) -        if self.account.getAPIData(self.req, cmd="checklink", olink=pyfile.url) != "Alive": -            self.fail("Offline or not downloadable - contact Zevera support") +    def handlePremium(self): +        if self.account.getAPIData(self.req, cmd="checklink", olink=self.pyfile.url) != "Alive": +            self.fail(_("Offline or not downloadable")) -        header = self.account.getAPIData(self.req, just_header=True, cmd="generatedownloaddirect", olink=pyfile.url) +        header = self.account.getAPIData(self.req, just_header=True, cmd="generatedownloaddirect", olink=self.pyfile.url)          if not "location" in header: -            self.fail("Unable to initialize download - contact Zevera support") - -        self.download(header['location'], disposition=True) - -        check = self.checkDownload({"error": 'action="ErrorDownload.aspx'}) -        if check == "error": -            self.fail("Error response received - contact Zevera support") - -    # BitAPI not used - defunct, probably abandoned by Zevera -    # -    # api_url = "http://zevera.com/API.ashx" -    # -    # def process(self, pyfile): -    #     if not self.account: -    #         self.logError(_("Please enter your zevera.com account or deactivate this plugin")) -    #         self.fail("No zevera.com account provided") -    # -    #     self.logDebug("zevera.com: Old URL: %s" % pyfile.url) -    # -    #     last_size = retries = 0 -    #     olink = pyfile.url #quote(pyfile.url.encode('utf_8')) -    # -    #     for _ in xrange(100): -    #         self.retData = self.account.loadAPIRequest(self.req, cmd = 'download_request', olink = olink) -    #         self.checkAPIErrors(self.retData) -    # -    #         if self.retData['FileInfo']['StatusID'] == 100: -    #             break -    #         elif self.retData['FileInfo']['StatusID'] == 99: -    #             self.fail('Failed to initialize download (99)') -    #         else: -    #             if self.retData['FileInfo']['Progress']['BytesReceived'] <= last_size: -    #                 if retries >= 6: -    #                     self.fail('Failed to initialize download (%d)' % self.retData['FileInfo']['StatusID'] ) -    #                 retries += 1 -    #             else: -    #                 retries = 0 -    # -    #             last_size = self.retData['FileInfo']['Progress']['BytesReceived'] -    # -    #             self.setWait(self.retData['Update_Wait']) -    #             self.wait() -    # -    #     pyfile.name = self.retData['FileInfo']['RealFileName'] -    #     pyfile.size = self.retData['FileInfo']['FileSizeInBytes'] -    # -    #     self.retData = self.account.loadAPIRequest(self.req, cmd = 'download_start', -    #                                                FileID = self.retData['FileInfo']['FileID']) -    #     self.checkAPIErrors(self.retData) -    # -    #     self.download(self.api_url, get = { -    #         'cmd': "open_stream", -    #         'login': self.account.loginname, -    #         'pass': self.account.password, -    #         'FileID': self.retData['FileInfo']['FileID'], -    #         'startBytes': 0 -    #         } -    #     ) -    # -    # def checkAPIErrors(self, retData): -    #     if not retData: -    #         self.fail('Unknown API response') -    # -    #     if retData['ErrorCode']: -    #         self.logError(retData['ErrorCode'], retData['ErrorMessage']) -    #         #self.fail('ERROR: ' + retData['ErrorMessage']) -    # -    #     if pyfile.size / 1024000 > retData['AccountInfo']['AvailableTODAYTrafficForUseInMBytes']: -    #         self.logWarning("Not enough data left to download the file") -    # -    # def crazyDecode(self, ustring): -    #     # accepts decoded ie. unicode string - API response is double-quoted, double-utf8-encoded -    #     # no idea what the proper order of calling these functions would be :-/ -    #     return html_unescape(unquote(unquote(ustring.replace( -    #                          '@DELIMITER@','#'))).encode('raw_unicode_escape').decode('utf-8')) +            self.fail(_("Unable to initialize download")) + +        self.link = header['location'] + + +    def checkFile(self): +        super(ZeveraCom, self).checkFile() + +        if self.checkDownload({"error": 'action="ErrorDownload.aspx'}) is "error": +            self.fail(_("Error response received - contact Zevera support")) + + +getInfo = create_getInfo(ZeveraCom) diff --git a/module/plugins/hoster/ZippyshareCom.py b/module/plugins/hoster/ZippyshareCom.py index b4fa4f8ac..73dd1f22d 100644 --- a/module/plugins/hoster/ZippyshareCom.py +++ b/module/plugins/hoster/ZippyshareCom.py @@ -1,71 +1,66 @@  # -*- coding: utf-8 -*- -# Test links (random.bin): -# http://www13.zippyshare.com/v/18665333/file.html -  import re +from urlparse import urljoin +  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo  class ZippyshareCom(SimpleHoster): -    __name__ = "ZippyshareCom" -    __type__ = "hoster" -    __pattern__ = r'(?P<HOST>http://www\d{0,2}\.zippyshare.com)/v(?:/|iew.jsp.*key=)(?P<KEY>\d+)' -    __version__ = "0.49" +    __name__    = "ZippyshareCom" +    __type__    = "hoster" +    __version__ = "0.65" + +    __pattern__ = r'(?P<HOST>http://www\d{0,2}\.zippyshare\.com)/v(?:/|iew\.jsp.*key=)(?P<KEY>\d+)' +      __description__ = """Zippyshare.com hoster plugin""" -    __author_name__ = ("spoob", "zoidberg", "stickell", "skylab") -    __author_mail__ = ("spoob@pyload.org", "zoidberg@mujmail.cz", "l.stickell@yahoo.it", "development@sky-lab.de") +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    NAME_PATTERN = r'("\d{6,}/"[ ]*\+.+?"/|<title>Zippyshare.com - )(?P<N>.+?)("|</title>)' +    SIZE_PATTERN = r'>Size:.+?">(?P<S>[\d.,]+) (?P<U>[\w^_]+)' -    FILE_NAME_PATTERN = r'<title>Zippyshare\.com - (?P<N>[^<]+)</title>' -    FILE_SIZE_PATTERN = r'>Size:</font>\s*<font [^>]*>(?P<S>[0-9.,]+) (?P<U>[kKMG]+)i?B</font><br />' -    FILE_INFO_PATTERN = r'document\.getElementById\(\'dlbutton\'\)\.href = "[^;]*/(?P<N>[^"]+)";' -    FILE_OFFLINE_PATTERN = r'>File does not exist on this server</div>' +    OFFLINE_PATTERN = r'>File does not exist on this server<' + +    COOKIES = [("zippyshare.com", "ziplocale", "en")] -    SH_COOKIES = [('zippyshare.com', 'ziplocale', 'en')]      def setup(self): -        self.multiDL = True +        self.multiDL        = True +        self.chunkLimit     = -1 +        self.resumeDownload = True +      def handleFree(self): -        url = self.get_file_url() -        if not url: -            self.fail("Download URL not found.") -        self.logDebug("Download URL: %s" % url) +        url = self.get_link()          self.download(url) -    def get_file_url(self): -        """returns the absolute downloadable filepath""" -        url_parts = re.search(r'(addthis:url="(http://www(\d+).zippyshare.com/v/(\d*)/file.html))', self.html) -        number = url_parts.group(4) -        check = re.search(r'<script type="text/javascript">([^<]*?)(var a = (\d*);)', self.html) -        if check: -            a = int(re.search(r'<script type="text/javascript">([^<]*?)(var a = (\d*);)', self.html).group(3)) -            k = int(re.search(r'<script type="text/javascript">([^<]*?)(\d*%(\d*))', self.html).group(3)) -            checksum = ((a + 3) % k) * ((a + 3) % 3) + 18 -        else: -            # This might work but is insecure -            # checksum = eval(re.search("((\d*)\s\%\s(\d*)\s\+\s(\d*)\s\%\s(\d*))", self.html).group(0)) - -            m = re.search(r"((?P<a>\d*)\s%\s(?P<b>\d*)\s\+\s(?P<c>\d*)\s%\s(?P<k>\d*))", self.html) -            if not m: -                self.parseError("Unable to detect values to calculate direct link") -            a = int(m.group("a")) -            b = int(m.group("b")) -            c = int(m.group("c")) -            k = int(m.group("k")) -            if a == c: -                checksum = ((a % b) + (a % k)) + +    def get_checksum(self): +        try: +            m = re.search(r'\+[ ]*\((\d+)[ ]*\%[ ]*(\d+)[ ]*\+[ ]*(\d+)[ ]*\%[ ]*(\d+)\)[ ]*\+', self.html) +            if m: +                a1, a2, c1, c2 = map(int, m.groups()) +                b = (a1 % a2) + (c1 % c2)              else: -                checksum = ((a % b) + (c % k)) +                a1, a2 = map(int, re.search(r'\(\'downloadB\'\).omg = (\d+)%(\d+)'     , self.html).groups()) +                c1, c2 = map(int, re.search(r'\(\'downloadB\'\).omg\) \* \((\d+)%(\d+)', self.html).groups()) +                b = (a1 % a2) * (c1 % c2) + 18 + +        except Exception, e: +            self.error(_("Unable to calculate checksum"), e) -        self.logInfo('Checksum: %s' % checksum) +        else: +            return b -        filename = re.search(r'>Name:</font>\s*<font [^>]*>(?P<N>[^<]+)</font><br />', self.html).group('N') -        url = "/d/%s/%s/%s" % (number, checksum, filename) -        self.logInfo(self.file_info['HOST'] + url) -        return self.file_info['HOST'] + url +    def get_link(self): +        checksum = self.get_checksum() +        p_url    = '/'.join(("d", self.info['pattern']['KEY'], str(checksum), self.pyfile.name)) +        dl_link  = urljoin(self.info['pattern']['HOST'], p_url) +        return dl_link  getInfo = create_getInfo(ZippyshareCom) diff --git a/module/plugins/internal/CaptchaService.py b/module/plugins/internal/CaptchaService.py index 400484d26..965799e8e 100644 --- a/module/plugins/internal/CaptchaService.py +++ b/module/plugins/internal/CaptchaService.py @@ -1,106 +1,332 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. +import re -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. +from random import random -    @author: zoidberg -""" +from module.common.json_layer import json_loads -import re -from random import random +class CaptchaService: +    __name__    = "CaptchaService" +    __version__ = "0.16" -class CaptchaService(): -    __version__ = "0.04" +    __description__ = """Base captcha service plugin""" +    __license__     = "GPLv3" +    __authors__     = [("pyLoad Team", "admin@pyload.org")] -    def __init__(self, plugin): -        self.plugin = plugin +    KEY_PATTERN = None -class ReCaptcha(): -    RECAPTCHA_KEY_PATTERN = r"https?://(?:www\.)?google\.com/recaptcha/api/challenge\?k=(?P<key>\w+)" -    RECAPTCHA_KEY_AJAX_PATTERN = r"Recaptcha\.create\s*\(\s*[\"'](?P<key>\w+)[\"']\s*," +    key = None  #: last key detected -    recaptcha_key = None      def __init__(self, plugin):          self.plugin = plugin -    def detect_key(self, html): -        m = re.search(self.RECAPTCHA_KEY_PATTERN, html) -        if not m: -            m = re.search(self.RECAPTCHA_KEY_AJAX_PATTERN, html) + +    def detect_key(self, html=None): +        if not html: +            if hasattr(self.plugin, "html") and self.plugin.html: +                html = self.plugin.html +            else: +                errmsg = _("%s html not found") % self.__name__ +                self.plugin.fail(errmsg)  #@TODO: replace all plugin.fail(errmsg) with plugin.error(errmsg) in 0.4.10 +                raise TypeError(errmsg) + +        m = re.search(self.KEY_PATTERN, html)          if m: -            self.recaptcha_key = m.group('key') -            return self.recaptcha_key +            self.key = m.group(1).strip() +            self.plugin.logDebug("%s key: %s" % (self.__name__, self.key)) +            return self.key          else: +            self.plugin.logDebug("%s key not found" % self.__name__)              return None +      def challenge(self, key=None): -        if not key and self.recaptcha_key: -            key = self.recaptcha_key -        elif not (key or self.recaptcha_key): -            raise TypeError("ReCaptcha key not found") +        raise NotImplementedError + -        js = self.plugin.req.load("http://www.google.com/recaptcha/api/challenge", get={"k": key}, cookies=True) +    def result(self, server, challenge): +        raise NotImplementedError + + +class ReCaptcha(CaptchaService): +    __name__    = "ReCaptcha" +    __version__ = "0.08" + +    __description__ = """ReCaptcha captcha service plugin""" +    __license__     = "GPLv3" +    __authors__     = [("pyLoad Team", "admin@pyload.org")] + + +    KEY_PATTERN      = r'recaptcha(?:/api|\.net)/(?:challenge|noscript)\?k=([\w-]+)' +    KEY_AJAX_PATTERN = r'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_PATTERN, html) or re.search(self.KEY_AJAX_PATTERN, html) +        if m: +            self.key = m.group(1).strip() +            self.plugin.logDebug("ReCaptcha key: %s" % self.key) +            return self.key +        else: +            self.plugin.logDebug("ReCaptcha key not found") +            return None + +    def challenge(self, key=None): +        if not key: +            if self.detect_key(): +                key = self.key +            else: +                errmsg = _("ReCaptcha key not found") +                self.plugin.fail(errmsg) +                raise TypeError(errmsg) + +        html = self.plugin.req.load("http://www.google.com/recaptcha/api/challenge", get={'k': key})          try: -            challenge = re.search("challenge : '(.*?)',", js).group(1) -            server = re.search("server : '(.*?)',", js).group(1) +            challenge = re.search("challenge : '(.+?)',", html).group(1) +            server    = re.search("server : '(.+?)',", html).group(1)          except: -            self.plugin.fail("recaptcha error") -        result = self.result(server, challenge) +            errmsg = _("ReCaptcha challenge pattern not found") +            self.plugin.fail(errmsg) +            raise ValueError(errmsg) + +        self.plugin.logDebug("ReCaptcha challenge: %s" % challenge) + +        return challenge, self.result(server, challenge) -        return challenge, result      def result(self, server, challenge): -        return self.plugin.decryptCaptcha("%simage" % server, get={"c": challenge}, -                                          cookies=True, forceUser=True, imgtype="jpg") +        result = self.plugin.decryptCaptcha("%simage" % server, +                                            get={'c': challenge}, +                                            cookies=True, +                                            forceUser=True, +                                            imgtype="jpg") + +        self.plugin.logDebug("ReCaptcha result: %s" % result) + +        return result  class AdsCaptcha(CaptchaService): -    def challenge(self, src): -        js = self.plugin.req.load(src, cookies=True) +    __name__    = "AdsCaptcha" +    __version__ = "0.06" + +    __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.plugin.logDebug("AdsCaptcha key|id: %s | %s" % self.key) +            return self.key +        else: +            self.plugin.logDebug("AdsCaptcha key or id not found") +            return None + + +    def challenge(self, key=None): +        if not key: +            if self.detect_key(): +                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: '(.*?)',", js).group(1) -            server = re.search("server: '(.*?)',", js).group(1) +            challenge = re.search("challenge: '(.+?)',", html).group(1) +            server    = re.search("server: '(.+?)',", html).group(1)          except: -            self.plugin.fail("adscaptcha error") -        result = self.result(server, challenge) +            errmsg = _("AdsCaptcha challenge pattern not found") +            self.plugin.fail(errmsg) +            raise ValueError(errmsg) + +        self.plugin.logDebug("AdsCaptcha challenge: %s" % challenge) + +        return challenge, self.result(server, challenge) -        return challenge, result      def result(self, server, challenge): -        return self.plugin.decryptCaptcha("%sChallenge.aspx" % server, get={"cid": challenge, "dummy": random()}, -                                          cookies=True, imgtype="jpg") +        result = self.plugin.decryptCaptcha("%sChallenge.aspx" % server, +                                            get={'cid': challenge, 'dummy': random()}, +                                            cookies=True, +                                            imgtype="jpg") + +        self.plugin.logDebug("AdsCaptcha result: %s" % result) + +        return result  class SolveMedia(CaptchaService): -    def __init__(self, plugin): -        self.plugin = plugin +    __name__    = "SolveMedia" +    __version__ = "0.06" + +    __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 challenge(self, src): -        html = self.plugin.req.load("http://api.solvemedia.com/papi/challenge.noscript?k=%s" % src, cookies=True) + +    def challenge(self, key=None): +        if not key: +            if self.detect_key(): +                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: -            self.plugin.fail("solvemedia error") -        result = self.result(challenge) +            errmsg = _("SolveMedia challenge pattern not found") +            self.plugin.fail(errmsg) +            raise ValueError(errmsg) + +        self.plugin.logDebug("SolveMedia challenge: %s" % challenge) + +        return challenge, self.result(server, challenge) + + +    def result(self, server, challenge): +        result = self.plugin.decryptCaptcha(server, get={'c': challenge}, imgtype="gif") + +        self.plugin.logDebug("SolveMedia result: %s" % result) + +        return result + + +class AdYouLike(CaptchaService): +    __name__    = "AdYouLike" +    __version__ = "0.02" + +    __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.plugin.logDebug("AdYouLike ayl|callback: %s | %s" % self.key) +            return self.key   #: key is the tuple(ayl, callback) +        else: +            self.plugin.logDebug("AdYouLike ayl or callback not found") +            return None + + +    def challenge(self, key=None): +        if not key: +            if self.detect_key(): +                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: +            errmsg = _("AdYouLike challenge pattern not found") +            self.plugin.fail(errmsg) +            raise ValueError(errmsg) + +        self.plugin.logDebug("AdYouLike challenge: %s" % challenge) + +        return self.result(ayl, 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: +            errmsg = _("AdYouLike result not found") +            self.plugin.fail(errmsg) +            raise ValueError(errmsg) + +        result = {'_ayl_captcha_engine' : "adyoulike", +                  '_ayl_env'            : server['all']['env'], +                  '_ayl_tid'            : challenge['tid'], +                  '_ayl_token_challenge': challenge['token'], +                  '_ayl_response'       : response} -        return challenge, result +        self.plugin.logDebug("AdYouLike result: %s" % result) -    def result(self, challenge): -        return self.plugin.decryptCaptcha("http://api.solvemedia.com/papi/media?c=%s" % challenge, imgtype="gif") +        return result diff --git a/module/plugins/internal/DeadCrypter.py b/module/plugins/internal/DeadCrypter.py index 10eccb9bd..07c5c3881 100644 --- a/module/plugins/internal/DeadCrypter.py +++ b/module/plugins/internal/DeadCrypter.py @@ -1,16 +1,32 @@  # -*- coding: utf-8 -*- +from urllib import unquote +from urlparse import urlparse + +from module.plugins.internal.SimpleCrypter import create_getInfo  from module.plugins.Crypter import Crypter as _Crypter  class DeadCrypter(_Crypter): -    __name__ = "DeadCrypter" -    __type__ = "crypter" -    __pattern__ = None -    __version__ = "0.01" -    __description__ = """Crypter is no longer available""" -    __author_name__ = "stickell" -    __author_mail__ = "l.stickell@yahoo.it" +    __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 getInfo(cls, url="", html=""): +        return {'name': urlparse(unquote(url)).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 1, 'url': url} +      def setup(self): -        self.fail("Crypter is no longer available") +        self.pyfile.error = "Crypter is no longer available" +        self.offline()  #@TODO: self.offline("Crypter is no longer available") + + +getInfo = create_getInfo(DeadCrypter) diff --git a/module/plugins/internal/DeadHoster.py b/module/plugins/internal/DeadHoster.py index 201835e2b..6f3252f70 100644 --- a/module/plugins/internal/DeadHoster.py +++ b/module/plugins/internal/DeadHoster.py @@ -1,22 +1,32 @@  # -*- coding: utf-8 -*- +from urllib import unquote +from urlparse import urlparse + +from module.plugins.internal.SimpleHoster import create_getInfo  from module.plugins.Hoster import Hoster as _Hoster -def create_getInfo(plugin): -    def getInfo(urls): -        yield [('#N/A: ' + url, 0, 1, url) for url in urls] -    return getInfo +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 getInfo(cls, url="", html=""): +        return {'name': urlparse(unquote(url)).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 1, 'url': url} -class DeadHoster(_Hoster): -    __name__ = "DeadHoster" -    __type__ = "hoster" -    __pattern__ = None -    __version__ = "0.11" -    __description__ = """Hoster is no longer available""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz"      def setup(self): -        self.fail("Hoster is no longer available") +        self.pyfile.error = "Hoster is no longer available" +        self.offline()  #@TODO: self.offline("Hoster is no longer available") + + +getInfo = create_getInfo(DeadHoster) diff --git a/module/plugins/internal/AbstractExtractor.py b/module/plugins/internal/Extractor.py index 0ecc11f06..0b2462dac 100644 --- a/module/plugins/internal/AbstractExtractor.py +++ b/module/plugins/internal/Extractor.py @@ -1,6 +1,5 @@  # -*- coding: utf-8 -*- -  class ArchiveError(Exception):      pass @@ -9,30 +8,52 @@ class CRCError(Exception):      pass -class WrongPassword(Exception): +class PasswordError(Exception):      pass -class AbtractExtractor: +class Extractor: +    __name__    = "Extractor" +    __version__ = "0.13" + +    __description__ = """Base extractor plugin""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "ranan@pyload.org"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    EXTENSIONS = [] -    __version__ = "0.1" -    @staticmethod -    def checkDeps(): +    @classmethod +    def checkDeps(cls):          """ Check if system statisfy dependencies          :return: boolean          """          return True -    @staticmethod -    def getTargets(files_ids): + +    @classmethod +    def isArchive(cls, file): +        raise NotImplementedError + + +    @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          """ -        raise NotImplementedError +        targets = [] + +        for file, id in files_ids: +            if cls.isArchive(file): +                targets.append((file, id)) + +        return targets -    def __init__(self, m, file, out, fullpath, overwrite, excludefiles, renice): + +    def __init__(self, m, file, out, password, fullpath, overwrite, excludefiles, renice, delete, keepbroken):          """Initialize extractor for specific file          :param m: ExtractArchive Hook plugin @@ -42,49 +63,70 @@ class AbtractExtractor:          :param overwrite: Overwrite existing archives          :param renice: Renice value          """ -        self.m = m -        self.file = file -        self.out = out -        self.fullpath = fullpath -        self.overwrite = overwrite +        self.m            = m +        self.file         = file +        self.out          = out +        self.password     = password +        self.fullpath     = fullpath +        self.overwrite    = overwrite          self.excludefiles = excludefiles -        self.renice = renice -        self.files = []  #: Store extracted files here +        self.renice       = renice +        self.delete       = delete +        self.keepbroken   = keepbroken +        self.files        = []  #: Store extracted files here +      def init(self):          """ Initialize additional data structures """          pass -    def checkArchive(self): + +    def verify(self):          """Check if password if needed. Raise ArchiveError if integrity is          questionable. -        :return: boolean          :raises ArchiveError          """ -        return False +        pass + -    def checkPassword(self, password): +    def isPassword(self, password):          """ Check if the given password is/might be correct.          If it can not be decided at this point return true.          :param password:          :return: boolean          """ -        return True +        if isinstance(password, basestring): +            return True +        else: +            return False + + +    def setPassword(self, password): +        if self.isPassword(password): +            self.password = password +            return True +        else: +            return False -    def extract(self, progress, password=None): + +    def repair(self): +        return False + + +    def extract(self, progress=lambda x: 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 WrongPassword +        :raises PasswordError          :raises CRCError          :raises ArchiveError          :return:          """          raise NotImplementedError +      def getDeleteFiles(self):          """Return list of files to delete, do *not* delete them here. @@ -92,6 +134,7 @@ class AbtractExtractor:          """          raise NotImplementedError +      def getExtractedFiles(self):          """Populate self.files at some point while extracting"""          return self.files diff --git a/module/plugins/internal/MultiHook.py b/module/plugins/internal/MultiHook.py new file mode 100644 index 000000000..8164a02fc --- /dev/null +++ b/module/plugins/internal/MultiHook.py @@ -0,0 +1,235 @@ +# -*- coding: utf-8 -*- + +import re + +from module.plugins.Hook import Hook +from module.utils import remove_chars + + +class MultiHook(Hook): +    __name__    = "MultiHook" +    __type__    = "hook" +    __version__ = "0.26" + +    __config__ = [("mode"        , "all;listed;unlisted", "Use for plugins (if supported)"               , "all"), +                  ("pluginlist"  , "str"                , "Plugin list (comma separated)"                , ""   ), +                  ("revertfailed", "bool"               , "Revert to standard download if download fails", False), +                  ("interval"    , "int"                , "Reload interval in hours (0 to disable)"      , 12   )] + +    __description__ = """Hook plugin for multi hoster/crypter""" +    __license__     = "GPLv3" +    __authors__     = [("pyLoad Team", "admin@pyload.org"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    MIN_INTERVAL = 12 * 60 * 60  #: reload plugins every 12h + +    PLUGIN_REPLACEMENTS = [("1fichier.com"   , "onefichier.com"), +                           ("2shared.com"    , "twoshared.com" ), +                           ("4shared.com"    , "fourshared.com"), +                           ("cloudnator.com" , "shragle.com"   ), +                           ("easy-share.com" , "crocko.com"    ), +                           ("fileparadox.com", "fileparadox.in"), +                           ("freakshare.net" , "freakshare.com"), +                           ("hellshare.com"  , "hellshare.cz"  ), +                           ("ifile.it"       , "filecloud.io"  ), +                           ("nowdownload.ch" , "nowdownload.sx"), +                           ("nowvideo.co"    , "nowvideo.sx"   ), +                           ("putlocker.com"  , "firedrive.com" ), +                           ("share-rapid.cz" , "multishare.cz" ), +                           ("sharerapid.cz"  , "multishare.cz" ), +                           ("ul.to"          , "uploaded.to"   ), +                           ("uploaded.net"   , "uploaded.to"   )] + + +    def setup(self): +        self.type          = self.core.pluginManager.findPlugin(self.__name__)[1] or "hoster" +        self.plugins       = [] +        self.supported     = [] +        self.new_supported = [] + + +    def getURL(self, *args, **kwargs):  #@TODO: Remove in 0.4.10 +        """ see HTTPRequest for argument list """ +        h = pyreq.getHTTPRequest(timeout=120) +        try: +            rep = h.load(*args, **kwargs) +        finally: +            h.close() + +        return rep + + +    def getConfig(self, option, default=''): +        """getConfig with default value - sublass may not implements all config options""" +        try: +            return self.getConf(option) + +        except KeyError: +            return default + + +    def pluginCached(self): +        if not self.plugins: +            try: +                pluginset = self.pluginSet(self.getHosters() if self.type == "hoster" else self.getCrypters()) +            except Exception, e: +                self.logError(e) +                return [] + +            try: +                configmode = self.getConfig("mode", '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): +        plugins = set((str(x).strip().lower() for x in plugins)) + +        for rep in self.PLUGIN_REPLACEMENTS: +            if rep[0] in plugins: +                plugins.remove(rep[0]) +                plugins.add(rep[1]) + +        plugins.discard('') + +        return 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.interval = max(self.getConfig("interval", 0), self.MIN_INTERVAL) + +        self.logInfo(_("Reloading supported %s list") % self.type) + +        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.type == "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.pluginCached(): +            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.type) +            return + +        module = self.core.pluginManager.getPlugin(self.__name__) +        klass  = getattr(module, self.__name__) + +        # inject plugin plugin +        self.logDebug("Overwritten %ss: %s" % (self.type, ", ".join(sorted(self.supported)))) + +        for plugin in self.supported: +            hdict = self.core.pluginManager.plugins[self.type][plugin] +            hdict['new_module'] = module +            hdict['new_name']   = self.__name__ + +        if excludedList: +            self.logInfo(_("%ss not overwritten: %s") % (self.type.capitalize(), ", ".join(sorted(excludedList)))) + +        if self.new_supported: +            plugins = sorted(self.new_supported) + +            self.logDebug("New %ss: %s" % (self.type, ", ".join(plugins))) + +            # create new regexp +            regexp = r'.*(%s).*' % "|".join([x.replace(".", "\.") for x in plugins]) +            if hasattr(klass, "__pattern__") and isinstance(klass.__pattern__, basestring) and '://' in klass.__pattern__: +                regexp = r'%s|%s' % (klass.__pattern__, regexp) + +            self.logDebug("Regexp: %s" % regexp) + +            hdict = self.core.pluginManager.plugins[self.type][self.__name__] +            hdict['pattern'] = regexp +            hdict['re']      = re.compile(regexp) + + +    def unloadPlugin(self, plugin): +        hdict = self.core.pluginManager.plugins[self.type][plugin] +        if "module" in hdict: +            del hdict['module'] + +        if "new_module" in hdict: +            del hdict['new_module'] +            del hdict['new_name'] + + +    def unload(self): +        """Remove override for all plugins. Scheduler job is removed by hookmanager""" +        for plugin in self.supported: +            self.unloadPlugin(plugin) + +        # reset pattern +        klass = getattr(self.core.pluginManager.getPlugin(self.__name__), self.__name__) +        hdict = self.core.pluginManager.plugins[self.type][self.__name__] + +        hdict['pattern'] = getattr(klass, "__pattern__", r'^unmatchable$') +        hdict['re']      = re.compile(hdict['pattern']) + + +    def downloadFailed(self, pyfile): +        """remove plugin override if download fails but not if file is offline/temp.offline""" +        if pyfile.hasStatus("failed") and self.getConfig("revertfailed", True): +            hdict = self.core.pluginManager.plugins[self.type][pyfile.pluginname] +            if "new_name" in hdict and hdict['new_name'] == self.__name__: +                self.logDebug("Unload MultiHook", pyfile.pluginname, hdict) +                self.unloadPlugin(pyfile.pluginname) +                pyfile.setStatus("queued") diff --git a/module/plugins/internal/MultiHoster.py b/module/plugins/internal/MultiHoster.py index 97f3a5996..8cdb2fbff 100644 --- a/module/plugins/internal/MultiHoster.py +++ b/module/plugins/internal/MultiHoster.py @@ -2,188 +2,83 @@  import re -from module.utils import remove_chars -from module.plugins.Hook import Hook +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, replace_patterns, set_cookies -class MultiHoster(Hook): -    """ -    Generic MultiHoster plugin -    """ +class MultiHoster(SimpleHoster): +    __name__    = "MultiHoster" +    __type__    = "hoster" +    __version__ = "0.27" -    __version__ = "0.19" +    __pattern__ = r'^unmatchable$' -    replacements = [("2shared.com", "twoshared.com"), ("4shared.com", "fourshared.com"), ("cloudnator.com", "shragle.com"), -                    ("ifile.it", "filecloud.io"), ("easy-share.com", "crocko.com"), ("freakshare.net", "freakshare.com"), -                    ("hellshare.com", "hellshare.cz"), ("share-rapid.cz", "sharerapid.com"), ("sharerapid.cz", "sharerapid.com"), -                    ("ul.to", "uploaded.to"), ("uploaded.net", "uploaded.to"), ("1fichier.com", "onefichier.com")] -    ignored = [] -    interval = 24 * 60 * 60  #: reload hosters daily +    __description__ = """Multi hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] -    def setup(self): -        self.hosters = [] -        self.supported = [] -        self.new_supported = [] - -    def getConfig(self, option, default=''): -        """getConfig with default value - sublass may not implements all config options""" -        try: -            return self.getConf(option) -        except KeyError: -            return default - -    def getHosterCached(self): -        if not self.hosters: - -            try: -                hosterSet = self.toHosterSet(self.getHoster()) - set(self.ignored) -            except Exception, e: -                self.logError("%s" % str(e)) -                return [] - -            try: -                configMode = self.getConfig('hosterListMode', 'all') -                if configMode in ("listed", "unlisted"): -                    configSet = self.toHosterSet(self.getConfig('hosterList', '').replace('|', ',').replace(';', ',').split(',')) - -                    if configMode == "listed": -                        hosterSet &= configSet -                    else: -                        hosterSet -= configSet -            except Exception, e: -                self.logError("%s" % str(e)) +    LOGIN_ACCOUNT = True -            self.hosters = list(hosterSet) -        return self.hosters +    def setup(self): +        self.chunkLimit = 1 +        self.multiDL    = self.premium -    def toHosterSet(self, hosters): -        hosters = set((str(x).strip().lower() for x in hosters)) -        for rep in self.replacements: -            if rep[0] in hosters: -                hosters.remove(rep[0]) -                hosters.add(rep[1]) +    def prepare(self): +        self.info      = {} +        self.link      = ""     #@TODO: Move to hoster class in 0.4.10 +        self.directDL  = False  #@TODO: Move to hoster class in 0.4.10 -        hosters.discard('') -        return hosters +        if self.LOGIN_ACCOUNT and not self.account: +            self.fail(_("Required account not found")) -    def getHoster(self): -        """Load list of supported hoster +        self.req.setOption("timeout", 120) -        :return: List of domain names -        """ -        raise NotImplementedError +        if isinstance(self.COOKIES, list): +            set_cookies(self.req.cj, self.COOKIES) -    def coreReady(self): -        if self.cb: -            self.core.scheduler.removeJob(self.cb) +        if self.DIRECT_LINK is None: +            self.directDL = self.__pattern__ != r'^unmatchable$' +        else: +            self.directDL = self.DIRECT_LINK -        self.setConfig("activated", True)  #: config not in sync after plugin reload +        self.pyfile.url = replace_patterns(self.pyfile.url, +                                           self.FILE_URL_REPLACEMENTS if hasattr(self, "FILE_URL_REPLACEMENTS") else self.URL_REPLACEMENTS)  #@TODO: Remove FILE_URL_REPLACEMENTS check in 0.4.10 -        cfg_interval = self.getConfig("interval", None)  #: reload interval in hours -        if cfg_interval is not None: -            self.interval = cfg_interval * 60 * 60 -        if self.interval: -            self._periodical() -        else: -            self.periodical() +    def process(self, pyfile): +        self.prepare() -    def initPeriodical(self): -        pass +        if self.directDL: +            self.logDebug("Looking for direct download link...") +            self.handleDirect() -    def periodical(self): -        """reload hoster list periodically""" -        self.logInfo("Reloading supported hoster list") +        if self.link: +            self.pyfile.url = self.link +            self.checkNameSize() -        old_supported = self.supported -        self.supported, self.new_supported, self.hosters = [], [], [] +        elif not self.lastDownload: +            self.preload() +            self.checkInfo() -        self.overridePlugins() +            if self.premium and (not self.CHECK_TRAFFIC or self.checkTrafficLeft()): +                self.logDebug("Handled as premium download") +                self.handlePremium() +            else: +                self.logDebug("Handled as free download") +                self.handleFree() -        old_supported = [hoster for hoster in old_supported if hoster not in self.supported] -        if old_supported: -            self.logDebug("UNLOAD: %s" % ", ".join(old_supported)) -            for hoster in old_supported: -                self.unloadHoster(hoster) +        self.downloadLink(self.link) +        self.checkFile() -    def overridePlugins(self): -        pluginMap = {} -        for name in self.core.pluginManager.hosterPlugins.keys(): -            pluginMap[name.lower()] = name -        accountList = [name.lower() for name, data in self.core.accountManager.accounts.items() if data] -        excludedList = [] +    def handlePremium(self): +        return self.handleFree() -        for hoster in self.getHosterCached(): -            name = remove_chars(hoster.lower(), "-.") -            if name in accountList: -                excludedList.append(hoster) -            else: -                if name in pluginMap: -                    self.supported.append(pluginMap[name]) -                else: -                    self.new_supported.append(hoster) - -        if not self.supported and not self.new_supported: -            self.logError(_("No Hoster loaded")) -            return - -        module = self.core.pluginManager.getPlugin(self.__name__) -        klass = getattr(module, self.__name__) - -        # inject plugin plugin -        self.logDebug("Overwritten Hosters: %s" % ", ".join(sorted(self.supported))) -        for hoster in self.supported: -            dict = self.core.pluginManager.hosterPlugins[hoster] -            dict["new_module"] = module -            dict["new_name"] = self.__name__ - -        if excludedList: -            self.logInfo("The following hosters were not overwritten - account exists: %s" % ", ".join(sorted(excludedList))) - -        if self.new_supported: -            self.logDebug("New Hosters: %s" % ", ".join(sorted(self.new_supported))) - -            # create new regexp -            regexp = r".*(%s).*" % "|".join([x.replace(".", "\\.") for x in self.new_supported]) -            if hasattr(klass, "__pattern__") and isinstance(klass.__pattern__, basestring) and '://' in klass.__pattern__: -                regexp = r"%s|%s" % (klass.__pattern__, regexp) - -            self.logDebug("Regexp: %s" % regexp) - -            dict = self.core.pluginManager.hosterPlugins[self.__name__] -            dict["pattern"] = regexp -            dict["re"] = re.compile(regexp) - -    def unloadHoster(self, hoster): -        dict = self.core.pluginManager.hosterPlugins[hoster] -        if "module" in dict: -            del dict["module"] - -        if "new_module" in dict: -            del dict["new_module"] -            del dict["new_name"] - -    def unload(self): -        """Remove override for all hosters. Scheduler job is removed by hookmanager""" -        for hoster in self.supported: -            self.unloadHoster(hoster) - -        # reset pattern -        klass = getattr(self.core.pluginManager.getPlugin(self.__name__), self.__name__) -        dict = self.core.pluginManager.hosterPlugins[self.__name__] -        dict["pattern"] = getattr(klass, '__pattern__', r"^unmatchable$") -        dict["re"] = re.compile(dict["pattern"]) - -    def downloadFailed(self, pyfile): -        """remove plugin override if download fails but not if file is offline/temp.offline""" -        if pyfile.hasStatus("failed") and self.getConfig("unloadFailing", True): -            hdict = self.core.pluginManager.hosterPlugins[pyfile.pluginname] -            if "new_name" in hdict and hdict['new_name'] == self.__name__: -                self.logDebug("Unload MultiHoster", pyfile.pluginname, hdict) -                self.unloadHoster(pyfile.pluginname) -                pyfile.setStatus("queued") +    def handleFree(self): +        if self.premium: +            raise NotImplementedError +        else: +            self.fail(_("Required premium account not found")) diff --git a/module/plugins/internal/SimpleCrypter.py b/module/plugins/internal/SimpleCrypter.py index b1a18f5e0..566252245 100644 --- a/module/plugins/internal/SimpleCrypter.py +++ b/module/plugins/internal/SimpleCrypter.py @@ -1,78 +1,141 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. +import re -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. +from urlparse import urlparse -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. +from module.plugins.Crypter import Crypter +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, replace_patterns, set_cookies +from module.utils import fixup -    @author: zoidberg -""" -import re - -from module.plugins.Crypter import Crypter -from module.utils import html_unescape -from module.plugins.internal.SimpleHoster import replace_patterns +class SimpleCrypter(Crypter, SimpleHoster): +    __name__    = "SimpleCrypter" +    __type__    = "crypter" +    __version__ = "0.36" +    __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)] -class SimpleCrypter(Crypter): -    __name__ = "SimpleCrypter" -    __version__ = "0.07" -    __pattern__ = None -    __type__ = "crypter"      __description__ = """Simple decrypter plugin""" -    __author_name__ = ("stickell", "zoidberg") -    __author_mail__ = ("l.stickell@yahoo.it", "zoidberg@mujmail.cz") +    __license__     = "GPLv3" +    __authors__     = [("stickell", "l.stickell@yahoo.it"), +                       ("zoidberg", "zoidberg@mujmail.cz"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + +      """ -    These patterns should be defined by each crypter: +    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)' -    LINK_PATTERN: group(1) must be a download link -    example: <div class="link"><a href="(http://speedload.org/\w+) +      TEMP_OFFLINE_PATTERN: (optional) Checks if the page is temporarily unreachable +        example: TEMP_OFFLINE_PATTERN = r'Server maintainance' -    TITLE_PATTERN: (optional) the group defined by 'title' should be the title -    example: <title>Files of: (?P<title>[^<]+) folder</title> -    If it's impossible to extract the links using the LINK_PATTERN only you can override the getLinks method. +    You can override the getLinks method if you need a more sophisticated way to extract the links. -    If the links are disposed on multiple pages you need to define a pattern: -    PAGES_PATTERN: the group defined by 'pages' must be the total number of pages +    If the links are splitted on multiple pages you can define the PAGES_PATTERN regex: -    and a function: +      PAGES_PATTERN: (optional) group(1) should be the number of overall pages containing the links +        example: PAGES_PATTERN = r'Pages: (\d+)' -    loadPage(self, page_n): -    must return the html of the page number 'page_n' +    and its loadPage method: + + +      def loadPage(self, page_n): +          return the html of the page number page_n      """ -    FILE_URL_REPLACEMENTS = [] +    LINK_PATTERN = None -    def decrypt(self, pyfile): -        pyfile.url = replace_patterns(pyfile.url, self.FILE_URL_REPLACEMENTS) +    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 + + +    #@TODO: Remove in 0.4.10 +    def init(self): +        account_name = (self.__name__ + ".py").replace("Folder.py", "").replace(".py", "") +        account = self.core.accountManager.getAccountPlugin(account_name) + +        if account and account.canUse(): +            self.user, data = account.selectAccount() +            self.req = account.getAccountRequest(self.user) +            self.premium = account.isPremium(self.user) + +            self.account = account -        self.html = self.load(pyfile.url, decode=True) -        package_name, folder_name = self.getPackageNameAndFolder() +    def prepare(self): +        self.info  = {} +        self.links = []  #@TODO: Move to hoster class in 0.4.10 -        self.package_links = self.getLinks() +        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.handleMultiPages() -        self.logDebug('Package has %d links' % len(self.package_links)) +        self.logDebug("Package has %d links" % len(self.links)) + +        if self.links: +            self.packages = [(self.info['name'], self.links, self.info['folder'])] -        if self.package_links: -            self.packages = [(package_name, self.package_links, folder_name)] +        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 getinfo: +            self.updateInfo(self.getInfo(self.pyfile.url, self.html)) + +        name = self.info['name'] +        url  = self.info['url'] + +        if name and name != url: +            self.pyfile.name = name          else: -            self.fail('Could not extract any links') +            self.pyfile.name = self.info['name'] = urlparse(name).path.split('/')[-1] + +        folder = self.info['folder'] = self.pyfile.name + +        self.logDebug("File name: %s" % self.pyfile.name, +                      "File folder: %s" % folder) +      def getLinks(self):          """ @@ -81,26 +144,14 @@ class SimpleCrypter(Crypter):          """          return re.findall(self.LINK_PATTERN, self.html) -    def getPackageNameAndFolder(self): -        if hasattr(self, 'TITLE_PATTERN'): -            m = re.search(self.TITLE_PATTERN, self.html) -            if m: -                name = folder = html_unescape(m.group('title').strip()) -                self.logDebug("Found name [%s] and folder [%s] in package info" % (name, folder)) -                return name, folder - -        name = self.pyfile.package().name -        folder = self.pyfile.package().folder -        self.logDebug("Package info not found, defaulting to pyfile name [%s] and folder [%s]" % (name, folder)) -        return name, folder      def handleMultiPages(self): -        pages = re.search(self.PAGES_PATTERN, self.html) -        if pages: -            pages = int(pages.group('pages')) -        else: +        try: +            m = re.search(self.PAGES_PATTERN, self.html) +            pages = int(m.group(1)) +        except:              pages = 1          for p in xrange(2, pages + 1):              self.html = self.loadPage(p) -            self.package_links += self.getLinks() +            self.links += self.getLinks() diff --git a/module/plugins/internal/SimpleDereferer.py b/module/plugins/internal/SimpleDereferer.py new file mode 100644 index 000000000..04d63658e --- /dev/null +++ b/module/plugins/internal/SimpleDereferer.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- + +import re + +from urllib import unquote + +from module.plugins.Crypter import Crypter +from module.plugins.internal.SimpleHoster import _isDirectLink, set_cookies + + +class SimpleDereferer(Crypter): +    __name__    = "SimpleDereferer" +    __type__    = "crypter" +    __version__ = "0.02" + +    __pattern__ = r'^unmatchable$' +    __config__  = [("use_subfolder", "bool", "Save package to subfolder", True), +                   ("subfolder_per_package", "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 = _isDirectLink(pyfile.url) + +        if not link: +            try: +                link = unquote(re.match(self.__pattern__, pyfile.url).group('LINK')) + +            except Exception: +                self.prepare() +                self.preload() + +                if self.html is None: +                    self.fail(_("No html retrieved")) + +                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.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/module/plugins/internal/SimpleHoster.py b/module/plugins/internal/SimpleHoster.py index f10433e78..6075e3f67 100644 --- a/module/plugins/internal/SimpleHoster.py +++ b/module/plugins/internal/SimpleHoster.py @@ -1,36 +1,51 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" -from urlparse import urlparse  import re + +from os.path import exists  from time import time +from urllib import unquote +from urlparse import urljoin, urlparse -from module.plugins.Hoster import Hoster -from module.utils import html_unescape, fixup, parseFileSize -from module.network.RequestFactory import getURL +from module.PyFile import statusMap as _statusMap  from module.network.CookieJar import CookieJar +from module.network.RequestFactory import getURL +from module.plugins.Hoster import Hoster +from module.plugins.Plugin import Fail +from module.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) -        #self.logDebug(rf, rt, string)      return string @@ -46,23 +61,24 @@ def parseHtmlTagAttrValue(attr_name, tag):      return m.group(2) if m else None -def parseHtmlForm(attr_str, html, input_names=None): -    for form in re.finditer(r"(?P<tag><form[^>]*%s[^>]*>)(?P<content>.*?)</?(form|body|html)[^>]*>" % attr_str, +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): +        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 value is None: +                if not value:                      inputs[name] = inputtag.group(3) or ''                  else:                      inputs[name] = value -        if isinstance(input_names, dict): +        if input_names:              # check input attributes -            for key, val in input_names.items(): +            for key, val in input_names.iteritems():                  if key in inputs:                      if isinstance(val, basestring) and inputs[key] == val:                          continue @@ -70,219 +86,534 @@ def parseHtmlForm(attr_str, html, input_names=None):                          continue                      elif hasattr(val, "search") and re.match(val, inputs[key]):                          continue -                    break  # attibute value does not match +                    break  #: attibute value does not match                  else: -                    break  # attibute name does not match +                    break  #: attibute name does not match              else: -                return action, inputs  # passed attribute check +                return action, inputs  #: passed attribute check          else:              # no attribute check              return action, inputs -    return {}, None  # no matching form found +    return {}, None  #: no matching form found -def parseFileInfo(self, url='', html=''): -    info = {"name": url, "size": 0, "status": 3} +#: 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: +        res  = urlparse(unquote(url)).path.split('/')[-1] or _("Unknown"), 0, 3, url -    if hasattr(self, "pyfile"): -        url = self.pyfile.url +    return res -    if hasattr(self, "req") and self.req.http.code == '404': -        info['status'] = 1 + +#@TODO: Remove in 0.4.10 +#@NOTE: Every plugin must have own parseInfos classmethod to work with 0.4.10 +def create_getInfo(plugin): +    if hasattr(plugin, "parseInfos"): +        fn = lambda urls: [(info['name'], info['size'], info['status'], info['url']) for info in plugin.parseInfos(urls)]      else: -        if not html and hasattr(self, "html"): -            html = self.html -        if isinstance(self.SH_BROKEN_ENCODING, (str, unicode)): -            html = unicode(html, self.SH_BROKEN_ENCODING) -            if hasattr(self, "html"): -                self.html = html - -        if hasattr(self, "FILE_OFFLINE_PATTERN") and re.search(self.FILE_OFFLINE_PATTERN, html): -            # File offline -            info['status'] = 1 -        else: -            online = False -            try: -                info.update(re.match(self.__pattern__, url).groupdict()) -            except: -                pass +        fn = lambda urls: [parseFileInfo(url) for url in urls] -            for pattern in ("FILE_INFO_PATTERN", "FILE_NAME_PATTERN", "FILE_SIZE_PATTERN"): -                try: -                    info.update(re.search(getattr(self, pattern), html).groupdict()) -                    online = True -                except AttributeError: -                    continue +    return fn -            if online: -                # File online, return name and size -                info['status'] = 2 -                if 'N' in info: -                    info['name'] = replace_patterns(info['N'], self.FILE_NAME_REPLACEMENTS) -                if 'S' in info: -                    size = replace_patterns(info['S'] + info['U'] if 'U' in info else info['S'], -                                            self.FILE_SIZE_REPLACEMENTS) -                    info['size'] = parseFileSize(size) -                elif isinstance(info['size'], (str, unicode)): -                    if 'units' in info: -                        info['size'] += info['units'] -                    info['size'] = parseFileSize(info['size']) -    if hasattr(self, "file_info"): -        self.file_info = info +def timestamp(): +    return int(time() * 1000) -    return info['name'], info['size'], info['status'], url +#@TODO: Move to hoster class in 0.4.10 +def _isDirectLink(self, url, resumable=False): +    link = "" -def create_getInfo(plugin): -    def getInfo(urls): -        for url in urls: -            cj = CookieJar(plugin.__name__) -            if isinstance(plugin.SH_COOKIES, list): -                set_cookies(cj, plugin.SH_COOKIES) -            file_info = parseFileInfo(plugin, url, getURL(replace_patterns(url, plugin.FILE_URL_REPLACEMENTS), -                                                          decode=not plugin.SH_BROKEN_ENCODING, cookies=cj)) -            yield file_info +    for i in xrange(5 if resumable else 1): +        header = self.load(url, ref=True, cookies=True, just_header=True, decode=True) -    return getInfo +        if 'content-disposition' in header or 'content-length' in header: +            link = url +        elif 'location' in header and header['location']: +            location = header['location'] -def timestamp(): -    return int(time() * 1000) +            if not urlparse(location).scheme: +                p = urlparse(url) +                base = "%s://%s" % (p.scheme, p.netloc) +                location = urljoin(base, location) +            if 'code' in header and header['code'] == 302: +                link = location -class PluginParseError(Exception): -    def __init__(self, msg): -        Exception.__init__(self) -        self.value = 'Parse error (%s) - plugin may be out of date' % msg +            elif resumable: +                url = location +                self.logDebug("Redirect #%d to: %s" % (++i, location)) +                continue -    def __str__(self): -        return repr(self.value) +        elif 'content-type' in header and header['content-type' ] and "html" not in header['content-type']: +            link = url + +        break +    else: +        self.logError(_("Too many redirects")) + +    return link + + +def secondsToMidnight(gmt=0): +    now = datetime.utcnow() + timedelta(hours=gmt) + +    if now.hour is 0 and now.minute < 10: +        midnight = now +    else: +        midnight = now + 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:  #@NOTE: work-around for python 2.5 and 2.6 missing timedelta.total_seconds +        res = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6 + +    return int(res)  class SimpleHoster(Hoster): -    __name__ = "SimpleHoster" -    __version__ = "0.33" -    __pattern__ = None -    __type__ = "hoster" +    __name__    = "SimpleHoster" +    __type__    = "hoster" +    __version__ = "0.84" + +    __pattern__ = r'^unmatchable$' +      __description__ = """Simple hoster plugin""" -    __author_name__ = ("zoidberg", "stickell") -    __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it") +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + +      """ -    These patterns should be defined by each hoster: -    FILE_INFO_PATTERN = r'(?P<N>file_name) (?P<S>file_size) (?P<U>units)' -    or FILE_NAME_PATTERN = r'(?P<N>file_name)' -    and FILE_SIZE_PATTERN = r'(?P<S>file_size) (?P<U>units)' -    FILE_OFFLINE_PATTERN = r'File (deleted|not found)' -    TEMP_OFFLINE_PATTERN = r'Server maintainance' - -    You can also define a PREMIUM_ONLY_PATTERN to detect links that can be downloaded only with a premium account. +    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="(.+?)"'      """ -    FILE_SIZE_REPLACEMENTS = [] -    FILE_NAME_REPLACEMENTS = [("&#?\w+;", fixup)] -    FILE_URL_REPLACEMENTS = [] +    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) + + +    @classmethod +    def parseInfos(cls, urls): +        for url in urls: +            url = replace_patterns(url, cls.FILE_URL_REPLACEMENTS if hasattr(cls, "FILE_URL_REPLACEMENTS") else cls.URL_REPLACEMENTS)  #@TODO: Remove FILE_URL_REPLACEMENTS check in 0.4.10 +            yield cls.getInfo(url) + + +    @classmethod +    def getInfo(cls, url="", html=""): +        info   = {'name': urlparse(unquote(url)).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 3, 'url': url} +        online = False -    SH_BROKEN_ENCODING = False  # Set to True or encoding name if encoding in http header is not correct -    SH_COOKIES = True  # or False or list of tuples [(domain, name, value)] -    SH_CHECK_TRAFFIC = False  # True = force check traffic left for a premium account +        try: +            info['pattern'] = re.match(cls.__pattern__, url).groupdict()  #: pattern groups will be saved here, please save api stuff to info['api'] +        except Exception: +            pass + +        if not html: +            try: +                if not url: +                    info['error']  = "missing url" +                    info['status'] = 1 +                    raise + +                if _isDirectLink(url): +                    info['error']  = "direct link" +                    info['status'] = 2 +                    raise + +                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 +                        raise + +                    if e.code is 503: +                        info['status'] = 6 +                        raise +            except: +                return info + +        if hasattr(cls, "OFFLINE_PATTERN") and re.search(cls.OFFLINE_PATTERN, html): +            info['status'] = 1 + +        elif hasattr(cls, "FILE_OFFLINE_PATTERN") and re.search(cls.FILE_OFFLINE_PATTERN, html):  #@TODO: Remove in 0.4.10 +            info['status'] = 1 + +        elif hasattr(cls, "TEMP_OFFLINE_PATTERN") and re.search(cls.TEMP_OFFLINE_PATTERN, html): +            info['status'] = 6 + +        else: +            if not 'pattern' in info: +                info['pattern'] = {} + +            for pattern in ("FILE_INFO_PATTERN", "INFO_PATTERN", +                            "FILE_NAME_PATTERN", "NAME_PATTERN", +                            "FILE_SIZE_PATTERN", "SIZE_PATTERN", +                            "HASHSUM_PATTERN"):  #@TODO: Remove old patterns starting with "FILE_" in 0.4.10 +                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 not info['pattern']: +                info.pop('pattern', None) + +        if online: +            info['status'] = 2 + +            if 'N' in info['pattern']: +                info['name'] = replace_patterns(unquote(info['pattern']['N'].strip()), +                                                cls.FILE_NAME_REPLACEMENTS if hasattr(cls, "FILE_NAME_REPLACEMENTS") else cls.NAME_REPLACEMENTS)  #@TODO: Remove FILE_NAME_REPLACEMENTS check in 0.4.10 + +            if 'S' in info['pattern']: +                size = replace_patterns(info['pattern']['S'] + info['pattern']['U'] if 'U' in info['pattern'] else info['pattern']['S'], +                                        cls.FILE_SIZE_REPLACEMENTS if hasattr(cls, "FILE_SIZE_REPLACEMENTS") else cls.SIZE_REPLACEMENTS)  #@TODO: Remove FILE_SIZE_REPLACEMENTS check in 0.4.10 +                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'] + +        return info -    def init(self): -        self.file_info = {}      def setup(self):          self.resumeDownload = self.multiDL = self.premium -        if isinstance(self.SH_COOKIES, list): -            set_cookies(self.req.cj, self.SH_COOKIES) -    def process(self, pyfile): -        pyfile.url = replace_patterns(pyfile.url, self.FILE_URL_REPLACEMENTS) + +    def prepare(self): +        self.info      = {} +        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 +          self.req.setOption("timeout", 120) -        # Due to a 0.4.9 core bug self.load would keep previous cookies even if overridden by cookies parameter. -        # Workaround using getURL. Can be reverted in 0.5 as the cookies bug has been fixed. -        self.html = getURL(pyfile.url, decode=not self.SH_BROKEN_ENCODING, cookies=self.SH_COOKIES) -        premium_only = hasattr(self, 'PREMIUM_ONLY_PATTERN') and re.search(self.PREMIUM_ONLY_PATTERN, self.html) -        if not premium_only:  # Usually premium only pages doesn't show the file information -            self.getFileInfo() - -        if self.premium and (not self.SH_CHECK_TRAFFIC or self.checkTrafficLeft()): -            self.handlePremium() -        elif premium_only: -            self.fail("This link require a premium account") + +        if isinstance(self.COOKIES, list): +            set_cookies(self.req.cj, self.COOKIES) + +        if (self.MULTI_HOSTER +            and (self.__pattern__ != self.core.pluginManager.hosterPlugins[self.__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: -            # This line is required due to the getURL workaround. Can be removed in 0.5 -            self.html = self.load(pyfile.url, decode=not self.SH_BROKEN_ENCODING, cookies=self.SH_COOKIES) -            self.handleFree() +            self.directDL = self.DIRECT_LINK -    def load(self, url, get={}, post={}, ref=True, cookies=True, just_header=False, decode=False): -        if type(url) == unicode: -            url = url.encode('utf8') -        return Hoster.load(self, url=url, get=get, post=post, ref=ref, cookies=cookies, -                           just_header=just_header, decode=decode) +        self.pyfile.url = replace_patterns(self.pyfile.url, +                                           self.FILE_URL_REPLACEMENTS if hasattr(self, "FILE_URL_REPLACEMENTS") else self.URL_REPLACEMENTS)  #@TODO: Remove FILE_URL_REPLACEMENTS check in 0.4.10 -    def getFileInfo(self): -        self.logDebug("URL: %s" % self.pyfile.url) -        if hasattr(self, "TEMP_OFFLINE_PATTERN") and re.search(self.TEMP_OFFLINE_PATTERN, self.html): -            self.tempOffline() -        name, size, status = parseFileInfo(self)[:3] +    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): +        self.prepare() +        self.checkNameSize() + +        if self.directDL: +            self.logDebug("Looking for direct download link...") +            self.handleDirect() + +        if self.multihost and not self.link and not self.lastDownload: +            self.logDebug("Looking for leeched download link...") +            self.handleMulti() + +            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() + +            else: +                self.logDebug("Handled as free download") +                self.handleFree() + +        self.downloadLink(self.link) +        self.checkFile() + + +    def downloadLink(self, link): +        if link and isinstance(link, basestring): +            self.correctCaptcha() +            self.download(link, disposition=True) + + +    def checkFile(self): +        if self.cTask and not self.lastDownload: +            self.invalidCaptcha() +            self.retry(10, reason=_("Wrong captcha")) + +        elif not self.lastDownload or not exists(fs_encode(self.lastDownload)): +            errmsg = _("No file downloaded") +            if 'error' in self.info: +                self.fail(errmsg, self.info['error']) +            else: +                self.fail(errmsg) + +        else: +            rules = {'empty file': re.compile(r"^$")} + +            if hasattr(self, 'ERROR_PATTERN'): +                rules['error'] = re.compile(self.ERROR_PATTERN) + +            check = self.checkDownload(rules) +            if check:  #@TODO: Move to hoster in 0.4.10 +                errmsg = check.strip().capitalize() +                if self.lastCheck: +                    errmsg += " | " + self.lastCheck.group(0).strip() +                self.retry(10, 60, errmsg) + + +    def checkErrors(self): +        if hasattr(self, 'PREMIUM_ONLY_PATTERN') and self.premium and re.search(self.PREMIUM_ONLY_PATTERN, self.html): +            self.fail(_("Link require a premium account to be handled")) + +        if hasattr(self, 'ERROR_PATTERN'): +            m = re.search(self.ERROR_PATTERN, self.html) +            if m: +                errmsg = self.info['error'] = m.group(1) +                self.error(errmsg) -        if status == 1: +        if 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 + +        self.info.pop('error', None) + + +    def checkStatus(self, getinfo=True): +        if getinfo: +            self.updateInfo(self.getInfo(self.pyfile.url, self.html)) + +        status = self.info['status'] + +        if status is 1:              self.offline() -        elif status != 2: -            self.logDebug(self.file_info) -            self.parseError('File info') -        if name: +        elif status is 6: +            self.tempOffline() + +        elif status is not 2: +            self.logDebug("File status: %s" % statusMap[status], +                          "File info: %s"   % self.info) + + +    def checkNameSize(self, getinfo=True): +        if getinfo: +            self.updateInfo(self.getInfo(self.pyfile.url, self.html)) + +        name = self.info['name'] +        size = self.info['size'] +        url  = self.info['url'] + +        if name and name != url:              self.pyfile.name = name          else: -            self.pyfile.name = html_unescape(urlparse(self.pyfile.url).path.split("/")[-1]) +            self.pyfile.name = name = self.info['name'] = urlparse(name).path.split('/')[-1] -        if size: +        if size > 0:              self.pyfile.size = size          else: -            self.logError("File size not parsed") +            size = "Unknown" + +        self.logDebug("File name: %s" % name, +                      "File size: %s" % size) + + +    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 updateInfo(self, info): +        self.logDebug(_("File info (BEFORE): %s") % self.info) +        self.info.update(info) +        self.logDebug(_("File info (AFTER): %s")  % self.info) + + +    def handleDirect(self): +        link = _isDirectLink(self, self.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):  #: Multi-hoster handler +        pass -        self.logDebug("FILE NAME: %s FILE SIZE: %s" % (self.pyfile.name, self.pyfile.size)) -        return self.file_info      def handleFree(self): -        self.fail("Free download not implemented") +        if not hasattr(self, 'LINK_FREE_PATTERN'): +            self.fail(_("Free download not implemented")) + +        try: +            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) + +        except Exception, e: +            self.fail(e) +      def handlePremium(self): -        self.fail("Premium download not implemented") +        if not hasattr(self, 'LINK_PREMIUM_PATTERN'): +            self.fail(_("Premium download not implemented")) + +        try: +            m = re.search(self.LINK_PREMIUM_PATTERN, self.html) +            if m is None: +                self.error(_("Premium download link not found")) + +            self.link = m.group(1) + +        except Exception, e: +            self.fail(e) -    def parseError(self, msg): -        raise PluginParseError(msg)      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) +            time_str  = "%dh %dm" % divmod(wait_time / 60, 60)          else:              wait_time = 900 -            time_str = "(unknown time)" +            time_str  = _("(unknown time)")              max_tries = 100 -        self.logInfo("Download limit reached, reconnect or wait %s" % time_str) +        self.logInfo(_("Download limit reached, reconnect or wait %s") % time_str)          self.setWait(wait_time, True)          self.wait() -        self.retry(max_tries=max_tries, reason="Download limit reached") +        self.retry(max_tries=max_tries, reason=_("Download limit reached")) + -    def parseHtmlForm(self, attr_str='', input_names=None): +    def parseHtmlForm(self, attr_str="", input_names={}):          return parseHtmlForm(attr_str, self.html, input_names) +      def checkTrafficLeft(self): -        traffic = self.account.getAccountInfo(self.user, True)["trafficleft"] -        if traffic == -1: +        traffic = self.account.getAccountInfo(self.user, True)['trafficleft'] + +        if traffic is None: +            return False +        elif traffic == -1:              return True -        size = self.pyfile.size / 1024 -        self.logInfo("Filesize: %i KiB, Traffic left for user %s: %i KiB" % (size, self.user, traffic)) -        return size <= traffic - -    # TODO: Remove in 0.5 -    def wait(self, seconds=False, reconnect=False): -        if seconds: -            self.setWait(seconds, reconnect) -        super(SimpleHoster, self).wait() +        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 + + +    #@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/module/plugins/internal/UnRar.py b/module/plugins/internal/UnRar.py index e3765602b..572fe95b9 100644 --- a/module/plugins/internal/UnRar.py +++ b/module/plugins/internal/UnRar.py @@ -1,132 +1,163 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: RaNaN -""" -  import os  import re -from os.path import join +  from glob import glob -from subprocess import Popen, PIPE +from os.path import basename, dirname, join  from string import digits +from subprocess import Popen, PIPE +from module.plugins.internal.Extractor import Extractor, ArchiveError, CRCError, PasswordError  from module.utils import save_join, decode -from module.plugins.internal.AbstractExtractor import AbtractExtractor, WrongPassword, ArchiveError, CRCError -class UnRar(AbtractExtractor): -    __name__ = "UnRar" -    __version__ = "0.16" +def renice(pid, value): +    if os.name != "nt" and value: +        try: +            Popen(["renice", str(value), str(pid)], stdout=PIPE, stderr=PIPE, bufsize=-1) +        except: +            print "Renice failed" + + +class UnRar(Extractor): +    __name__    = "UnRar" +    __version__ = "1.01" + +    __description__ = """Rar extractor plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + -    # there are some more uncovered rar formats -    re_version = re.compile(r"(UNRAR 5[\.\d]+(.*?)freeware)") -    re_splitfile = re.compile(r"(.*)\.part(\d+)\.rar$", re.I) -    re_partfiles = re.compile(r".*\.(rar|r[0-9]+)", re.I) -    re_filelist = re.compile(r"(.+)\s+(\d+)\s+(\d+)\s+") -    re_filelist5 = re.compile(r"(.+)\s+(\d+)\s+\d\d-\d\d-\d\d\s+\d\d:\d\d\s+(.+)") -    re_wrongpwd = re.compile("(Corrupt file or wrong password|password incorrect)", re.I)      CMD = "unrar" -    @staticmethod -    def checkDeps(): +    EXTENSIONS = ["rar", "zip", "cab", "arj", "lzh", "tar", "gz", "bz2", "ace", "uue", "jar", "iso", "7z", "xz", "z"] + + +    #@NOTE: there are some more uncovered rar formats +    re_rarpart = re.compile(r'(.*)\.part(\d+)\.rar$', re.I) +    re_rarfile = re.compile(r'.*\.(rar|r\d+)$', re.I) + +    re_filelist  = re.compile(r'(.+)\s+(\d+)\s+(\d+)\s+|(.+)\s+(\d+)\s+\d\d-\d\d-\d\d\s+\d\d:\d\d\s+(.+)') +    re_wrongpwd  = re.compile(r'password', re.I) +    re_wrongcrc  = re.compile(r'encrypted|damaged|CRC failed|checksum error', re.I) + + +    @classmethod +    def checkDeps(cls):          if os.name == "nt": -            UnRar.CMD = join(pypath, "UnRAR.exe") -            p = Popen([UnRar.CMD], stdout=PIPE, stderr=PIPE) +            cls.CMD = join(pypath, "UnRAR.exe") +            p = Popen([cls.CMD], stdout=PIPE, stderr=PIPE)              p.communicate()          else:              try: -                p = Popen([UnRar.CMD], stdout=PIPE, stderr=PIPE) +                p = Popen([cls.CMD], stdout=PIPE, stderr=PIPE)                  p.communicate() -            except OSError: +            except OSError:                  # fallback to rar -                UnRar.CMD = "rar" -                p = Popen([UnRar.CMD], stdout=PIPE, stderr=PIPE) +                cls.CMD = "rar" +                p = Popen([cls.CMD], stdout=PIPE, stderr=PIPE)                  p.communicate()          return True -    @staticmethod -    def getTargets(files_ids): -        result = [] + +    @classmethod +    def isArchive(cls, file): +        f = basename(file).lower() +        return any(f.endswith('.%s' % ext) for ext in cls.EXTENSIONS) + + +    @classmethod +    def getTargets(cls, files_ids): +        targets = []          for file, id in files_ids: -            if not file.endswith(".rar"): +            if not cls.isArchive(file):                  continue -            match = UnRar.re_splitfile.findall(file) -            if match: +            m = cls.re_rarpart.findall(file) +            if m:                  # only add first parts -                if int(match[0][1]) == 1: -                    result.append((file, id)) +                if int(m[0][1]) == 1: +                    targets.append((file, id))              else: -                result.append((file, id)) +                targets.append((file, id)) -        return result +        return targets -    def init(self): -        self.passwordProtected = False -        self.headerProtected = False  #: list files will not work without password -        self.smallestFile = None  #: small file to test passwords -        self.password = ""  #: save the correct password -    def checkArchive(self): -        p = self.call_unrar("l", "-v", self.file) -        out, err = p.communicate() -        if self.re_wrongpwd.search(err): -            self.passwordProtected = True -            self.headerProtected = True -            return True +    def check(self, out="", err=""): +        if not out or not err: +            return + +        if err.strip(): +            if self.re_wrongpwd.search(err): +                raise PasswordError + +            elif self.re_wrongcrc.search(err): +                raise CRCError + +            else:  #: raise error if anything is on stderr +                raise ArchiveError(err.strip())          # output only used to check if passworded files are present -        if self.re_version.search(out): -            for attr, size, name in self.re_filelist5.findall(out): -                if attr.startswith("*"): -                    self.passwordProtected = True -                    return True -        else: -            for name, size, packed in self.re_filelist.findall(out): -                if name.startswith("*"): -                    self.passwordProtected = True -                    return True +        for attr in self.re_filelist.findall(out): +            if attr[0].startswith("*"): +                raise PasswordError + + +    def verify(self): +        p = self.call_cmd("l", "-v", self.file, password=self.password) + +        self.check(*p.communicate()) + +        if p and p.returncode: +            raise ArchiveError("Process terminated") -        self.listContent() -        if not self.files: -            raise ArchiveError("Empty Archive") +        if not self.list(): +            raise ArchiveError("Empty archive") + + +    def isPassword(self, password): +        if isinstance(password, basestring): +            p = self.call_cmd("l", "-v", self.file, password=password) +            out, err = p.communicate() + +            if not self.re_wrongpwd.search(err): +                return True          return False -    def checkPassword(self, password): -        # at this point we can only verify header protected files -        if self.headerProtected: -            p = self.call_unrar("l", "-v", self.file, password=password) + +    def repair(self): +        p = self.call_cmd("rc", self.file) +        out, err = p.communicate() + +        if p.returncode or err.strip(): +            p = self.call_cmd("r", self.file)              out, err = p.communicate() -            if self.re_wrongpwd.search(err): + +            if p.returncode or err.strip():                  return False +            else: +                self.file = join(dirname(self.file), re.search(r'(fixed|rebuild)\.%s' % basename(self.file), out).group(0))          return True -    def extract(self, progress, password=None): + +    def extract(self, progress=lambda x: None): +        self.verify() + +        progress(0) +          command = "x" if self.fullpath else "e" -        p = self.call_unrar(command, self.file, self.out, password=password) +        p = self.call_cmd(command, self.file, self.out, password=self.password) +          renice(p.pid, self.renice) -        progress(0)          progressstring = ""          while True:              c = p.stdout.read(1) @@ -134,7 +165,7 @@ class UnRar(AbtractExtractor):              if not c:                  break              # reading a percentage sign -> set progress and restart -            if c == '%': +            if c is '%':                  progress(int(progressstring))                  progressstring = ""              # not reading a digit -> therefore restart @@ -142,42 +173,43 @@ class UnRar(AbtractExtractor):                  progressstring = ""              # add digit to progressstring              else: -                progressstring = progressstring + c +                progressstring += c +          progress(100) +        self.files = self.list() +          # retrieve stderr -        err = p.stderr.read() - -        if "CRC failed" in err and not password and not self.passwordProtected: -            raise CRCError -        elif "CRC failed" in err: -            raise WrongPassword -        if err.strip():  #: raise error if anything is on stderr -            raise ArchiveError(err.strip()) +        self.check(err=p.stderr.read()) +          if p.returncode:              raise ArchiveError("Process terminated") -        if not self.files: -            self.password = password -            self.listContent()      def getDeleteFiles(self): -        if ".part" in self.file: -            return glob(re.sub("(?<=\.part)([01]+)", "*", self.file, re.IGNORECASE)) +        if ".part" in basename(self.file): +            return glob(re.sub("(?<=\.part)([01]+)", "*", self.file, re.I)) +          # get files which matches .r* and filter unsuited files out -        parts = glob(re.sub(r"(?<=\.r)ar$", "*", self.file, re.IGNORECASE)) -        return filter(lambda x: self.re_partfiles.match(x), parts) +        parts = glob(re.sub(r"(?<=\.r)ar$", "*", self.file, re.I)) + +        return filter(lambda x: self.re_rarfile.match(x), parts) + -    def listContent(self): +    def list(self):          command = "vb" if self.fullpath else "lb" -        p = self.call_unrar(command, "-v", self.file, password=self.password) + +        p = self.call_cmd(command, "-v", self.file, password=self.password)          out, err = p.communicate() -        if "Cannot open" in err: -            raise ArchiveError("Cannot open file") +        if err.strip(): +            self.m.logError(err) +            if "Cannot open" in err: +                return list() -        if err.strip():  #: only log error at this point -            self.m.logError(err.strip()) +        if p.returncode: +            self.m.logError("Process terminated") +            return list()          result = set() @@ -185,38 +217,37 @@ class UnRar(AbtractExtractor):              f = f.strip()              result.add(save_join(self.out, f)) -        self.files = result +        return list(result) -    def call_unrar(self, command, *xargs, **kwargs): + +    def call_cmd(self, command, *xargs, **kwargs):          args = [] +          # overwrite flag -        args.append("-o+") if self.overwrite else args.append("-o-") +        if self.overwrite: +            args.append("-o+") +        else: +            args.append("-o-") +            if self.delete: +                args.append("-or") -        if self.excludefiles: -            for word in self.excludefiles.split(';'): -                args.append("-x%s" % word) +        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"]) +        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.m.logDebug(" ".join(call)) -        p = Popen(call, stdout=PIPE, stderr=PIPE) - -        return p - - -def renice(pid, value): -    if os.name != "nt" and value: -        try: -            Popen(["renice", str(value), str(pid)], stdout=PIPE, stderr=PIPE, bufsize=-1) -        except: -            print "Renice failed" +        return Popen(call, stdout=PIPE, stderr=PIPE) diff --git a/module/plugins/internal/UnZip.py b/module/plugins/internal/UnZip.py index 501962442..5ec56cbdf 100644 --- a/module/plugins/internal/UnZip.py +++ b/module/plugins/internal/UnZip.py @@ -1,50 +1,86 @@  # -*- coding: utf-8 -*- -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. +from __future__ import with_statement -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. +import sys +import zipfile -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. +from module.plugins.internal.Extractor import Extractor, ArchiveError, CRCError, PasswordError -    @author: RaNaN -""" -import zipfile -import sys +class UnZip(Extractor): +    __name__    = "UnZip" +    __version__ = "1.01" + +    __description__ = """Zip extractor plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] -from module.plugins.internal.AbstractExtractor import AbtractExtractor +    EXTENSIONS = ["zip", "zip64"] -class UnZip(AbtractExtractor): -    __name__ = "UnZip" -    __version__ = "0.1" -    @staticmethod -    def checkDeps(): +    @classmethod +    def checkDeps(cls):          return sys.version_info[:2] >= (2, 6) -    @staticmethod -    def getTargets(files_ids): -        result = [] -        for file, id in files_ids: -            if file.endswith(".zip"): -                result.append((file, id)) +    @classmethod +    def isArchive(cls, file): +        return zipfile.is_zipfile(file) + + +    def verify(self): +        try: +            with zipfile.ZipFile(self.file, 'r', allowZip64=True) as z: +                z.setpassword(self.password) +                badcrc = z.testzip() + +        except (BadZipfile, LargeZipFile), e: +            raise ArchiveError(e) + +        except RuntimeError, e: +            if 'encrypted' in e: +                raise PasswordError +            else: +                raise ArchiveError(e) + +        else: +            if badcrc: +                raise CRCError + +        if not self.list(): +            raise ArchiveError("Empty archive") + + +    def list(self): +        try: +            with zipfile.ZipFile(self.file, 'r', allowZip64=True) as z: +                z.setpassword(self.password) +                return z.namelist() +        except Exception: +            return list() + + +    def extract(self, progress=lambda x: None): +        try: +            with zipfile.ZipFile(self.file, 'r', allowZip64=True) as z: +                progress(0) +                z.extractall(self.out, pwd=self.password) +                progress(100) + +        except (BadZipfile, LargeZipFile), e: +            raise ArchiveError(e) + +        except RuntimeError, e: +            if e is "Bad password for file": +                raise PasswordError +            else: +                raise ArchiveError(e) -        return result +        finally: +            self.files = self.list() -    def extract(self, progress, password=None): -        z = zipfile.ZipFile(self.file) -        self.files = z.namelist() -        z.extractall(self.out)      def getDeleteFiles(self):          return [self.file] diff --git a/module/plugins/internal/XFSAccount.py b/module/plugins/internal/XFSAccount.py new file mode 100644 index 000000000..2784ecd0b --- /dev/null +++ b/module/plugins/internal/XFSAccount.py @@ -0,0 +1,160 @@ +# -*- coding: utf-8 -*- + +import re + +from time import gmtime, mktime, strptime +from urlparse import urljoin + +from module.plugins.Account import Account +from module.plugins.internal.SimpleHoster import parseHtmlForm, set_cookies + + +class XFSAccount(Account): +    __name__    = "XFSAccount" +    __type__    = "account" +    __version__ = "0.33" + +    __description__ = """XFileSharing account plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = None +    HOSTER_URL    = None + +    COOKIES = [(HOSTER_DOMAIN, "lang", "english")] + +    PREMIUM_PATTERN = r'\(Premium only\)' + +    VALID_UNTIL_PATTERN = r'Premium.[Aa]ccount expire:.*?(\d{1,2} [\w^_]+ \d{4})' + +    TRAFFIC_LEFT_PATTERN = r'Traffic available today:.*?<b>\s*(?P<S>[\d.,]+|[Uu]nlimited)\s*(?:(?P<U>[\w^_]+)\s*)?</b>' +    TRAFFIC_LEFT_UNIT    = "MB"  #: used only if no group <U> was found + +    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'>\s*(Incorrect Login or Password|Error<)' + + +    def __init__(self, manager, accounts):  #@TODO: remove in 0.4.10 +        self.init() +        return super(XFSAccount, self).__init__(manager, accounts) + + +    def init(self): +        if not self.HOSTER_DOMAIN: +            self.logError(_("Missing HOSTER_DOMAIN")) + +        if not self.HOSTER_URL: +            self.HOSTER_URL = "http://www.%s/" % self.HOSTER_DOMAIN or "" + + +    def loadAccountInfo(self, user, req): +        validuntil   = None +        trafficleft  = None +        leechtraffic = None +        premium      = None + +        html = req.load(self.HOSTER_URL, get={'op': "my_account"}, decode=True) + +        premium = True if re.search(self.PREMIUM_PATTERN, html) else False + +        m = re.search(self.VALID_UNTIL_PATTERN, html) +        if m: +            expiredate = m.group(1).strip() +            self.logDebug("Expire date: " + expiredate) + +            try: +                validuntil = mktime(strptime(expiredate, "%d %B %Y")) + +            except Exception, e: +                self.logError(e) + +            else: +                self.logDebug("Valid until: %s" % validuntil) + +                if validuntil > mktime(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 isinstance(self.COOKIES, list): +            set_cookies(req.cj, self.COOKIES) + +        url = urljoin(self.HOSTER_URL, "login.html") +        html = req.load(url, decode=True) + +        action, inputs = parseHtmlForm('name="FL"', html) +        if not inputs: +            inputs = {'op': "login", +                      'redirect': self.HOSTER_URL} + +        inputs.update({'login': user, +                       'password': data['password']}) + +        html = req.load(self.HOSTER_URL, post=inputs, decode=True) + +        if re.search(self.LOGIN_FAIL_PATTERN, html): +            self.wrongPassword() diff --git a/module/plugins/internal/XFSCrypter.py b/module/plugins/internal/XFSCrypter.py new file mode 100644 index 000000000..4b57dab90 --- /dev/null +++ b/module/plugins/internal/XFSCrypter.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo + + +class XFSCrypter(SimpleCrypter): +    __name__    = "XFSCrypter" +    __type__    = "crypter" +    __version__ = "0.05" + +    __pattern__ = r'^unmatchable$' + +    __description__ = """XFileSharing decrypter plugin""" +    __license__     = "GPLv3" +    __authors__     = [("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = None +    HOSTER_NAME = None + +    URL_REPLACEMENTS = [(r'&?per_page=\d+', ""), (r'[?/&]+$', ""), (r'(.+/[^?]+)$', r'\1?'), (r'$', r'&per_page=10000')] + +    COOKIES = [(HOSTER_DOMAIN, "lang", "english")] + +    LINK_PATTERN = r'<(?:td|TD).*?>\s*<a href="(.+?)".*?>.+?(?:</a>)?\s*</(?:td|TD)>' +    NAME_PATTERN = r'<[tT]itle>.*?\: (?P<N>.+) folder</[tT]itle>' + +    OFFLINE_PATTERN      = r'>\s*\w+ (Not Found|file (was|has been) removed)' +    TEMP_OFFLINE_PATTERN = r'>\s*\w+ server (is in )?(maintenance|maintainance)' diff --git a/module/plugins/internal/XFSHoster.py b/module/plugins/internal/XFSHoster.py new file mode 100644 index 000000000..4555e7b0f --- /dev/null +++ b/module/plugins/internal/XFSHoster.py @@ -0,0 +1,341 @@ +# -*- coding: utf-8 -*- + +import re + +from random import random +from time import sleep + +from pycurl import FOLLOWLOCATION, LOW_SPEED_TIME + +from module.plugins.internal.CaptchaService import ReCaptcha, SolveMedia +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, secondsToMidnight +from module.utils import html_unescape + + +class XFSHoster(SimpleHoster): +    __name__    = "XFSHoster" +    __type__    = "hoster" +    __version__ = "0.33" + +    __pattern__ = r'^unmatchable$' + +    __description__ = """XFileSharing hoster plugin""" +    __license__     = "GPLv3" +    __authors__     = [("zoidberg", "zoidberg@mujmail.cz"), +                       ("stickell", "l.stickell@yahoo.it"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + + +    HOSTER_DOMAIN = None +    HOSTER_NAME   = None + +    TEXT_ENCODING = False +    COOKIES       = [(HOSTER_DOMAIN, "lang", "english")] +    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 +        self.resumeDownload = self.multiDL = self.premium + + +    def prepare(self): +        """ Initialize important variables """ +        if not self.HOSTER_DOMAIN: +            self.fail(_("Missing HOSTER_DOMAIN")) + +        if not self.HOSTER_NAME: +            self.HOSTER_NAME = "".join([str.capitalize() for str in self.HOSTER_DOMAIN.split('.')]) + +        if not self.LINK_PATTERN: +            pattern = r'(https?://(www\.)?([^/]*?%s|\d+\.\d+\.\d+\.\d+)(\:\d+)?(/d/|(/files)?/\d+/\w+/).+?)["\'<]' +            self.LINK_PATTERN = pattern % self.HOSTER_DOMAIN.replace('.', '\.') + +        self.captcha = None +        self.errmsg  = None + +        super(XFSHoster, self).prepare() + +        if self.DIRECT_LINK is None: +            self.directDL = bool(self.premium) + + +    def handleFree(self): +        link = self.getDownloadLink() + +        if link: +            if self.captcha: +                self.correctCaptcha() + +            self.download(link, ref=True, cookies=True, disposition=True) + +        elif self.errmsg: +            if 'captcha' in self.errmsg: +                self.fail(_("No valid captcha code entered")) +            else: +                self.fail(self.errmsg) + +        else: +            self.fail(_("Download link not found")) + + +    def handlePremium(self): +        return self.handleFree() + + +    def getDownloadLink(self): +        for i in xrange(1, 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.req.http.c.setopt(FOLLOWLOCATION, 0) + +            self.html = self.load(self.pyfile.url, post=data, ref=True, decode=True) + +            self.req.http.c.setopt(FOLLOWLOCATION, 1) + +            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.errmsg = None + +        return m.group(1).strip()  #@TODO: Remove .strip() in 0.4.10 + + +    def handleMulti(self): +        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'] = self.pyfile.url +        inputs['up1oad_type'] = 'url' + +        self.logDebug(action, inputs) + +        self.req.setOption("timeout", 600)  #: wait for file to upload to easybytez.com + +        self.html = self.load(action, post=inputs) + +        self.checkErrors() + +        action, inputs = self.parseHtmlForm('F1') +        if not inputs: +            if self.errmsg: +                self.retry(reason=self.errmsg) +            else: +                self.error(_("TEXTAREA F1 not found")) + +        self.logDebug(inputs) + +        stmsg = inputs['st'] + +        if stmsg == 'OK': +            self.html = self.load(action, post=inputs) + +        elif 'Can not leech file' in stmsg: +            self.retry(20, 3 * 60, _("Can not leech file")) + +        elif 'today' in stmsg: +            self.retry(wait_time=secondsToMidnight(gmt=2), reason=_("You've used all Leech traffic today")) + +        else: +            self.fail(stmsg) + +        #get easybytez.com link for uploaded file +        m = re.search(self.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'] +        else: +            self.fail(_("Download link not found")) + + +    def checkErrors(self): +        m = re.search(self.ERROR_PATTERN, self.html) +        if m is None: +            self.errmsg = None +        else: +            self.errmsg = m.group(1).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}[u.lower()] for v, u in +                                 re.findall(r'(\d+)\s*(hr|hour|min|sec)', self.errmsg, re.I)]) +                self.wait(wait_time, True) + +            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: +            captcha_key = recaptcha.detect_key() +        else: +            self.logDebug("ReCaptcha key: %s" % captcha_key) + +        if captcha_key: +            inputs['recaptcha_challenge_field'], inputs['recaptcha_response_field'] = recaptcha.challenge(captcha_key) +            return 3 + +        solvemedia = SolveMedia(self) +        try: +            captcha_key = re.search(self.SOLVEMEDIA_PATTERN, self.html).group(1) +        except: +            captcha_key = solvemedia.detect_key() +        else: +            self.logDebug("SolveMedia key: %s" % captcha_key) + +        if captcha_key: +            inputs['adcopy_challenge'], inputs['adcopy_response'] = solvemedia.challenge(captcha_key) +            return 4 + +        return 0 diff --git a/module/plugins/internal/XFSPAccount.py b/module/plugins/internal/XFSPAccount.py deleted file mode 100644 index 76aff54f0..000000000 --- a/module/plugins/internal/XFSPAccount.py +++ /dev/null @@ -1,82 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -    This program is free software; you can redistribute it and/or modify -    it under the terms of the GNU General Public License as published by -    the Free Software Foundation; either version 3 of the License, -    or (at your option) any later version. - -    This program is distributed in the hope that it will be useful, -    but WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -    See the GNU General Public License for more details. - -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. - -    @author: zoidberg -""" - -import re -from time import mktime, strptime -from module.plugins.Account import Account -from module.plugins.internal.SimpleHoster import parseHtmlForm -from module.utils import parseFileSize - - -class XFSPAccount(Account): -    __name__ = "XFSPAccount" -    __version__ = "0.06" -    __type__ = "account" -    __description__ = """XFileSharingPro base account plugin""" -    __author_name__ = "zoidberg" -    __author_mail__ = "zoidberg@mujmail.cz" - -    MAIN_PAGE = None - -    VALID_UNTIL_PATTERN = r'>Premium.[Aa]ccount expire:</TD><TD><b>([^<]+)</b>' -    TRAFFIC_LEFT_PATTERN = r'>Traffic available today:</TD><TD><b>([^<]+)</b>' -    LOGIN_FAIL_PATTERN = r'Incorrect Login or Password|>Error<' -    PREMIUM_PATTERN = r'>Renew premium<' - -    def loadAccountInfo(self, user, req): -        html = req.load(self.MAIN_PAGE + "?op=my_account", decode=True) - -        validuntil = trafficleft = None -        premium = True if re.search(self.PREMIUM_PATTERN, html) else False - -        found = re.search(self.VALID_UNTIL_PATTERN, html) -        if found: -            premium = True -            trafficleft = -1 -            try: -                self.logDebug(found.group(1)) -                validuntil = mktime(strptime(found.group(1), "%d %B %Y")) -            except Exception, e: -                self.logError(e) -        else: -            found = re.search(self.TRAFFIC_LEFT_PATTERN, html) -            if found: -                trafficleft = found.group(1) -                if "Unlimited" in trafficleft: -                    premium = True -                else: -                    trafficleft = parseFileSize(trafficleft) / 1024 - -        return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium} - -    def login(self, user, data, req): -        html = req.load('%slogin.html' % self.MAIN_PAGE, decode=True) - -        action, inputs = parseHtmlForm('name="FL"', html) -        if not inputs: -            inputs = {"op": "login", -                      "redirect": self.MAIN_PAGE} - -        inputs.update({"login": user, -                       "password": data['password']}) - -        html = req.load(self.MAIN_PAGE, post=inputs, decode=True) - -        if re.search(self.LOGIN_FAIL_PATTERN, html): -            self.wrongPassword()  | 
