+                       resume_len = 0
+               if self.params['continuedl'] and resume_len != 0:
+                       self.report_resuming_byte(resume_len)
+                       request.add_header('Range','bytes=%d-' % resume_len)
+
+               # Establish connection
+               try:
+                       data = urllib2.urlopen(request)
+               except (urllib2.HTTPError, ), err:
+                       if err.code != 416: #  416 is 'Requested range not satisfiable'
+                               raise
+                       data = urllib2.urlopen(basic_request)
+                       content_length = data.info()['Content-Length']
+                       if content_length is not None and long(content_length) == resume_len:
+                               self.report_file_already_downloaded(filename)
+                               return True
+                       else:
+                               self.report_unable_to_resume()
+                               open_mode = 'wb'
+
+               data_len = data.info().get('Content-length', None)
+               data_len_str = self.format_bytes(data_len)
+               byte_counter = 0
+               block_size = 1024
+               start = time.time()
+               while True:
+                       # Download and write
+                       before = time.time()
+                       data_block = data.read(block_size)
+                       after = time.time()
+                       data_block_len = len(data_block)
+                       if data_block_len == 0:
+                               break
+                       byte_counter += data_block_len
+
+                       # Open file just in time
+                       if stream is None:
+                               try:
+                                       stream = open(filename, open_mode)
+                                       self.report_destination(filename)
+                               except (OSError, IOError), err:
+                                       self.trouble('ERROR: unable to open for writing: %s' % str(err))
+                                       return False
+                       stream.write(data_block)
+                       block_size = self.best_block_size(after - before, data_block_len)
+
+                       # Progress message
+                       percent_str = self.calc_percent(byte_counter, data_len)
+                       eta_str = self.calc_eta(start, time.time(), data_len, byte_counter)
+                       speed_str = self.calc_speed(start, time.time(), byte_counter)
+                       self.report_progress(percent_str, data_len_str, speed_str, eta_str)
+
+                       # Apply rate limit
+                       self.slow_down(start, byte_counter)
+
+               self.report_finish()
+               if data_len is not None and str(byte_counter) != data_len:
+                       raise ContentTooShortError(byte_counter, long(data_len))
+               return True
+
+class InfoExtractor(object):
+       """Information Extractor class.
+
+       Information extractors are the classes that, given a URL, extract
+       information from the video (or videos) the URL refers to. This
+       information includes the real video URL, the video title and simplified
+       title, author and others. The information is stored in a dictionary
+       which is then passed to the FileDownloader. The FileDownloader
+       processes this information possibly downloading the video to the file
+       system, among other possible outcomes. The dictionaries must include
+       the following fields:
+
+       id:             Video identifier.
+       url:            Final video URL.
+       uploader:       Nickname of the video uploader.
+       title:          Literal title.
+       stitle:         Simplified title.
+       ext:            Video filename extension.
+
+       Subclasses of this one should re-define the _real_initialize() and
+       _real_extract() methods, as well as the suitable() static method.
+       Probably, they should also be instantiated and added to the main
+       downloader.
+       """
+
+       _ready = False
+       _downloader = None
+
+       def __init__(self, downloader=None):
+               """Constructor. Receives an optional downloader."""
+               self._ready = False
+               self.set_downloader(downloader)
+
+       @staticmethod
+       def suitable(url):
+               """Receives a URL and returns True if suitable for this IE."""
+               return False
+
+       def initialize(self):
+               """Initializes an instance (authentication, etc)."""
+               if not self._ready:
+                       self._real_initialize()
+                       self._ready = True
+
+       def extract(self, url):
+               """Extracts URL information and returns it in list of dicts."""
+               self.initialize()
+               return self._real_extract(url)
+
+       def set_downloader(self, downloader):
+               """Sets the downloader for this IE."""
+               self._downloader = downloader
+       
+       def _real_initialize(self):
+               """Real initialization process. Redefine in subclasses."""
+               pass
+
+       def _real_extract(self, url):
+               """Real extraction process. Redefine in subclasses."""
+               pass
+
+class YoutubeIE(InfoExtractor):
+       """Information extractor for youtube.com."""
+
+       _VALID_URL = r'^((?:http://)?(?:\w+\.)?youtube\.com/(?:(?:v/)|(?:(?:watch(?:\.php)?)?\?(?:.+&)?v=)))?([0-9A-Za-z_-]+)(?(1).+)?$'
+       _LANG_URL = r'http://uk.youtube.com/?hl=en&persist_hl=1&gl=US&persist_gl=1&opt_out_ackd=1'
+       _LOGIN_URL = 'http://www.youtube.com/signup?next=/&gl=US&hl=en'
+       _AGE_URL = 'http://www.youtube.com/verify_age?next_url=/&gl=US&hl=en'
+       _NETRC_MACHINE = 'youtube'
+       _available_formats = ['22', '35', '18', '5', '17', '13', None] # listed in order of priority for -b flag
+       _video_extensions = {
+               '13': '3gp',
+               '17': 'mp4',
+               '18': 'mp4',
+               '22': 'mp4',
+       }
+
+       @staticmethod
+       def suitable(url):
+               return (re.match(YoutubeIE._VALID_URL, url) is not None)
+
+       @staticmethod
+       def htmlentity_transform(matchobj):
+               """Transforms an HTML entity to a Unicode character."""
+               entity = matchobj.group(1)
+
+               # Known non-numeric HTML entity
+               if entity in htmlentitydefs.name2codepoint:
+                       return unichr(htmlentitydefs.name2codepoint[entity])
+
+               # Unicode character
+               mobj = re.match(ur'(?u)#(x?\d+)', entity)
+               if mobj is not None:
+                       numstr = mobj.group(1)
+                       if numstr.startswith(u'x'):
+                               base = 16
+                               numstr = u'0%s' % numstr
+                       else:
+                               base = 10
+                       return unichr(long(numstr, base))
+
+               # Unknown entity in name, return its literal representation
+               return (u'&%s;' % entity)
+
+       def report_lang(self):
+               """Report attempt to set language."""
+               self._downloader.to_stdout(u'[youtube] Setting language')
+
+       def report_login(self):
+               """Report attempt to log in."""
+               self._downloader.to_stdout(u'[youtube] Logging in')
+       
+       def report_age_confirmation(self):
+               """Report attempt to confirm age."""
+               self._downloader.to_stdout(u'[youtube] Confirming age')
+       
+       def report_video_info_webpage_download(self, video_id):
+               """Report attempt to download video info webpage."""
+               self._downloader.to_stdout(u'[youtube] %s: Downloading video info webpage' % video_id)
+       
+       def report_information_extraction(self, video_id):
+               """Report attempt to extract video information."""
+               self._downloader.to_stdout(u'[youtube] %s: Extracting video information' % video_id)
+       
+       def report_unavailable_format(self, video_id, format):
+               """Report extracted video URL."""
+               self._downloader.to_stdout(u'[youtube] %s: Format %s not available' % (video_id, format))
+       
+       def _real_initialize(self):
+               if self._downloader is None:
+                       return
+
+               username = None
+               password = None
+               downloader_params = self._downloader.params
+
+               # Attempt to use provided username and password or .netrc data
+               if downloader_params.get('username', None) is not None:
+                       username = downloader_params['username']
+                       password = downloader_params['password']
+               elif downloader_params.get('usenetrc', False):
+                       try:
+                               info = netrc.netrc().authenticators(self._NETRC_MACHINE)
+                               if info is not None:
+                                       username = info[0]
+                                       password = info[2]
+                               else:
+                                       raise netrc.NetrcParseError('No authenticators for %s' % self._NETRC_MACHINE)
+                       except (IOError, netrc.NetrcParseError), err:
+                               self._downloader.to_stderr(u'WARNING: parsing .netrc: %s' % str(err))
+                               return
+
+               # Set language
+               request = urllib2.Request(self._LANG_URL, None, std_headers)
+               try:
+                       self.report_lang()
+                       urllib2.urlopen(request).read()
+               except (urllib2.URLError, httplib.HTTPException, socket.error), err:
+                       self._downloader.to_stderr(u'WARNING: unable to set language: %s' % str(err))
+                       return
+
+               # No authentication to be performed
+               if username is None:
+                       return
+
+               # Log in
+               login_form = {
+                               'current_form': 'loginForm',
+                               'next':         '/',
+                               'action_login': 'Log In',
+                               'username':     username,
+                               'password':     password,
+                               }
+               request = urllib2.Request(self._LOGIN_URL, urllib.urlencode(login_form), std_headers)
+               try:
+                       self.report_login()
+                       login_results = urllib2.urlopen(request).read()
+                       if re.search(r'(?i)<form[^>]* name="loginForm"', login_results) is not None:
+                               self._downloader.to_stderr(u'WARNING: unable to log in: bad username or password')
+                               return
+               except (urllib2.URLError, httplib.HTTPException, socket.error), err:
+                       self._downloader.to_stderr(u'WARNING: unable to log in: %s' % str(err))
+                       return
+       
+               # Confirm age
+               age_form = {
+                               'next_url':             '/',
+                               'action_confirm':       'Confirm',
+                               }
+               request = urllib2.Request(self._AGE_URL, urllib.urlencode(age_form), std_headers)
+               try:
+                       self.report_age_confirmation()
+                       age_results = urllib2.urlopen(request).read()
+               except (urllib2.URLError, httplib.HTTPException, socket.error), err:
+                       self._downloader.trouble(u'ERROR: unable to confirm age: %s' % str(err))
+                       return
+
+       def _real_extract(self, url):
+               # Extract video id from URL
+               mobj = re.match(self._VALID_URL, url)
+               if mobj is None:
+                       self._downloader.trouble(u'ERROR: invalid URL: %s' % url)
+                       return
+               video_id = mobj.group(2)
+
+               # Downloader parameters
+               best_quality = False
+               format_param = None
+               quality_index = 0
+               if self._downloader is not None:
+                       params = self._downloader.params
+                       format_param = params.get('format', None)
+                       if format_param == '0':
+                               format_param = self._available_formats[quality_index]
+                               best_quality = True
+
+               while True:
+                       # Extension
+                       video_extension = self._video_extensions.get(format_param, 'flv')
+
+                       # Get video info
+                       video_info_url = 'http://www.youtube.com/get_video_info?&video_id=%s&el=detailpage&ps=default&eurl=&gl=US&hl=en' % video_id
+                       request = urllib2.Request(video_info_url, None, std_headers)
+                       try:
+                               self.report_video_info_webpage_download(video_id)
+                               video_info_webpage = urllib2.urlopen(request).read()
+                       except (urllib2.URLError, httplib.HTTPException, socket.error), err:
+                               self._downloader.trouble(u'ERROR: unable to download video info webpage: %s' % str(err))
+                               return
+                       self.report_information_extraction(video_id)
+
+                       # "t" param
+                       mobj = re.search(r'(?m)&token=([^&]+)(?:&|$)', video_info_webpage)
+                       if mobj is None:
+                               # Attempt to see if YouTube has issued an error message
+                               mobj = re.search(r'(?m)&reason=([^&]+)(?:&|$)', video_info_webpage)
+                               if mobj is None:
+                                       self._downloader.trouble(u'ERROR: unable to extract "t" parameter for unknown reason')
+                                       stream = open('reportme-ydl-%s.dat' % time.time(), 'wb')
+                                       stream.write(video_info_webpage)
+                                       stream.close()
+                               else:
+                                       reason = urllib.unquote_plus(mobj.group(1))
+                                       self._downloader.trouble(u'ERROR: YouTube said: %s' % reason.decode('utf-8'))
+                               return
+                       token = urllib.unquote(mobj.group(1))
+                       video_real_url = 'http://www.youtube.com/get_video?video_id=%s&t=%s&eurl=&el=detailpage&ps=default&gl=US&hl=en' % (video_id, token)
+                       if format_param is not None:
+                               video_real_url = '%s&fmt=%s' % (video_real_url, format_param)
+
+                       # uploader
+                       mobj = re.search(r'(?m)&author=([^&]+)(?:&|$)', video_info_webpage)
+                       if mobj is None:
+                               self._downloader.trouble(u'ERROR: unable to extract uploader nickname')
+                               return
+                       video_uploader = urllib.unquote(mobj.group(1))
+
+                       # title
+                       mobj = re.search(r'(?m)&title=([^&]+)(?:&|$)', video_info_webpage)
+                       if mobj is None:
+                               self._downloader.trouble(u'ERROR: unable to extract video title')
+                               return
+                       video_title = urllib.unquote(mobj.group(1))
+                       video_title = video_title.decode('utf-8')
+                       video_title = re.sub(ur'(?u)&(.+?);', self.htmlentity_transform, video_title)
+                       video_title = video_title.replace(os.sep, u'%')
+
+                       # simplified title
+                       simple_title = re.sub(ur'(?u)([^%s]+)' % simple_title_chars, ur'_', video_title)
+                       simple_title = simple_title.strip(ur'_')
+
+                       try:
+                               # Process video information
+                               self._downloader.process_info({
+                                       'id':           video_id.decode('utf-8'),
+                                       'url':          video_real_url.decode('utf-8'),
+                                       'uploader':     video_uploader.decode('utf-8'),
+                                       'title':        video_title,
+                                       'stitle':       simple_title,
+                                       'ext':          video_extension.decode('utf-8'),
+                               })
+
+                               return
+
+                       except UnavailableFormatError, err:
+                               if best_quality:
+                                       if quality_index == len(self._available_formats) - 1:
+                                               # I don't ever expect this to happen
+                                               self._downloader.trouble(u'ERROR: no known formats available for video')
+                                               return
+                                       else:
+                                               self.report_unavailable_format(video_id, format_param)
+                                               quality_index += 1
+                                               format_param = self._available_formats[quality_index]
+                                               continue
+                               else: 
+                                       self._downloader.trouble('ERROR: format not available for video')
+                                       return
+
+
+class MetacafeIE(InfoExtractor):
+       """Information Extractor for metacafe.com."""
+
+       _VALID_URL = r'(?:http://)?(?:www\.)?metacafe\.com/watch/([^/]+)/([^/]+)/.*'
+       _DISCLAIMER = 'http://www.metacafe.com/family_filter/'
+       _FILTER_POST = 'http://www.metacafe.com/f/index.php?inputType=filter&controllerGroup=user'
+       _youtube_ie = None
+
+       def __init__(self, youtube_ie, downloader=None):
+               InfoExtractor.__init__(self, downloader)
+               self._youtube_ie = youtube_ie
+
+       @staticmethod
+       def suitable(url):
+               return (re.match(MetacafeIE._VALID_URL, url) is not None)
+
+       def report_disclaimer(self):
+               """Report disclaimer retrieval."""
+               self._downloader.to_stdout(u'[metacafe] Retrieving disclaimer')
+
+       def report_age_confirmation(self):
+               """Report attempt to confirm age."""
+               self._downloader.to_stdout(u'[metacafe] Confirming age')
+       
+       def report_download_webpage(self, video_id):
+               """Report webpage download."""
+               self._downloader.to_stdout(u'[metacafe] %s: Downloading webpage' % video_id)
+       
+       def report_extraction(self, video_id):
+               """Report information extraction."""
+               self._downloader.to_stdout(u'[metacafe] %s: Extracting information' % video_id)
+
+       def _real_initialize(self):
+               # Retrieve disclaimer
+               request = urllib2.Request(self._DISCLAIMER, None, std_headers)
+               try:
+                       self.report_disclaimer()
+                       disclaimer = urllib2.urlopen(request).read()
+               except (urllib2.URLError, httplib.HTTPException, socket.error), err:
+                       self._downloader.trouble(u'ERROR: unable to retrieve disclaimer: %s' % str(err))
+                       return
+
+               # Confirm age
+               disclaimer_form = {
+                       'filters': '0',
+                       'submit': "Continue - I'm over 18",
+                       }
+               request = urllib2.Request(self._FILTER_POST, urllib.urlencode(disclaimer_form), std_headers)
+               try:
+                       self.report_age_confirmation()
+                       disclaimer = urllib2.urlopen(request).read()
+               except (urllib2.URLError, httplib.HTTPException, socket.error), err:
+                       self._downloader.trouble(u'ERROR: unable to confirm age: %s' % str(err))
+                       return
+       
+       def _real_extract(self, url):
+               # Extract id and simplified title from URL
+               mobj = re.match(self._VALID_URL, url)
+               if mobj is None:
+                       self._downloader.trouble(u'ERROR: invalid URL: %s' % url)
+                       return
+
+               video_id = mobj.group(1)
+
+               # Check if video comes from YouTube
+               mobj2 = re.match(r'^yt-(.*)$', video_id)
+               if mobj2 is not None:
+                       self._youtube_ie.extract('http://www.youtube.com/watch?v=%s' % mobj2.group(1))
+                       return
+
+               simple_title = mobj.group(2).decode('utf-8')
+               video_extension = 'flv'
+
+               # Retrieve video webpage to extract further information
+               request = urllib2.Request('http://www.metacafe.com/watch/%s/' % video_id)
+               try:
+                       self.report_download_webpage(video_id)
+                       webpage = urllib2.urlopen(request).read()
+               except (urllib2.URLError, httplib.HTTPException, socket.error), err:
+                       self._downloader.trouble(u'ERROR: unable retrieve video webpage: %s' % str(err))
+                       return
+
+               # Extract URL, uploader and title from webpage
+               self.report_extraction(video_id)
+               mobj = re.search(r'(?m)&mediaURL=([^&]+)', webpage)
+               if mobj is None:
+                       self._downloader.trouble(u'ERROR: unable to extract media URL')
+                       return
+               mediaURL = urllib.unquote(mobj.group(1))
+
+               #mobj = re.search(r'(?m)&gdaKey=(.*?)&', webpage)
+               #if mobj is None:
+               #       self._downloader.trouble(u'ERROR: unable to extract gdaKey')
+               #       return
+               #gdaKey = mobj.group(1)
+               #
+               #video_url = '%s?__gda__=%s' % (mediaURL, gdaKey)
+
+               video_url = mediaURL
+
+               mobj = re.search(r'(?im)<title>(.*) - Video</title>', webpage)
+               if mobj is None:
+                       self._downloader.trouble(u'ERROR: unable to extract title')
+                       return
+               video_title = mobj.group(1).decode('utf-8')
+
+               mobj = re.search(r'(?ms)<li id="ChnlUsr">.*?Submitter:.*?<a .*?>(.*?)<', webpage)
+               if mobj is None:
+                       self._downloader.trouble(u'ERROR: unable to extract uploader nickname')
+                       return
+               video_uploader = mobj.group(1)
+
+               try:
+                       # Process video information
+                       self._downloader.process_info({
+                               'id':           video_id.decode('utf-8'),
+                               'url':          video_url.decode('utf-8'),
+                               'uploader':     video_uploader.decode('utf-8'),
+                               'title':        video_title,
+                               'stitle':       simple_title,
+                               'ext':          video_extension.decode('utf-8'),
+                       })
+               except UnavailableFormatError:
+                       self._downloader.trouble(u'ERROR: format not available for video')
+
+
+class YoutubeSearchIE(InfoExtractor):
+       """Information Extractor for YouTube search queries."""
+       _VALID_QUERY = r'ytsearch(\d+|all)?:[\s\S]+'
+       _TEMPLATE_URL = 'http://www.youtube.com/results?search_query=%s&page=%s&gl=US&hl=en'
+       _VIDEO_INDICATOR = r'href="/watch\?v=.+?"'
+       _MORE_PAGES_INDICATOR = r'(?m)>\s*Next\s*</a>'
+       _youtube_ie = None
+       _max_youtube_results = 1000
+
+       def __init__(self, youtube_ie, downloader=None):
+               InfoExtractor.__init__(self, downloader)
+               self._youtube_ie = youtube_ie
+       
+       @staticmethod
+       def suitable(url):
+               return (re.match(YoutubeSearchIE._VALID_QUERY, url) is not None)
+
+       def report_download_page(self, query, pagenum):
+               """Report attempt to download playlist page with given number."""
+               self._downloader.to_stdout(u'[youtube] query "%s": Downloading page %s' % (query, pagenum))
+
+       def _real_initialize(self):
+               self._youtube_ie.initialize()
+       
+       def _real_extract(self, query):
+               mobj = re.match(self._VALID_QUERY, query)
+               if mobj is None:
+                       self._downloader.trouble(u'ERROR: invalid search query "%s"' % query)
+                       return
+
+               prefix, query = query.split(':')
+               prefix = prefix[8:]
+               if prefix == '':
+                       self._download_n_results(query, 1)
+                       return
+               elif prefix == 'all':
+                       self._download_n_results(query, self._max_youtube_results)
+                       return