diff options
| author | 2013-04-13 21:06:34 +0200 | |
|---|---|---|
| committer | 2013-04-13 21:06:34 +0200 | |
| commit | 0b2e9ddcafed94fb780ea8d07ea23f6f14612830 (patch) | |
| tree | 36f60fb82cc61cf17947cbe7dcbbc719994e234c /module | |
| parent | Cleanup. (diff) | |
| parent | MBLinkInfo: updated pattern (diff) | |
| download | pyload-0b2e9ddcafed94fb780ea8d07ea23f6f14612830.tar.xz | |
Merge branch 'stable' of git://github.com/pyload/pyload into stable
Diffstat (limited to 'module')
32 files changed, 968 insertions, 385 deletions
| diff --git a/module/plugins/accounts/DebridItaliaCom.py b/module/plugins/accounts/DebridItaliaCom.py index d68f1c8a8..91dd3787f 100644 --- a/module/plugins/accounts/DebridItaliaCom.py +++ b/module/plugins/accounts/DebridItaliaCom.py @@ -1,5 +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/>.    # +############################################################################ +  import re  import _strptime  import time diff --git a/module/plugins/accounts/MultiDebridCom.py b/module/plugins/accounts/MultiDebridCom.py new file mode 100644 index 000000000..904be5ee7 --- /dev/null +++ b/module/plugins/accounts/MultiDebridCom.py @@ -0,0 +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/>.    # +############################################################################ + +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'] * 86400) +            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/RarefileNet.py b/module/plugins/accounts/RarefileNet.py index 57f293c55..90ad02d43 100644 --- a/module/plugins/accounts/RarefileNet.py +++ b/module/plugins/accounts/RarefileNet.py @@ -3,10 +3,10 @@ from module.plugins.internal.XFSPAccount import XFSPAccount  class RarefileNet(XFSPAccount):      __name__ = "RarefileNet" -    __version__ = "0.01" +    __version__ = "0.02"      __type__ = "account"      __description__ = """RareFile.net account plugin"""      __author_name__ = ("zoidberg")      __author_mail__ = ("zoidberg@mujmail.cz") -    MAIN_PAGE = "http://rarefile.in/"
\ No newline at end of file +    MAIN_PAGE = "http://rarefile.net/" diff --git a/module/plugins/crypter/DataHuFolder.py b/module/plugins/crypter/DataHuFolder.py new file mode 100644 index 000000000..f710f60d7 --- /dev/null +++ b/module/plugins/crypter/DataHuFolder.py @@ -0,0 +1,55 @@ +# -*- 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 + + +class DataHuFolder(SimpleCrypter): +    __name__ = "DataHuFolder" +    __type__ = "crypter" +    __pattern__ = r"http://(www\.)?data.hu/dir/\w+" +    __version__ = "0.03" +    __description__ = """Data.hu Folder Plugin""" +    __author_name__ = ("crash", "stickell") +    __author_mail__ = ("l.stickell@yahoo.it") + +    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) + +        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") + +        package_name, folder_name = self.getPackageNameAndFolder() + +        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') diff --git a/module/plugins/crypter/GooGl.py b/module/plugins/crypter/GooGl.py new file mode 100644 index 000000000..bcb1d7494 --- /dev/null +++ b/module/plugins/crypter/GooGl.py @@ -0,0 +1,41 @@ +# -*- 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+" +    __version__ = "0.01" +    __description__ = """Goo.gl Crypter Plugin""" +    __author_name__ = ("stickell") +    __author_mail__ = ("l.stickell@yahoo.it") + +    API_URL = 'https://www.googleapis.com/urlshortener/v1/url' + +    def decrypt(self, pyfile): +        rep = self.load(self.API_URL, get={'shortUrl': pyfile.url}) +        self.logDebug('JSON data: ' + rep) +        rep = json_loads(rep) + +        if 'longUrl' in rep: +            self.core.files.addLinks([rep['longUrl']], self.pyfile.package().id) +        else: +            self.fail('Unable to expand shortened link') diff --git a/module/plugins/crypter/MBLinkInfo.py b/module/plugins/crypter/MBLinkInfo.py new file mode 100644 index 000000000..e266c7722 --- /dev/null +++ b/module/plugins/crypter/MBLinkInfo.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- + +import re + +from module.plugins.Crypter import Crypter + + +class MBLinkInfo(Crypter): +    __name__ = "MBLinkInfo" +    __type__ = "container" +    __pattern__ = r"http://(?:www\.)?mblink\.info/?\?id=(\d+)" +    __version__ = "0.02" +    __description__ = """MBLink.Info Container Plugin""" +    __author_name__ = ("Gummibaer", "stickell") +    __author_mail__ = ("Gummibaer@wiki-bierkiste.de", "l.stickell@yahoo.it") + +    URL_PATTERN = r'<meta[^;]+; URL=(.*)["\']>' + +    def decrypt(self, pyfile): +        src = self.load(pyfile.url) +        found = re.search(self.URL_PATTERN, src) +        if found: +            link = found.group(1) +            self.logDebug("Redirected to " + link) +            self.core.files.addLinks([link], self.pyfile.package().id) +        else: +            self.fail('Unable to detect valid link') diff --git a/module/plugins/crypter/NetfolderIn.py b/module/plugins/crypter/NetfolderIn.py index d71a73d0a..c5c602c27 100644 --- a/module/plugins/crypter/NetfolderIn.py +++ b/module/plugins/crypter/NetfolderIn.py @@ -1,22 +1,25 @@  # -*- coding: utf-8 -*- -from module.plugins.Crypter import Crypter  import re -class NetfolderIn(Crypter): +from module.plugins.internal.SimpleCrypter import SimpleCrypter + + +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.4" +    __version__ = "0.6"      __description__ = """NetFolder Crypter Plugin"""      __author_name__ = ("RaNaN", "fragonib")      __author_mail__ = ("RaNaN@pyload.org", "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) -         +          # Check for password protection              if self.isPasswordProtected():              self.html = self.submitPassword() @@ -31,54 +34,37 @@ class NetfolderIn(Crypter):          # 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'))  +            id = max(m.group('id1'), m.group('id2'))          except AttributeError:              self.logDebug("Unable to get package id from url [%s]" % self.pyfile.url)              return          url = "http://netfolder.in/folder.php?folder_id=" + id          password = self.getPassword() -                    +          # Submit package password      -        post = { 'password' : password, 'save' : 'Absenden' } +        post = {'password': password, 'save': 'Absenden'}          self.logDebug("Submitting password [%s] for protected links with id [%s]" % (password, id))          html = self.load(url, {}, post) -         +          # Check for invalid password          if '<div class="InPage_Error">' in html:              self.logDebug("Incorrect password, please set right password on Edit package form and retry")              return None -         -        return html  -     -     -    def getPackageNameAndFolder(self): -        title_re = r'<div class="Text">Inhalt des Ordners <span(.*)>(?P<title>.+)</span></div>' -        m = re.search(title_re, self.html) -        if m is not None: -            name = folder = m.group('title') -            self.logDebug("Found name [%s] and folder [%s] in package info" % (name, folder)) -            return name, folder -        else: -            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 -         -         + +        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)) diff --git a/module/plugins/crypter/SpeedLoadOrgFolder.py b/module/plugins/crypter/SpeedLoadOrgFolder.py index 5b350787f..f85ede6f3 100644 --- a/module/plugins/crypter/SpeedLoadOrgFolder.py +++ b/module/plugins/crypter/SpeedLoadOrgFolder.py @@ -1,5 +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  class SpeedLoadOrgFolder(SimpleCrypter): diff --git a/module/plugins/crypter/UlozToFolder.py b/module/plugins/crypter/UlozToFolder.py index c6672ea8c..814d5240d 100644 --- a/module/plugins/crypter/UlozToFolder.py +++ b/module/plugins/crypter/UlozToFolder.py @@ -7,7 +7,7 @@ class UlozToFolder(Crypter):      __name__ = "UlozToFolder"      __type__ = "crypter"      __pattern__ = r"http://.*(uloz\.to|ulozto\.(cz|sk|net)|bagruj.cz|zachowajto.pl)/(m|soubory)/.*" -    __version__ = "0.1a" +    __version__ = "0.2"      __description__ = """Uloz.to Folder Plugin"""      __author_name__ = ("zoidberg")      __author_mail__ = ("zoidberg@mujmail.cz") diff --git a/module/plugins/crypter/UploadedToFolder.py b/module/plugins/crypter/UploadedToFolder.py index d4534297e..c514f23d0 100644 --- a/module/plugins/crypter/UploadedToFolder.py +++ b/module/plugins/crypter/UploadedToFolder.py @@ -1,13 +1,30 @@  # -*- coding: utf-8 -*-
 -from module.plugins.Crypter import Crypter
 +############################################################################
 +# 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
 -class UploadedToFolder(Crypter):
 +from module.plugins.internal.SimpleCrypter import SimpleCrypter
 +
 +
 +class UploadedToFolder(SimpleCrypter):
      __name__ = "UploadedToFolder"
      __type__ = "crypter"
 -    __pattern__ = r"http://(?:www\.)?(uploaded|ul)\.(to|net)/(f|list)/(?P<id>\w+)"
 -    __version__ = "0.1"
 +    __pattern__ = r"http://(?:www\.)?(uploaded|ul)\.(to|net)/(f|folder|list)/(?P<id>\w+)"
 +    __version__ = "0.3"
      __description__ = """UploadedTo Crypter Plugin"""
      __author_name__ = ("stickell")
      __author_mail__ = ("l.stickell@yahoo.it")
 @@ -31,15 +48,3 @@ class UploadedToFolder(Crypter):          self.logDebug('Package has %d links' % len(package_links))
          self.packages = [(package_name, package_links, folder_name)]
 -
 -    def getPackageNameAndFolder(self):
 -        m = re.search(self.TITLE_PATTERN, self.html)
 -        if m:
 -            name = folder = m.group('title')
 -            self.logDebug("Found name [%s] and folder [%s] in package info" % (name, folder))
 -            return name, folder
 -        else:
 -            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
 diff --git a/module/plugins/crypter/YoutubeBatch.py b/module/plugins/crypter/YoutubeBatch.py index 2e68dfe02..567191bb9 100644 --- a/module/plugins/crypter/YoutubeBatch.py +++ b/module/plugins/crypter/YoutubeBatch.py @@ -2,26 +2,41 @@  # -*- coding: utf-8 -*-  import re +import json  from module.plugins.Crypter import Crypter +API_KEY = "AIzaSyCKnWLNlkX-L4oD1aEzqqhRw1zczeD6_k0" +  class YoutubeBatch(Crypter):      __name__ = "YoutubeBatch"      __type__ = "container" -    __pattern__ = r"http://(?:[^/]*?)youtube\.com/((?:view_play_list|playlist|.*?feature=PlayList).*?[\?&](?:list|p)=|user/)(\w+)" +    __pattern__ = r"http://(?:[^/]*?)youtube\.com/((?:view_play_list|playlist|.*?feature=PlayList).*?[\?&](?:list|p)=)([a-zA-Z0-9-_]+)"      __version__ = "0.92"      __description__ = """Youtube.com Channel Download Plugin""" -    __author_name__ = ("RaNaN", "Spoob", "zoidberg") -    __author_mail__ = ("RaNaN@pyload.org", "spoob@pyload.org", "zoidberg@mujmail.cz") +    __author_name__ = ("RaNaN", "Spoob", "zoidberg", "roland") +    __author_mail__ = ("RaNaN@pyload.org", "spoob@pyload.org", "zoidberg@mujmail.cz", "roland@enkore.de") + +    def get_videos(self, playlist_id, token=None): +        url = "https://www.googleapis.com/youtube/v3/playlistItems?playlistId=%s&part=snippet&key=%s&maxResults=50" % (playlist_id, API_KEY) +        if token: +            url += "&pageToken=" + token + +        response = json.loads(self.load(url)) + +        for item in response["items"]: +            if item["kind"] == "youtube#playlistItem" and item["snippet"]["resourceId"]["kind"] == "youtube#video": +                yield "http://youtube.com/watch?v=" + item["snippet"]["resourceId"]["videoId"] + +        if "nextPageToken" in response: +            for item in self.get_videos(playlist_id, response["nextPageToken"]): +                yield item      def decrypt(self, pyfile):          match_id = re.match(self.__pattern__, self.pyfile.url) -        if match_id.group(1) == "user/": -            url = "http://gdata.youtube.com/feeds/api/users/%s/uploads?v=2" % match_id.group(2) -        else: -            url = "http://gdata.youtube.com/feeds/api/playlists/%s?v=2" % match_id.group(2) - -        rep = self.load(url)          new_links = [] -        new_links.extend(re.findall(r"href\='(http:\/\/www.youtube.com\/watch\?v\=[^']+)&", rep)) +        playlist_id = match_id.group(2) + +        new_links.extend(self.get_videos(playlist_id)) +          self.packages.append((self.pyfile.package().name, new_links, self.pyfile.package().name)) diff --git a/module/plugins/hooks/DebridItaliaCom.py b/module/plugins/hooks/DebridItaliaCom.py index 8cd997f4d..d570eebe3 100644 --- a/module/plugins/hooks/DebridItaliaCom.py +++ b/module/plugins/hooks/DebridItaliaCom.py @@ -1,11 +1,26 @@  # -*- 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.01" +    __version__ = "0.03"      __type__ = "hook"      __config__ = [("activated", "bool", "Activated", "False"),                    ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), @@ -22,4 +37,5 @@ class DebridItaliaCom(MultiHoster):                  "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"] +                "depositfiles.com", "extabit.com", "filefactory.com", "sharefiles.co", +                "ryushare.com", "tusfiles.net", "nowvideo.co"] diff --git a/module/plugins/hooks/DownloadScheduler.py b/module/plugins/hooks/DownloadScheduler.py index 7cadede38..4049d71c5 100644 --- a/module/plugins/hooks/DownloadScheduler.py +++ b/module/plugins/hooks/DownloadScheduler.py @@ -18,61 +18,69 @@  import re  from time import localtime +  from module.plugins.Hook import Hook +  class DownloadScheduler(Hook):      __name__ = "DownloadScheduler" -    __version__ = "0.20" +    __version__ = "0.21"      __description__ = """Download Scheduler""" -    __config__ = [("activated", "bool", "Activated", "False"),                   -                  ("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")] -    __author_name__ = ("zoidberg") -    __author_mail__ = ("zoidberg@mujmail.cz") -     +    __config__ = [("activated", "bool", "Activated", "False"), +                  ("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") +      def setup(self): -        self.cb = None # callback to scheduler job; will be by removed hookmanager when hook unloaded        -     +        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")                    -         -        schedule = re.findall("(\d{1,2}):(\d{2})[\s]*(-?\d+)", schedule.lower().replace("full", "-1").replace("none", "0"))          + +    def updateSchedule(self, schedule=None): +        if schedule is None: +            schedule = self.getConfig("timetable") + +        schedule = re.findall("(\d{1,2}):(\d{2})[\s]*(-?\d+)", +                              schedule.lower().replace("full", "-1").replace("none", "0"))          if not schedule:              self.logError("Invalid schedule")              return -         +          t0 = localtime()          now = (t0.tm_hour, t0.tm_min, t0.tm_sec, "X")          schedule = sorted([(int(x[0]), int(x[1]), 0, int(x[2])) for x in schedule] + [now]) -     -        self.logDebug("Schedule", schedule)       -       + +        self.logDebug("Schedule", schedule) +          for i, v in enumerate(schedule):              if v[3] == "X": -                last, next = schedule[i-1], schedule[(i+1) % len(schedule)] +                last, next = schedule[i - 1], schedule[(i + 1) % len(schedule)]                  self.logDebug("Now/Last/Next", now, last, next) -                  -                self.setDownloadSpeed(last[3])  -                 -                next_time = (((24 + next[0] - now[0])* 60 + next[1] - now[1]) * 60 + next[2] - now[2]) % 86400 + +                self.setDownloadSpeed(last[3]) + +                next_time = (((24 + next[0] - now[0]) * 60 + next[1] - now[1]) * 60 + next[2] - now[2]) % 86400                  self.core.scheduler.removeJob(self.cb) -                self.cb = self.core.scheduler.addJob(next_time, self.updateSchedule, threaded=False)                                      -                 -    def setDownloadSpeed(self, speed):      +                self.cb = self.core.scheduler.addJob(next_time, self.updateSchedule, threaded=False) + +    def setDownloadSpeed(self, speed):          if speed == 0: -            self.logInfo("Stopping download server. (Running downloads will not be aborted.)") +            abort = self.getConfig("abort") +            self.logInfo("Stopping download server. (Running downloads will %sbe aborted.)" % ('' if abort else 'not '))              self.core.api.pauseServer() +            if abort: +                self.core.api.stopAllDownloads()          else:              self.core.api.unpauseServer() -             +              if speed > 0:                  self.logInfo("Setting download speed to %d kB/s" % speed) -                self.core.api.setConfigValue("download","limit_speed",1) -                self.core.api.setConfigValue("download","max_speed",speed) +                self.core.api.setConfigValue("download", "limit_speed", 1) +                self.core.api.setConfigValue("download", "max_speed", speed)              else:                  self.logInfo("Setting download speed to FULL") -                self.core.api.setConfigValue("download","limit_speed",0) -                self.core.api.setConfigValue("download","max_speed",-1)
