diff options
Diffstat (limited to 'module/plugins')
| -rw-r--r-- | module/plugins/Plugin.py | 8 | ||||
| -rw-r--r-- | module/plugins/accounts/RapidshareCom.py | 51 | ||||
| -rw-r--r-- | module/plugins/accounts/UploadedTo.py | 35 | ||||
| -rw-r--r-- | module/plugins/crypter/YoutubeBatch.py | 33 | ||||
| -rw-r--r-- | module/plugins/crypter/YoutubeChannel.py | 52 | ||||
| -rw-r--r-- | module/plugins/hooks/IRCInterface.py | 6 | ||||
| -rw-r--r-- | module/plugins/hooks/UnRar.py | 4 | ||||
| -rw-r--r-- | module/plugins/hooks/XMPPInterface.py | 229 | ||||
| -rw-r--r-- | module/plugins/hoster/NetloadIn.py | 20 | ||||
| -rw-r--r-- | module/plugins/hoster/ShareCx.py | 27 | ||||
| -rw-r--r-- | module/plugins/hoster/ShareonlineBiz.py | 31 | ||||
| -rw-r--r-- | module/plugins/hoster/YoutubeCom.py | 7 | 
12 files changed, 381 insertions, 122 deletions
| diff --git a/module/plugins/Plugin.py b/module/plugins/Plugin.py index 04a5adb91..02a15dfbc 100644 --- a/module/plugins/Plugin.py +++ b/module/plugins/Plugin.py @@ -34,6 +34,14 @@ from os import makedirs  from tempfile import NamedTemporaryFile  from mimetypes import guess_type +from itertools import islice + +def chunks(iterable, size): +  it = iter(iterable) +  item = list(islice(it, size)) +  while item: +    yield item +    item = list(islice(it, size))  def dec(func):      def new(*args): diff --git a/module/plugins/accounts/RapidshareCom.py b/module/plugins/accounts/RapidshareCom.py index 052470ada..7f5fff84d 100644 --- a/module/plugins/accounts/RapidshareCom.py +++ b/module/plugins/accounts/RapidshareCom.py @@ -28,31 +28,34 @@ class RapidshareCom(Account):      __author_mail__ = ("mkaay@mkaay.de")      def getAccountInfo(self, user): -        data = None -        for account in self.accounts.items(): -            if account[0] == user: -                data = account[1] -        if not data: -            return -        req = self.core.requestFactory.getRequest(self.__name__, user) -        api_url_base = "http://api.rapidshare.com/cgi-bin/rsapi.cgi" -        api_param_prem = {"sub": "getaccountdetails_v1", "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"): -            return -        fields = src.split("\n") -        info = {} -        for t in fields: -            if not t.strip(): -                continue -            k, v = t.split("=") -            info[k] = v +        try: +            data = None +            for account in self.accounts.items(): +                if account[0] == user: +                    data = account[1] +            if not data: +                return +            req = self.core.requestFactory.getRequest(self.__name__, user) +            api_url_base = "http://api.rapidshare.com/cgi-bin/rsapi.cgi" +            api_param_prem = {"sub": "getaccountdetails_v1", "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"): +                return +            fields = src.split("\n") +            info = {} +            for t in fields: +                if not t.strip(): +                    continue +                k, v = t.split("=") +                info[k] = v +                 +            out = Account.getAccountInfo(self, user) +            tmp = {"validuntil":None, "login":str(info["accountid"]), "trafficleft":int(info["tskb"]), "type":self.__name__} +            out.update(tmp) -        out = Account.getAccountInfo(self, user) -        tmp = {"validuntil":None, "login":str(info["accountid"]), "trafficleft":int(info["tskb"]), "type":self.__name__} -        out.update(tmp) -         -        return out +            return out +        except: +            return Account.getAccountInfo(self, user)      def login(self, user, data):          req = self.core.requestFactory.getRequest(self.__name__, user) diff --git a/module/plugins/accounts/UploadedTo.py b/module/plugins/accounts/UploadedTo.py index d2aa22b22..926f91255 100644 --- a/module/plugins/accounts/UploadedTo.py +++ b/module/plugins/accounts/UploadedTo.py @@ -30,22 +30,25 @@ class UploadedTo(Account):      __author_mail__ = ("mkaay@mkaay.de")      def getAccountInfo(self, user): -        data = None -        for account in self.accounts.items(): -            if account[0] == user: -                data = account[1] -        if not data: -            return -        req = self.core.requestFactory.getRequest(self.__name__, user) -        html = req.load("http://uploaded.to/", cookies=True) -        raw_traffic = re.search(r"Traffic left: </span><span class=.*?>(.*?)</span>", html).group(1) -        raw_valid = re.search(r"Valid until: </span> <span class=.*?>(.*?)</span>", html).group(1) -        traffic = int(self.parseTraffic(raw_traffic)) -        validuntil = int(mktime(strptime(raw_valid.strip(), "%d-%m-%Y %H:%M"))) -        out = Account.getAccountInfo(self, user) -        tmp =  {"login":user, "validuntil":validuntil, "trafficleft":traffic, "type":self.__name__} -        out.update(tmp) -        return out +        try: +            data = None +            for account in self.accounts.items(): +                if account[0] == user: +                    data = account[1] +            if not data: +                return +            req = self.core.requestFactory.getRequest(self.__name__, user) +            html = req.load("http://uploaded.to/", cookies=True) +            raw_traffic = re.search(r"Traffic left: </span><span class=.*?>(.*?)</span>", html).group(1) +            raw_valid = re.search(r"Valid until: </span> <span class=.*?>(.*?)</span>", html).group(1) +            traffic = int(self.parseTraffic(raw_traffic)) +            validuntil = int(mktime(strptime(raw_valid.strip(), "%d-%m-%Y %H:%M"))) +            out = Account.getAccountInfo(self, user) +            tmp =  {"login":user, "validuntil":validuntil, "trafficleft":traffic, "type":self.__name__} +            out.update(tmp) +            return out +        except: +            return Account.getAccountInfo(self, user)      def login(self, user, data):          req = self.core.requestFactory.getRequest(self.__name__, user) diff --git a/module/plugins/crypter/YoutubeBatch.py b/module/plugins/crypter/YoutubeBatch.py new file mode 100644 index 000000000..091c8558a --- /dev/null +++ b/module/plugins/crypter/YoutubeBatch.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import re + +from module.plugins.Crypter import Crypter + +class YoutubeBatch(Crypter): +    __name__ = "YoutubeBatch" +    __type__ = "container" +    __pattern__ = r"http://(www\.)?(de\.)?\youtube\.com/user/*" +    __version__ = "0.9" +    __description__ = """Youtube.com Channel Download Plugin""" +    __author_name__ = ("RaNaN", "Spoob") +    __author_mail__ = ("RaNaN@pyload.org", "spoob@pyload.org") + +    def setup(self): +        self.user = re.search(r"/user/(.+)", self.pyfile.url).group(1).split("#")[0] +        self.playlist = re.search(r"/user/%s.*?/user/(.{16})" % self.user, self.pyfile.url).group(1) + +    def file_exists(self): +        if "User not found" in self.req.load("http://gdata.youtube.com/feeds/api/playlists/%s?v=2" % self.playlist): +            return False +        return True + +    def decrypt(self, pyfile): +        if not self.file_exists(): +            self.offline() +        url = "http://gdata.youtube.com/feeds/api/playlists/%s?v=2" % self.playlist            +        rep = self.load(url) +        new_links = [] +        new_links.extend(re.findall(r"href\='(http:\/\/www.youtube.com\/watch\?v\=[^']+)&", rep)) +        self.packages.append((self.pyfile.package().name, new_links, self.pyfile.package().name)) diff --git a/module/plugins/crypter/YoutubeChannel.py b/module/plugins/crypter/YoutubeChannel.py deleted file mode 100644 index 292be06af..000000000 --- a/module/plugins/crypter/YoutubeChannel.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import re - -from module.plugins.Crypter import Crypter - -class YoutubeChannel(Crypter): -    __name__ = "YoutubeChannel" -    __type__ = "container" -    __pattern__ = r"http://(www\.)?(de\.)?\youtube\.com/user/*" -    __version__ = "0.9" -    __description__ = """Youtube.com Channel Download Plugin""" -    __author_name__ = ("RaNaN", "Spoob") -    __author_mail__ = ("RaNaN@pyload.org", "spoob@pyload.org") - -    def __init__(self, parent): -        Crypter.__init__(self, parent) -        self.parent = parent -        self.html = None -        self.read_config() -        self.user = re.search(r"/user/(.+)", self.parent.url).group(1).split("#")[0] - -    def file_exists(self): -        if "User not found" in self.req.load("http://gdata.youtube.com/feeds/api/users/%s" % self.user): -            return False -        return True - -    def proceed(self, url, location): -        max_videos = self.config['max_videos'] -        if not max_videos: -            max_videos = 1000 #max video a user can upload -        page = 0 -        temp_links = [] -        if "," in self.config['video_groups']: -            video_groups = self.config['video_groups'].split(",") -        else: -            video_groups = [self.config['video_groups']] -        for group in video_groups: -            for start_index in range(1, int(max_videos), 50): -                max_results = max_videos - page * 50 -                if max_results > 50: -                    max_results = 50 -                url = "http://gdata.youtube.com/feeds/api/users/%s/%s?max-results=%i&start-index=%i" % (self.user, group, max_results, start_index) -                rep = self.req.load(url) -                new_links = re.findall(r"href\='(http:\/\/www.youtube.com\/watch\?v\=[^']+)&", rep) -                if new_links != []: -                    temp_links.extend(new_links) -                else: -                    break -                page += 1 -        self.links = temp_links diff --git a/module/plugins/hooks/IRCInterface.py b/module/plugins/hooks/IRCInterface.py index 2ede56685..8d50647e3 100644 --- a/module/plugins/hooks/IRCInterface.py +++ b/module/plugins/hooks/IRCInterface.py @@ -86,8 +86,8 @@ class IRCInterface(Thread, Hook):          for t in self.getConfig("owner").split():              if t.strip().startswith("#"):                  self.sock.send("JOIN %s\r\n" % t.strip()) -        self.log.info("pyLoadIRC: Connected to %s!" % host) -        self.log.info("pyLoadIRC: Switching to listening mode!") +        self.log.info("pyLoad IRC: Connected to %s!" % host) +        self.log.info("pyLoad IRC: Switching to listening mode!")          try:                      self.main_loop() @@ -173,7 +173,7 @@ class IRCInterface(Thread, Hook):              for line in res:                  self.response(line, msg["origin"])          except Exception, e: -            self.log.error("pyLoadIRC: "+ repr(e)) +            self.log.error("pyLoad IRC: "+ repr(e))      def response(self, msg, origin=""): diff --git a/module/plugins/hooks/UnRar.py b/module/plugins/hooks/UnRar.py index 82c99a575..30cda62af 100644 --- a/module/plugins/hooks/UnRar.py +++ b/module/plugins/hooks/UnRar.py @@ -30,7 +30,7 @@ class UnRar(Hook):      __name__ = "UnRar"      __version__ = "0.1"      __description__ = """unrar""" -    __config__ = [ ("activated", "bool", "Activated", True), +    __config__ = [ ("activated", "bool", "Activated", False),                     ("fullpath", "bool", "extract full path", True),                     ("overwrite", "bool", "overwrite files", True),                     ("passwordfile", "str", "unrar passoword file", "unrar_passwords.txt"), @@ -109,7 +109,7 @@ class UnRar(Hook):                  u.crackPassword(passwords=self.passwords, statusFunction=s, overwrite=True, destination=folder)              except WrongPasswordError:                  continue -            except CommandError as e: +            except CommandError , e:                  if re.search("Cannot find volume", e.stderr):                      continue                  try: diff --git a/module/plugins/hooks/XMPPInterface.py b/module/plugins/hooks/XMPPInterface.py new file mode 100644 index 000000000..67a7f1b77 --- /dev/null +++ b/module/plugins/hooks/XMPPInterface.py @@ -0,0 +1,229 @@ +# -*- 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 sys +from threading import Thread +import time +from time import sleep +from traceback import print_exc + +from pyxmpp.all import JID,Iq,Presence,Message,StreamError +from pyxmpp.jabber.client import JabberClient +from pyxmpp.interface import implements +from pyxmpp.interfaces import * +from pyxmpp.streamtls import TLSSettings + +from module.plugins.Hook import Hook +from module.plugins.hooks.IRCInterface import IRCInterface + +class XMPPInterface(IRCInterface, JabberClient): +    __name__ = "XMPPInterface" +    __version__ = "0.1" +    __description__ = """connect to jabber and let owner perform different tasks""" +    __config__ = [("activated", "bool", "Activated", "False"), +        ("jid", "str", "Jabber ID", "user@exmaple-jabber-server.org"), +        ("pw", "str", "Password", ""), +        ("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")] +    __author_name__ = ("RaNaN") +    __author_mail__ = ("RaNaN@pyload.org") +         +    implements(IMessageHandlersProvider) +     +    def __init__(self, core): +        IRCInterface.__init__(self, core) +         +        self.jid = JID(self.getConfig("jid")) +        password = self.getConfig("pw") +         +        # if bare JID is provided add a resource -- it is required +        if not self.jid.resource: +            self.jid=JID(self.jid.node, self.jid.domain, "pyLoad") +      +        tls_settings = None + +        # setup client with provided connection information +        # and identity data +        JabberClient.__init__(self, self.jid, password, +                disco_name="pyLoad XMPP Client", disco_type="bot", +                tls_settings = tls_settings) + +        self.interface_providers = [ +            VersionHandler(self), +            self, +        ] +             +    def coreReady(self): +        self.new_package = {} +     +        self.start() +                 +    def packageFinished(self, pypack): +         +        try: +            if self.getConfig("info_pack"): +                self.announce(_("Package finished: %s") % pypack.name) +        except: +            pass +         +    def downloadFinished(self, pyfile): +        try: +            if self.getConfig("info_file"): +                self.announce(_("Download finished: %s @ %s") % (pyfile.name, pyfile.pluginname) ) +        except: +            pass +              +    def run(self): +        # connect to IRC etc. +        self.connect() +        try:         +            self.loop(1) +        except Exception, ex: +            self.core.log.error("pyLoad XMPP: %s" % str(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.log.debug("pyLoad XMPP: *** State changed: %s %r ***" % (state,arg) ) + +    def get_message_handlers(self): +        """Return list of (message_type, message_handler) tuples. + +        The handlers returned will be called when matching message is received +        in a client session.""" +        return [ +            ("normal", self.message), +            ] + +    def message(self,stanza): +        """Message handler for the component.""" +        subject=stanza.get_subject() +        body=stanza.get_body() +        t=stanza.get_type() +        self.log.debug(_(u'pyLoad XMPP: Message from %s received.') % (unicode(stanza.get_from(),))) +        self.log.debug(_(u'pyLoad XMPP: Body: %s') % body) +         +        if stanza.get_type()=="headline": +            # 'headline' messages should never be replied to +            return True +        if subject: +            subject=u"Re: "+subject +             +        to_jid = stanza.get_from() +        from_jid = stanza.get_to() + +        #j = JID() +        to_name = to_jid.as_utf8() +        from_name = from_jid.as_utf8() +         +        names = self.getConfig("owners").split(";") +         +        if to_name in names or to_jid.node+"@"+to_jid.domain in names: +             +            messages = [] +             +            trigger = "pass" +            args = None +             +            temp = body.split() +            trigger = temp[0] +            if len(temp) > 1: +                args = temp[1:] +         +            handler = getattr(self, "event_%s" % trigger, self.event_pass) +            try: +                res = handler(args) +                for line in res: +                    m=Message( +                        to_jid=to_jid, +                        from_jid=from_jid, +                        stanza_type=stanza.get_type(), +                        subject=subject, +                        body=line) +                     +                    messages.append(m) +            except Exception, e: +                    self.log.error("pyLoad XMPP: "+ repr(e)) +             +            return messages +         +        else: +            return True +         +             +    def announce(self, message): +        """ send message to all owners""" +        for user in self.getConfig("owners").split(";"): +             +            self.log.debug(_("pyLoad XMPP: Send message to %s") % user) +             +            to_jid = JID(user) +             +            m = Message(from_jid=self.jid, +                        to_jid=to_jid, +                        stanza_type="chat", +                        body=message) +             +            self.stream.send(m) +         +            +class VersionHandler(object): +    """Provides handler for a version query. +     +    This class will answer version query and announce 'jabber:iq:version' namespace +    in the client's disco#info results.""" +     +    implements(IIqHandlersProvider, IFeaturesProvider) + +    def __init__(self, client): +        """Just remember who created this.""" +        self.client = client + +    def get_features(self): +        """Return namespace which should the client include in its reply to a +        disco#info query.""" +        return ["jabber:iq:version"] + +    def get_iq_get_handlers(self): +        """Return list of tuples (element_name, namespace, handler) describing +        handlers of <iq type='get'/> stanzas""" +        return [ +            ("query", "jabber:iq:version", self.get_version), +            ] + +    def get_iq_set_handlers(self): +        """Return empty list, as this class provides no <iq type='set'/> stanza handler.""" +        return [] + +    def get_version(self,iq): +        """Handler for jabber:iq:version queries. + +        jabber:iq:version queries are not supported directly by PyXMPP, so the +        XML node is accessed directly through the libxml2 API.  This should be +        used very carefully!""" +        iq=iq.make_result_response() +        q=iq.new_query("jabber:iq:version") +        q.newTextChild(q.ns(),"name","Echo component") +        q.newTextChild(q.ns(),"version","1.0") +        return iq +    
\ No newline at end of file diff --git a/module/plugins/hoster/NetloadIn.py b/module/plugins/hoster/NetloadIn.py index 9e117fa14..6f0cb9461 100644 --- a/module/plugins/hoster/NetloadIn.py +++ b/module/plugins/hoster/NetloadIn.py @@ -4,8 +4,12 @@  import re  from time import sleep +  from module.plugins.Hoster import Hoster  from module.network.Request import getURL +from module.plugins.Plugin import chunks + +  def getInfo(urls):   ##  returns list of tupels (name, size (in bytes), status (see FileDatabase), url) @@ -14,14 +18,10 @@ def getInfo(urls):      apiurl = "http://api.netload.in/info.php?auth=Zf9SnQh9WiReEsb18akjvQGqT0I830e8&bz=1&md5=1&file_id="      id_regex = re.compile("http://.*netload\.in/(?:datei(.*?)(?:\.htm|/)|index.php?id=10&file_id=)")      urls_per_query = 80 - -    iterations = len(urls)/urls_per_query -    if len(urls)%urls_per_query > 0: -        iterations = iterations +1 - -    for i in range(iterations): +        +    for chunk in chunks(urls, urls_per_query):          ids = "" -        for url in urls[i*urls_per_query:(i+1)*urls_per_query]: +        for url in chunk:              match = id_regex.search(url)              if match:                  ids = ids + match.group(1) +";" @@ -37,19 +37,17 @@ def getInfo(urls):          result = [] -        counter = 0 -        for r in api.split(): +        for i, r in enumerate(api.split()):              try:                  tmp = r.split(";")                  try:                      size = int(tmp[2])                  except:                      size = 0 -                result.append( (tmp[1], size, 2 if tmp[3] == "online" else 1, urls[(i*80)+counter]) ) +                result.append( (tmp[1], size, 2 if tmp[3] == "online" else 1, chunk[i] ) )              except:                  print "Netload prefetch: Error while processing response: "                  print r -            counter = counter +1          yield result diff --git a/module/plugins/hoster/ShareCx.py b/module/plugins/hoster/ShareCx.py index feee30cd3..e64459754 100644 --- a/module/plugins/hoster/ShareCx.py +++ b/module/plugins/hoster/ShareCx.py @@ -3,8 +3,35 @@  import re
  from module.plugins.Hoster import Hoster
 +from module.plugins.Plugin import chunks
 +from module.network.Request import getURL
  #from module.BeautifulSoup import BeautifulSoup
 +def getInfo(urls):
 +    api_url = "http://www.share.cx/uapi?do=check&links="
 +    
 +    for chunk in chunks(urls, 90):
 +        get = ""
 +        for url in chunk:
 +            get += ";"+url
 +            
 +        api = getURL(api_url+get[1:])
 +        result = []
 +        
 +        for i, link in enumerate(api.split()):
 +            url,name,size = link.split(";")
 +            if name and size:
 +                status = 2
 +            else:
 +                status = 1
 +                
 +            if not name: name = chunk[i]
 +            if not size: size = 0
 +                
 +            result.append( (name, size, status, chunk[i]) )
 +        
 +        yield result
 +
  class ShareCx(Hoster):
      __name__ = "ShareCx"
      __type__ = "hoster"
 diff --git a/module/plugins/hoster/ShareonlineBiz.py b/module/plugins/hoster/ShareonlineBiz.py index 8646fcc88..42a2bc560 100644 --- a/module/plugins/hoster/ShareonlineBiz.py +++ b/module/plugins/hoster/ShareonlineBiz.py @@ -13,19 +13,30 @@ from time import sleep  from module.plugins.Hoster import Hoster  from module.network.Request import getURL +from module.plugins.Plugin import chunks +      def getInfo(urls):      api_url_base = "http://www.share-online.biz/linkcheck/linkcheck.php" -    api_param_file = {"links": "\n".join(x.replace("http://www.share-online.biz/dl/","") for x in urls)} #api only supports old style links -    src = getURL(api_url_base, post=api_param_file) -    result = [] -    for i, res in enumerate(src.split("\n")): -        if not res: -            continue -        fields = res.split(";") -        status = 2 if fields[1] == "OK" else 3 -        result.append((fields[2], int(fields[3]), status, urls[i])) -    yield result +     +    for chunk in chunks(urls, 90): +        api_param_file = {"links": "\n".join(x.replace("http://www.share-online.biz/dl/","") for x in chunk)} #api only supports old style links +        src = getURL(api_url_base, post=api_param_file) +        result = [] +        for i, res in enumerate(src.split("\n")): +            if not res: +                continue +            fields = res.split(";") +             +            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  class ShareonlineBiz(Hoster):      __name__ = "ShareonlineBiz" diff --git a/module/plugins/hoster/YoutubeCom.py b/module/plugins/hoster/YoutubeCom.py index e40b0c9ad..d92d8d128 100644 --- a/module/plugins/hoster/YoutubeCom.py +++ b/module/plugins/hoster/YoutubeCom.py @@ -32,9 +32,8 @@ class YoutubeCom(Hoster):          if self.getConf("quality") == "hd" or self.getConf("quality") == "hq":              file_suffix = ".mp4" -        name = re.search(file_name_pattern, html).group(1).replace("/", "") + file_suffix -         -        pyfile.name = name.replace("&", "&").replace("ö", "oe").replace("ä", "ae").replace("ü", "ue")         +        name = (re.search(file_name_pattern, html).group(1).replace("/", "") + file_suffix).decode("utf8") +        pyfile.name = name #.replace("&", "&").replace("ö", "oe").replace("ä", "ae").replace("ü", "ue")                  if self.getConf("quality") == "sd":              quality = "&fmt=6" @@ -45,4 +44,4 @@ class YoutubeCom(Hoster):          file_url = 'http://youtube.com/get_video?video_id=' + videoId + '&t=' + videoHash + quality + "&asv=2" -        self.download(file_url)
\ No newline at end of file +        self.download(file_url) | 