\ No newline at end of file +                self.core.api.setConfigValue("download", "limit_speed", 0) +                self.core.api.setConfigValue("download", "max_speed", -1) diff --git a/module/plugins/hooks/LinkdecrypterCom.py b/module/plugins/hooks/LinkdecrypterCom.py index 2cb91d120..d3d6bce68 100644 --- a/module/plugins/hooks/LinkdecrypterCom.py +++ b/module/plugins/hooks/LinkdecrypterCom.py @@ -24,7 +24,7 @@ from module.utils import remove_chars  class LinkdecrypterCom(Hook):      __name__ = "LinkdecrypterCom" -    __version__ = "0.16" +    __version__ = "0.17"      __description__ = """linkdecrypter.com - regexp loader"""      __config__ = [ ("activated", "bool", "Activated" , "True") ]      __author_name__ = ("zoidberg") @@ -50,7 +50,7 @@ class LinkdecrypterCom(Hook):              self.logError(_("Crypter list is empty"))              return -        regexp = r"http://([^.]+\.)*?(%s)/.*" % "|".join(online) +        regexp = r"https?://([^.]+\.)*?(%s)/.*" % "|".join(online)          dict = self.core.pluginManager.crypterPlugins[self.__name__]          dict["pattern"] = regexp diff --git a/module/plugins/hooks/MultiDebridCom.py b/module/plugins/hooks/MultiDebridCom.py new file mode 100644 index 000000000..c95138648 --- /dev/null +++ b/module/plugins/hooks/MultiDebridCom.py @@ -0,0 +1,42 @@ +# -*- 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/hoster/ARD.py b/module/plugins/hoster/ARD.py new file mode 100644 index 000000000..5ab65cd4b --- /dev/null +++ b/module/plugins/hoster/ARD.py @@ -0,0 +1,80 @@ + +import subprocess +import re +import os.path +import os + +from module.utils import save_join, save_path +from module.plugins.Hoster import Hoster + +# Requires rtmpdump +# by Roland Beermann + +class RTMP: +    # TODO: Port to some RTMP-library like rtmpy or similar +    # TODO?: Integrate properly into the API of pyLoad + +    command = "rtmpdump" + +    @classmethod +    def download_rtmp_stream(cls, url, output_file, playpath=None): +        opts = [ +            "-r", url, +            "-o", output_file, +        ] +        if playpath: +            opts.append("--playpath") +            opts.append(playpath) + +        cls._invoke_rtmpdump(opts) + +    @classmethod +    def _invoke_rtmpdump(cls, opts): +        args = [ +            cls.command +        ] +        args.extend(opts) + +        return subprocess.check_call(args) + +class ARD(Hoster): +    __name__ = "ARD Mediathek" +    __version__ = "0.1" +    __pattern__ = r"http://www\.ardmediathek\.de/.*" +    __config__ = [] + +    def process(self, pyfile): +        site = self.load(pyfile.url) + +        avail_videos = re.findall(r"""mediaCollection.addMediaStream\(0, ([0-9]*), "([^\"]*)", "([^\"]*)", "[^\"]*"\);""", site) +        avail_videos.sort(key=lambda videodesc: int(videodesc[0]), reverse=True) # The higher the number, the better the quality + +        quality, url, playpath = avail_videos[0] + +        pyfile.name = re.search(r"<h1>([^<]*)</h1>", site).group(1) + +        if url.startswith("http"): +            # Best quality is available over HTTP. Very rare. +            self.download(url) +        else: +            pyfile.setStatus("downloading") + +            download_folder = self.config['general']['download_folder'] + +            location = save_join(download_folder, pyfile.package().folder) + +            if not os.path.exists(location): +                os.makedirs(location, int(self.core.config["permission"]["folder"], 8)) + +                if self.core.config["permission"]["change_dl"] and os.name != "nt": +                    try: +                        uid = getpwnam(self.config["permission"]["user"])[2] +                        gid = getgrnam(self.config["permission"]["group"])[2] + +                        chown(location, uid, gid) +                    except Exception, e: +                        self.log.warning(_("Setting User and Group failed: %s") % str(e)) + +            output_file = save_join(location, save_path(pyfile.name)) + os.path.splitext(playpath)[1] + +            RTMP.download_rtmp_stream(url, playpath=playpath, output_file=output_file) diff --git a/module/plugins/hoster/DataHu.py b/module/plugins/hoster/DataHu.py new file mode 100644 index 000000000..7abd93d1f --- /dev/null +++ b/module/plugins/hoster/DataHu.py @@ -0,0 +1,53 @@ +# -*- 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://data.hu/get/6381232/random.bin + +import re + +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" +    __description__ = """Data.hu Download Hoster""" +    __author_name__ = ("crash", "stickell") +    __author_mail__ = ("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): +        self.resumeDownload = True +        self.html = self.load(self.pyfile.url, decode=True) + +        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') + +        self.download(url, disposition=True) + + +getInfo = create_getInfo(DataHu) diff --git a/module/plugins/hoster/DebridItaliaCom.py b/module/plugins/hoster/DebridItaliaCom.py index a8f7dd57e..470c4ae5d 100644 --- a/module/plugins/hoster/DebridItaliaCom.py +++ b/module/plugins/hoster/DebridItaliaCom.py @@ -1,5 +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/>.    # +############################################################################ +  import re  from module.plugins.Hoster import Hoster @@ -7,7 +22,7 @@ from module.plugins.Hoster import Hoster  class DebridItaliaCom(Hoster):      __name__ = "DebridItaliaCom" -    __version__ = "0.02" +    __version__ = "0.03"      __type__ = "hoster"      __pattern__ = r"https?://.*debriditalia\.com"      __description__ = """Debriditalia.com hoster plugin""" @@ -31,7 +46,10 @@ class DebridItaliaCom(Hoster):              page = self.load(url)              self.logDebug("XML data: %s" % page) -            new_url = re.search(r'<a href="(?:[^"]+)">(?P<direct>[^<]+)</a>', page).group('direct') +            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')          self.logDebug("New URL: %s" % new_url) diff --git a/module/plugins/hoster/EgoFilesCom.py b/module/plugins/hoster/EgoFilesCom.py index b27abb416..4e78a5b9a 100644 --- a/module/plugins/hoster/EgoFilesCom.py +++ b/module/plugins/hoster/EgoFilesCom.py @@ -1,20 +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/>.    #
 +############################################################################
 +
 +# Test link (random.bin):
 +# http://egofiles.com/mOZfMI1WLZ6HBkGG/random.bin
 +
 +import re
 +
  from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
  from module.plugins.ReCaptcha import ReCaptcha
 -import re
  class EgoFilesCom(SimpleHoster):
      __name__ = "EgoFilesCom"
      __type__ = "hoster"
      __pattern__ = r"https?://(www\.)?egofiles.com/(\w+)"
 -    __version__ = "0.10"
 +    __version__ = "0.12"
      __description__ = """Egofiles.com Download Hoster"""
      __author_name__ = ("stickell")
      __author_mail__ = ("l.stickell@yahoo.it")
 -    FILE_INFO_PATTERN = r'<div class="down-file">\s+(?P<N>\S+)\s+<div class="file-properties">\s+(File size|Rozmiar): (?P<S>[\w.]+) (?P<U>\w+) \|'
 +    FILE_INFO_PATTERN = r'<div class="down-file">\s+(?P<N>.+)\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>'
 @@ -25,8 +44,15 @@ class EgoFilesCom(SimpleHoster):          # 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:
 @@ -56,7 +82,7 @@ class EgoFilesCom(SimpleHoster):          if not downloadURL:
              self.fail("No Download url retrieved/all captcha attempts failed")
 -        self.download(downloadURL)
 +        self.download(downloadURL, disposition=True)
      def handlePremium(self):
          header = self.load(self.pyfile.url, just_header=True)
 @@ -65,11 +91,13 @@ class EgoFilesCom(SimpleHoster):              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'))
 +                self.download(m.group('link'), disposition=True)
 +
  getInfo = create_getInfo(EgoFilesCom)
 diff --git a/module/plugins/hoster/FilefactoryCom.py b/module/plugins/hoster/FilefactoryCom.py index 66d26c999..aebf0f38d 100644 --- a/module/plugins/hoster/FilefactoryCom.py +++ b/module/plugins/hoster/FilefactoryCom.py @@ -1,149 +1,121 @@  # -*- 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://www.filefactory.com/file/ymxkmdud2o3/n/random.bin + +import re + +from module.plugins.internal.SimpleHoster import SimpleHoster  from module.network.RequestFactory import getURL -from module.plugins.Hoster import Hoster -from module.plugins.ReCaptcha import ReCaptcha  from module.utils import parseFileSize -from module.plugins.Plugin import chunks -from module.common.json_layer import json_loads -import re -def checkFile(plugin, urls): -    url_dict = {} -     +def getInfo(urls): +    file_info = list() +    list_ids = dict() + +    # Create a dict id:url. Will be used to retrieve original url      for url in urls: -        url_dict[re.search(plugin.__pattern__, url).group('id')] = (url, 0, 0, url) -    url_ids = url_dict.keys() -    urls = map(lambda url_id: 'http://www.filefactory.com/file/' + url_id, url_ids) - -    html = getURL("http://www.filefactory.com/tool/links.php", post = {"func": "links", "links": "\n".join(urls)}, decode=True)    -         -    for m in re.finditer(plugin.LC_INFO_PATTERN, html): -        if m.group('id') in url_ids: -            url_dict[m.group('id')] = (m.group('name'), parseFileSize(m.group('size')), 2, url_dict[m.group('id')][3]) -             -    for m in re.finditer(plugin.LC_OFFLINE_PATTERN, html): -        if m.group('id') in url_ids: -            url_dict[m.group('id')] = (url_dict[m.group('id')][0], 0, 1, url_dict[m.group('id')][3]) -     -    file_info = url_dict.values() -     +        m = re.search(FilefactoryCom.__pattern__, url) +        list_ids[m.group('id')] = url + +    # WARN: There could be a limit of urls for request +    post_data = {'func': 'links', 'links': '\n'.join(urls)} +    rep = getURL('http://www.filefactory.com/tool/links.php', post=post_data, decode=True) + +    # Online links +    for m in re.finditer( +            r'innerText">\s*<h1 class="name">(?P<N>.+) \((?P<S>[\w.]+) (?P<U>\w+)\)</h1>\s*<p>http://www.filefactory.com/file/(?P<ID>\w+).*</p>\s*<p class="hidden size">', +            rep): +        file_info.append((m.group('N'), parseFileSize(m.group('S'), m.group('U')), 2, list_ids[m.group('ID')])) + +    # Offline links +    for m in re.finditer( +            r'innerText">\s*<h1>(http://www.filefactory.com/file/(?P<ID>\w+)/)</h1>\s*<p>\1</p>\s*<p class="errorResponse">Error: file not found</p>', +            rep): +        file_info.append((list_ids[m.group('ID')], 0, 1, list_ids[m.group('ID')])) +      return file_info -    -class FilefactoryCom(Hoster): + + +class FilefactoryCom(SimpleHoster):      __name__ = "FilefactoryCom"      __type__ = "hoster" -    __pattern__ = r"http://(?:www\.)?filefactory\.com/file/(?P<id>[a-zA-Z0-9]+).*" # URLs given out are often longer but this is the requirement -    __version__ = "0.36" +    __pattern__ = r"https?://(?:www\.)?filefactory\.com/file/(?P<id>[a-zA-Z0-9]+)" +    __version__ = "0.39"      __description__ = """Filefactory.Com File Download Hoster""" -    __author_name__ = ("paulking", "zoidberg") -     -    LC_INFO_PATTERN = r'<h1 class="name">(?P<name>[^<]+) \((?P<size>[0-9.]+ \w+)\)</h1>\s*<p>http://www.filefactory.com/file/(?P<id>\w+)/' -    LC_OFFLINE_PATTERN = r'<p>http://www.filefactory.com/file/(?P<id>\w+)/</p>\s*<p class="errorResponse">' -  -    FILE_OFFLINE_PATTERN = r'<title>File Not Found' -    FILE_NAME_PATTERN = r'<span class="last">(?P<name>.*?)</span>' -    FILE_INFO_PATTERN = r'<span>(?P<size>\d(\d|\.)*) (?P<units>..) file uploaded' -     -    FILE_CHECK_PATTERN = r'check:\s*\'(?P<check>.*?)\'' -    CAPTCHA_KEY_PATTERN = r'Recaptcha.create\(\s*"(.*?)",'  -    WAIT_PATTERN = r'id="startWait" value="(?P<wait>\d+)"' -    FILE_URL_PATTERN = r'<p[^>]*?id="downloadLinkTarget"[^>]*>\s*<a href="(?P<url>.*?)"'  -             -    def setup(self): -        self.multiDL = self.resumeDownloads = self.premium +    __author_name__ = ("stickell") +    __author_mail__ = ("l.stickell@yahoo.it")      def process(self, pyfile): -        # Check file -        pyfile.name, pyfile.size, status, self.url = checkFile(self, [pyfile.url])[0]      -        if status != 2: self.offline() -        self.logDebug("File Name: %s Size: %d" % (pyfile.name, pyfile.size))  -         -        # Handle downloading -        url = self.checkDirectDownload(pyfile.url) -        if url: -            self.download(url) -        else:                 -            self.html = self.load(pyfile.url, decode = True) -                       -            if self.premium: -                self.handlePremium() -            else: -                self.handleFree() -               -    def checkDirectDownload(self, url): -        for i in range(5): -            header = self.load(url, just_header = True)            -            if 'location' in header: -                url = header['location'].strip()  -                if not url.startswith("http://"): -                    url = "http://www.filefactory.com" + url -                self.logDebug('URL: ' + url) -            elif 'content-disposition' in header: -                return url -         -        return False                                 -     +        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)          if "Currently only Premium Members can download files larger than" in self.html:              self.fail("File too large for free download")          elif "All free download slots on this server are currently in use" in self.html:              self.retry(50, 900, "All free slots are busy") -              -        # Check Id -        self.check = re.search(self.FILE_CHECK_PATTERN, self.html).group('check') -        self.logDebug("File check code is [%s]" % self.check) -         -        # Resolve captcha -        found = re.search(self.CAPTCHA_KEY_PATTERN, self.html) -        recaptcha_key = found.group(1) if found else "6LeN8roSAAAAAPdC1zy399Qei4b1BwmSBSsBN8zm" -        recaptcha = ReCaptcha(self) -         -        # Try up to 5 times -        for i in range(5):            -            challenge, code = recaptcha.challenge(recaptcha_key) -            response = json_loads(self.load("http://www.filefactory.com/file/checkCaptcha.php", -                            post={"check" : self.check, "recaptcha_challenge_field" : challenge, "recaptcha_response_field" : code})) -            if response['status'] == 'ok': -                self.correctCaptcha() -                break -            else: -                self.invalidCaptcha()                             -        else: -            self.fail("No valid captcha after 5 attempts") - -        # This will take us to a wait screen -        waiturl = "http://www.filefactory.com" + response['path'] -        self.logDebug("Fetching wait with url [%s]" % waiturl) -        waithtml = self.load(waiturl, decode=True) -        found = re.search(r'<a href="(http://www.filefactory.com/dlf/.*?)"', waithtml) -        waithtml = self.load(found.group(1), decode=True) - -        # Find the wait value and wait -        wait = int(re.search(self.WAIT_PATTERN, waithtml).group('wait')) -        self.logDebug("Waiting %d seconds." % wait) -        self.setWait(wait, True) + +        # 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.setWait(int(waittime.group(1)))          self.wait() -        # Now get the real download url and retrieve the file -        url = re.search(self.FILE_URL_PATTERN,waithtml).group('url') -        # this may either download our file or forward us to an error page -        self.logDebug("Download URL: %s" % url) -        self.download(url) -         +        # 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">'})          if check == "multiple": -            self.setWait(15*60)              self.logDebug("Parallel downloads detected; waiting 15 minutes") -            self.wait() -            self.retry() +            self.retry(wait_time=15 * 60, reason='Parallel downloads')          elif check == "error":              self.fail("Unknown error") -     +      def handlePremium(self): -        self.fail('Please enable direct downloads') -         -def getInfo(urls): -    for chunk in chunks(urls, 100): yield checkFile(FilefactoryCom, chunk) +        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 +        elif 'content-disposition' in header: +            url = self.pyfile.url +        else: +            self.parseError('Unable to detect premium direct link') + +        self.logDebug('DIRECT PREMIUM LINK: ' + url) +        self.download(url, disposition=True) diff --git a/module/plugins/hoster/FilerNet.py b/module/plugins/hoster/FilerNet.py new file mode 100644 index 000000000..9693723f9 --- /dev/null +++ b/module/plugins/hoster/FilerNet.py @@ -0,0 +1,103 @@ +# -*- 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://filer.net/get/ivgf5ztw53et3ogd +# http://filer.net/get/hgo14gzcng3scbvv + +import pycurl +import re + +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo +from module.plugins.ReCaptcha import ReCaptcha + + +class FilerNet(SimpleHoster): +    __name__ = "FilerNet" +    __type__ = "hoster" +    __pattern__ = r"https?://(www\.)?filer\.net/get/(\w+)" +    __version__ = "0.01" +    __description__ = """Filer.net Download Hoster""" +    __author_name__ = ("stickell") +    __author_mail__ = ("l.stickell@yahoo.it") + +    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' + +    def process(self, pyfile): +        self.req.setOption("timeout", 120) +        self.html = self.load(pyfile.url, decode=not self.SH_BROKEN_ENCODING, cookies=self.SH_COOKIES) + +        # 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') + +        self.getFileInfo() +        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) + +        inputs = self.parseHtmlForm(input_names='token')[1] +        if 'token' not in inputs: +            self.parseError('Unable to detect token') +        token = inputs['token'] +        self.logDebug('Token: ' + token) + +        self.html = self.load(self.pyfile.url, post={'token': token}, decode=True) + +        inputs = self.parseHtmlForm(input_names='hash')[1] +        if 'hash' not in inputs: +            self.parseError('Unable to detect hash') +        hash_data = inputs['hash'] +        self.logDebug('Hash: ' + hash_data) + +        downloadURL = '' +        recaptcha = ReCaptcha(self) +        for i 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 +            self.req.http.c.setopt(pycurl.FOLLOWLOCATION, 0) +            self.load(self.pyfile.url, post=post_data) +            self.req.http.c.setopt(pycurl.FOLLOWLOCATION, 1) + +            if 'location' in self.req.http.header: +                location = re.search(r'location: (\S+)', self.req.http.header).group(1) +                downloadURL = 'http://filer.net' + location +                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) + + +getInfo = create_getInfo(FilerNet) diff --git a/module/plugins/hoster/MegaNz.py b/module/plugins/hoster/MegaNz.py index a28ddca9d..e5be4eeb7 100644 --- a/module/plugins/hoster/MegaNz.py +++ b/module/plugins/hoster/MegaNz.py @@ -19,7 +19,7 @@ class MegaNz(Hoster):      __name__ = "MegaNz"      __type__ = "hoster"      __pattern__ = r"https?://([a-z0-9]+\.)?mega\.co\.nz/#!([a-zA-Z0-9!_\-]+)" -    __version__ = "0.11" +    __version__ = "0.12"      __description__ = """mega.co.nz hoster plugin"""      __author_name__ = ("RaNaN", )      __author_mail__ = ("ranan@pyload.org", ) @@ -34,7 +34,7 @@ class MegaNz(Hoster):      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]]) +        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): @@ -69,7 +69,7 @@ class MegaNz(Hoster):          self.pyfile.setStatus("decrypting")          f = open(self.lastDownload, "rb") -        df = open(self.lastDownload.rstrip(self.FILE_SUFFIX), "wb") +        df = open(self.lastDownload.rsplit(self.FILE_SUFFIX)[0], "wb")          # TODO: calculate CBC-MAC for checksum diff --git a/module/plugins/hoster/MultiDebridCom.py b/module/plugins/hoster/MultiDebridCom.py new file mode 100644 index 000000000..ca98e8a0e --- /dev/null +++ b/module/plugins/hoster/MultiDebridCom.py @@ -0,0 +1,57 @@ +# -*- 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.01" +    __type__ = "hoster" +    __pattern__ = r"http://\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 init(self): +        self.chunkLimit = -1 +        self.resumeDownload = True + +    def process(self, pyfile): +        if not self.account: +            self.logError("Please enter your Multi-debrid.com account or deactivate this plugin") +            self.fail("No Multi-debrid.com account provided") + +        self.logDebug("Original URL: %s" % pyfile.url) +        if re.match(self.__pattern__, pyfile.url): +            new_url = pyfile.url +        else: +            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'] + +        self.logDebug("Unrestricted URL: " + new_url) + +        self.download(new_url, disposition=True) diff --git a/module/plugins/hoster/OneFichierCom.py b/module/plugins/hoster/OneFichierCom.py index 46323d829..5d7e14f1e 100644 --- a/module/plugins/hoster/OneFichierCom.py +++ b/module/plugins/hoster/OneFichierCom.py @@ -7,11 +7,10 @@ class OneFichierCom(SimpleHoster):      __name__ = "OneFichierCom"      __type__ = "hoster"      __pattern__ = r"(http://(\w+)\.((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.45" +    __version__ = "0.46"      __description__ = """1fichier.com download hoster"""      __author_name__ = ("fragonib", "the-razer", "zoidberg","imclem") -    __author_mail__ = ("fragonib[AT]yahoo[DOT]es", "daniel_ AT gmx DOT net", -    "zoidberg@mujmail.cz","imclem on github") +    __author_mail__ = ("fragonib[AT]yahoo[DOT]es", "daniel_ AT gmx DOT net", "zoidberg@mujmail.cz","imclem on github")      FILE_NAME_PATTERN = r'">File name :</th>\s*<td>(?P<N>[^<]+)</td>'      FILE_SIZE_PATTERN = r'<th>File size :</th>\s*<td>(?P<S>[^<]+)</td>' @@ -20,7 +19,7 @@ class OneFichierCom(SimpleHoster):      DOWNLOAD_LINK_PATTERN = r'<br/> <br/> <br/> \s+<a href="(?P<url>http://.*?)"'             PASSWORD_PROTECTED_TOKEN = "protected by password" -    WAITING_PATTERN = "Warning ! Without premium status, you can download only one file at a time and you must wait at least (\d+) minutes between each downloads." +    WAITING_PATTERN = "Warning ! Without premium status, you can download only one file at a time and you must wait up to (\d+) minutes between each downloads."      def process(self, pyfile):          found = re.search(self.__pattern__, pyfile.url)          file_id = found.group(2)       diff --git a/module/plugins/hoster/PutlockerCom.py b/module/plugins/hoster/PutlockerCom.py index ca5336231..9eff0dc2b 100644 --- a/module/plugins/hoster/PutlockerCom.py +++ b/module/plugins/hoster/PutlockerCom.py @@ -1,4 +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 @@ -16,114 +17,60 @@      @author: jeix  """ +# http://www.putlocker.com/file/83C174C844583CF7 +  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(PutlockerCom.PATTERN_OFFLINE, html): -            result.append((url, 0, 1, url)) -        else: -            name = re.search(PutlockerCom.PATTERN_FILENAME_1, html) -            if name is None: -                name = re.search(PutlockerCom.PATTERN_FILENAME_2, html) -            if name is None: -                result.append((url, 0, 1, url)) -                continue -                 -            name = name.group(1) -             -            # size = re.search(PutlockerCom.PATTERN_FILESIZE, html) -            # if size is None: -                # result.append((url, 0, 1, url)) -                # continue -             -            # size = size.group(1) -             -            result.append((name, 0, 2, url))         -    yield result -     -class PutlockerCom(Hoster): + +from module.plugins.internal.SimpleHoster import SimpleHoster + + +class PutlockerCom(SimpleHoster):      __name__ = "PutlockerCom"      __type__ = "hoster"      __pattern__ = r'http://(www\.)?putlocker\.com/(file|embed)/[A-Z0-9]+' -    __version__ = "0.21" +    __version__ = "0.24"      __description__ = """Putlocker.Com""" -    __author_name__ = ("jeix") -    -    PATTERN_OFFLINE    = r"This file doesn't exist, or has been removed." -    PATTERN_FILENAME_1 = "site-content.*?<h1>(.*?)<strong" -    PATTERN_FILENAME_2 = "<title>(.*?) \|" -    PATTERN_FILESIZE   = "site-content.*?<h1>.*?<strong>\\((.*?)\\)" -    -    -    def process(self, pyfile):    -         -        self.pyfile = pyfile -        self.html = self.load(pyfile.url, decode=True) -         -        if not self._checkOnline(): -            self.offline() -         -        self.pyfile.name = self._getName() -         -        self.link = self._getLink() -        if not self.link.startswith('http://'): -            self.link = "http://www.putlocker.com" + self.link -        self.download( self.link )          - -    def _checkOnline(self): -        if re.search(self.PATTERN_OFFLINE, self.html): -            return False -        else: -            return True -         -    def _getName(self): -        name = re.search(self.PATTERN_FILENAME_1, self.html) -        if name is None: -            name = re.search(self.PATTERN_FILENAME_2, self.html) -        # if name is None: -            # self.fail("%s: Plugin broken." % self.__name__) - -        return name.group(1) -         +    __author_name__ = ("jeix", "stickell") +    __author_mail__ = ("l.stickell@yahoo.it") + +    FILE_OFFLINE_PATTERN = r"This file doesn't exist, or has been removed." +    FILE_INFO_PATTERN = r'site-content">\s*<h1>(?P<N>.+)<strong>\( (?P<S>[^)]+) \)</strong></h1>' + +    def handleFree(self): +        self.html = self.load(self.pyfile.url, decode=True) + +        link = self._getLink() +        if not link.startswith('http://'): +            link = "http://www.putlocker.com" + link +        self.download(link, disposition=True) +      def _getLink(self): -        self.hash = re.search("<input type=\"hidden\" value=\"([a-z0-9]+)\" name=\"hash\">", self.html) -        # if self.hash is None: -            # self.fail("%s: Plugin broken." % self.__name__) -             -        self.param = "hash=" + self.hash.group(1) + "&confirm=Continue+as+Free+User" -        self.html2 = self.load(self.pyfile.url, post=self.param) -        if ">You have exceeded the daily stream limit for your country\\. You can wait until tomorrow" in self.html2 or "(>This content server has been temporarily disabled for upgrades|Try again soon\\. You can still download it below\\.<)" in self.html2: -            self.waittime = 2 * 60 * 60 -            self.retry(wait_time=self.waittime, reason="Waiting %s seconds" % self.waittime) -             -        self.link = re.search("<a href=\"/gopro\\.php\">Tired of ads and waiting\\? Go Pro\\!</a>[\t\n\rn ]+</div>[\t\n\rn ]+<a href=\"(/.*?)\"", self.html2) -        if self.link is None: -            self.link = re.search("\"(/get_file\\.php\\?download=[A-Z0-9]+\\&key=[a-z0-9]+)\"", self.html2) -         -        if self.link is None: -            self.link = re.search("\"(/get_file\\.php\\?download=[A-Z0-9]+\\&key=[a-z0-9]+&original=1)\"", self.html2) -             -        if self.link is None: -            self.link = re.search("\"(/get_file\\.php\\?id=[A-Z0-9]+\\&key=[A-Za-z0-9=]+\\&original=1)\"", self.html2) -             -        if self.link is None: -            self.link = re.search("playlist: \\'(/get_file\\.php\\?stream=[A-Za-z0-9=]+)\\'", self.html2) -            if not self.link is None: -                self.html3 = self.load("http://www.putlocker.com" + self.link.group(1)) -                self.link = re.search("media:content url=\"(http://.*?)\"", self.html3) -                if self.link is None: -                    self.link = re.search("\"(http://media\\-b\\d+\\.putlocker\\.com/download/\\d+/.*?)\"", self.html3) -             -        # if link is None: -            # self.fail("%s: Plugin broken." % self.__name__) - -        return self.link.group(1).replace("&", "&") -          -         +        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=2 * 60 * 60, reason="Download limit exceeded or server disabled") + +        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.putlocker.com" + link.group(1)) +                link = re.search(r'media:content url="(http://.*?)"', self.html) +                if not link: +                    link = re.search("\"(http://media\\-b\\d+\\.putlocker\\.com/download/\\d+/.*?)\"", self.html) +            else: +                self.parseError('Unable to detect a download link') + +        return link.group(1).replace("&", "&") diff --git a/module/plugins/hoster/RarefileNet.py b/module/plugins/hoster/RarefileNet.py index 7c5543cc8..a0f5930b5 100644 --- a/module/plugins/hoster/RarefileNet.py +++ b/module/plugins/hoster/RarefileNet.py @@ -1,30 +1,34 @@  # -*- coding: utf-8 -*- +  import re +  from module.plugins.hoster.XFileSharingPro import XFileSharingPro, create_getInfo  from module.utils import html_unescape +  class RarefileNet(XFileSharingPro):      __name__ = "RarefileNet"      __type__ = "hoster"      __pattern__ = r"http://(?:\w*\.)*rarefile.net/\w{12}" -    __version__ = "0.02" +    __version__ = "0.03"      __description__ = """Rarefile.net hoster plugin"""      __author_name__ = ("zoidberg")      __author_mail__ = ("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>'      HOSTER_NAME = "rarefile.net" -     +      def setup(self): -        self.resumeDownload = self.multiDL = self.premium  -     +        self.resumeDownload = self.multiDL = self.premium +      def handleCaptcha(self, inputs):          captcha_div = re.search(r'<b>Enter code.*?<div.*?>(.*?)</div>', self.html, re.S).group(1) -        self.logDebug(captcha_div)   +        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)  +        self.logDebug("CAPTCHA", inputs['code'], numerals)          return 3 -getInfo = create_getInfo(RarefileNet)
\ No newline at end of file +getInfo = create_getInfo(RarefileNet) diff --git a/module/plugins/hoster/UploadedTo.py b/module/plugins/hoster/UploadedTo.py index a2134ecb5..c7ce5846e 100644 --- a/module/plugins/hoster/UploadedTo.py +++ b/module/plugins/hoster/UploadedTo.py @@ -1,5 +1,9 @@  # -*- coding: utf-8 -*- +# Test links (random.bin): +# http://ul.to/044yug9o +# http://ul.to/gzfhd0xs +  import re  from module.utils import html_unescape, parseFileSize @@ -11,34 +15,37 @@ from module.plugins.ReCaptcha import ReCaptcha  key = "bGhGMkllZXByd2VEZnU5Y2NXbHhYVlZ5cEE1bkEzRUw=".decode('base64') +  def getID(url):      """ returns id from file url""" -    m = re.match(r"http://[\w\.-]*?(uploaded\.(to|net)(/file/|/?\?id=|.*?&id=)|ul\.to/)(?P<ID>\w+)", url) +    m = re.match(UploadedTo.__pattern__, url)      return m.group('ID') +  def getAPIData(urls): -        post = {"apikey" : key} +    post = {"apikey": key} -        idMap = {} +    idMap = {} -        for i, url in enumerate(urls): -            id = getID(url) -            post["id_%s" % i] = id -            idMap[id] = url +    for i, url in enumerate(urls): +        id = getID(url) +        post["id_%s" % i] = id +        idMap[id] = url -        api = unicode(getURL("http://uploaded.net/api/filemultiple", post=post, decode=False), 'iso-8859-1') +    api = unicode(getURL("http://uploaded.net/api/filemultiple", post=post, decode=False), 'iso-8859-1') -        result = {} +    result = {} -        if api: -            for line in api.splitlines(): -                data = line.split(",", 4) -                if data[1] in idMap: -                    result[data[1]] = (data[0], data[2], data[4], data[3], idMap[data[1]]) +    if api: +        for line in api.splitlines(): +            data = line.split(",", 4) +            if data[1] in idMap: +                result[data[1]] = (data[0], data[2], data[4], data[3], idMap[data[1]]) -        return result +    return result -def parseFileInfo(self, url = '', html = ''): + +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 @@ -54,6 +61,7 @@ def parseFileInfo(self, url = '', html = ''):      return name, size, status, fileid +  def getInfo(urls):      for chunk in chunks(urls, 80):          result = [] @@ -73,8 +81,8 @@ def getInfo(urls):  class UploadedTo(Hoster):      __name__ = "UploadedTo"      __type__ = "hoster" -    __pattern__ = r"http://[\w\.-]*?(uploaded\.(to|net)(/file/|/?\?id=|.*?&id=)|ul\.to/)\w+" -    __version__ = "0.64" +    __pattern__ = r"https?://[\w\.-]*?(uploaded\.(to|net)|ul\.to)(/file/|/?\?id=|.*?&id=|/)(?P<ID>\w+)" +    __version__ = "0.67"      __description__ = """Uploaded.net Download Hoster"""      __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") @@ -88,7 +96,7 @@ class UploadedTo(Hoster):          self.multiDL = False          self.resumeDownload = False          self.url = False -        self.chunkLimit = 1 # critical problems with more chunks +        self.chunkLimit = 1  # critical problems with more chunks          if self.account:              self.premium = self.account.getAccountInfo(self.user)["premium"]              if self.premium: @@ -108,7 +116,7 @@ class UploadedTo(Hoster):          if not api:              self.logWarning("No response for API call") -            self.html = unicode(self.load(pyfile.url, decode = False), 'iso-8859-1') +            self.html = unicode(self.load(pyfile.url, decode=False), 'iso-8859-1')              name, size, status, self.fileID = parseFileInfo(self)              self.logDebug(name, size, status, self.fileID)              if status == 1: @@ -140,8 +148,9 @@ class UploadedTo(Hoster):      def handlePremium(self):          info = self.account.getAccountInfo(self.user, True) -        self.log.debug("%(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.log.debug("%(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.log.info(_("%s: Not enough traffic left" % self.__name__))              self.account.empty(self.user)              self.resetAccount() @@ -185,7 +194,7 @@ class UploadedTo(Hoster):              #self.req.lastURL = str(self.url)              re_captcha = ReCaptcha(self)              challenge, result = re_captcha.challenge(challengeId.group(1)) -            options = {"recaptcha_challenge_field" : challenge, "recaptcha_response_field": result} +            options = {"recaptcha_challenge_field": challenge, "recaptcha_response_field": result}              self.wait()              result = self.load(url, post=options) @@ -193,13 +202,13 @@ class UploadedTo(Hoster):              if "limit-size" in result:                  self.fail("File too big for free download") -            elif "limit-slot" in result: # Temporary restriction so just wait a bit +            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 +            elif self.DL_LIMIT_PATTERN in result:  # limit-dl                  self.setWait(60 * 60, True)                  self.wait()                  self.retry() @@ -216,7 +225,7 @@ class UploadedTo(Hoster):          if not downloadURL:              self.fail("No Download url retrieved/all captcha attempts failed") -        self.download(downloadURL) +        self.download(downloadURL, disposition=True)          check = self.checkDownload({"limit-dl": self.DL_LIMIT_PATTERN})          if check == "limit-dl":              self.setWait(60 * 60, True) diff --git a/module/plugins/hoster/XVideosCom.py b/module/plugins/hoster/XVideosCom.py new file mode 100644 index 000000000..b7f3f7b58 --- /dev/null +++ b/module/plugins/hoster/XVideosCom.py @@ -0,0 +1,19 @@ + +import re +import urllib + +from module.plugins.Hoster import Hoster + +class XVideosCom(Hoster): +    __name__ = "XVideos.com" +    __version__ = "0.1" +    __pattern__ = r"http://www\.xvideos\.com/video([0-9]+)/.*" +    __config__ = [] + +    def process(self, pyfile): +        site = self.load(pyfile.url) +        pyfile.name = "%s (%s).flv" %( +            re.search(r"<h2>([^<]+)<span", site).group(1), +            re.search(self.__pattern__, pyfile.url).group(1), +        ) +        self.download(urllib.unquote(re.search(r"flv_url=([^&]+)&", site).group(1))) diff --git a/module/plugins/hoster/YoutubeCom.py b/module/plugins/hoster/YoutubeCom.py index 8b8764367..2fb1e5264 100644 --- a/module/plugins/hoster/YoutubeCom.py +++ b/module/plugins/hoster/YoutubeCom.py @@ -34,7 +34,7 @@ class YoutubeCom(Hoster):      __name__ = "YoutubeCom"      __type__ = "hoster"      __pattern__ = r"(http|https)://(www\.)?(de\.)?\youtube\.com/watch\?v=.*" -    __version__ = "0.29" +    __version__ = "0.30"      __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), diff --git a/module/plugins/internal/SimpleCrypter.py b/module/plugins/internal/SimpleCrypter.py index b8942c724..c9e350e86 100644 --- a/module/plugins/internal/SimpleCrypter.py +++ b/module/plugins/internal/SimpleCrypter.py @@ -18,18 +18,20 @@  """  import re +  from module.plugins.Crypter import Crypter +  class SimpleCrypter(Crypter):      __name__ = "SimpleCrypter" -    __version__ = "0.03" +    __version__ = "0.04"      __pattern__ = None      __type__ = "crypter"      __description__ = """Base crypter plugin"""      __author_name__ = ("stickell", "zoidberg")      __author_mail__ = ("l.stickell@yahoo.it", "zoidberg@mujmail.cz")      """ -    These patterns should be defined by each hoster: +    These patterns should be defined by each crypter:      LINK_PATTERN: group(1) must be a download link      example: <div class="link"><a href="(http://speedload.org/\w+) @@ -39,7 +41,7 @@ class SimpleCrypter(Crypter):      """      def decrypt(self, pyfile): -        self.html = self.load(pyfile.url) +        self.html = self.load(pyfile.url, decode=True)          package_name, folder_name = self.getPackageNameAndFolder() @@ -55,7 +57,7 @@ class SimpleCrypter(Crypter):          if hasattr(self, 'TITLE_PATTERN'):              m = re.search(self.TITLE_PATTERN, self.html)              if m: -                name = folder = m.group('title') +                name = folder = m.group('title').strip()                  self.logDebug("Found name [%s] and folder [%s] in package info" % (name, folder))                  return name, folder diff --git a/module/plugins/internal/SimpleHoster.py b/module/plugins/internal/SimpleHoster.py index cfc9f2b43..69ed57ff8 100644 --- a/module/plugins/internal/SimpleHoster.py +++ b/module/plugins/internal/SimpleHoster.py @@ -155,8 +155,8 @@ class SimpleHoster(Hoster):      """      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_INFO = r'(?P<N>file_name)' -    and FILE_SIZE_INFO = r'(?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'      """ diff --git a/module/plugins/internal/UnRar.py b/module/plugins/internal/UnRar.py index 240dc0233..da8e7cf3d 100644 --- a/module/plugins/internal/UnRar.py +++ b/module/plugins/internal/UnRar.py @@ -22,18 +22,20 @@ import re  from os.path import join  from glob import glob  from subprocess import Popen, PIPE +from string import digits  from module.utils import save_join, decode  from module.plugins.internal.AbstractExtractor import AbtractExtractor, WrongPassword, ArchiveError, CRCError  class UnRar(AbtractExtractor):      __name__ = "UnRar" -    __version__ = "0.11" +    __version__ = "0.13"      # there are some more uncovered rar formats -    re_splitfile = re.compile(r"(.*)\.part(\d+)\.rar$") +    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_wrongpwd = re.compile("(Corrupt file or wrong password|password incorrect)") +    re_wrongpwd = re.compile("(Corrupt file or wrong password|password incorrect)", re.I)      CMD = "unrar"      @staticmethod @@ -113,15 +115,31 @@ class UnRar(AbtractExtractor):      def extract(self, progress, password=None):          command = "x" if self.fullpath else "e" -        # popen thinks process is still alive (just like pexpect) - very strange behavior -        # so for now progress can not be determined correctly          p = self.call_unrar(command, self.file, self.out, password=password)          renice(p.pid, self.renice)          progress(0) -        out, err = p.communicate() #wait for process +        progressstring = "" +        while True: +            c = p.stdout.read(1) +            # quit loop on eof +            if not c: +                break +            # reading a percentage sign -> set progress and restart +            if c == '%': +                progress(int(progressstring)) +                progressstring = "" +            # not reading a digit -> therefore restart +            elif c not in digits: +                progressstring = "" +            # add digit to progressstring +            else: +                progressstring = progressstring + c          progress(100) +        # 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: @@ -139,7 +157,9 @@ class UnRar(AbtractExtractor):      def getDeleteFiles(self):          if ".part" in self.file:              return glob(re.sub("(?<=\.part)([01]+)", "*", self.file, re.IGNORECASE)) -        return [self.file] +        # 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)      def listContent(self):          command = "vb" if self.fullpath else "lb" | 
