Better fix for getting source url's
[youtube-dl] / youtube_dl / InfoExtractors.py
index 14a1d6523f4834ebcd85334d90b256dc6184f916..325c5ecd4c580018fd168f3812c3aafb885e5c57 100755 (executable)
@@ -114,8 +114,8 @@ class InfoExtractor(object):
     def _request_webpage(self, url_or_request, video_id, note=None, errnote=None):
         """ Returns the response handle """
         if note is None:
-            note = u'Downloading video webpage'
-        if note is not False:
+            self.report_download_webpage(video_id)
+        elif note is not False:
             self.to_screen(u'%s: %s' % (video_id, note))
         try:
             return compat_urllib_request.urlopen(url_or_request)
@@ -124,8 +124,8 @@ class InfoExtractor(object):
                 errnote = u'Unable to download webpage'
             raise ExtractorError(u'%s: %s' % (errnote, compat_str(err)), sys.exc_info()[2])
 
-    def _download_webpage(self, url_or_request, video_id, note=None, errnote=None):
-        """ Returns the data of the page as a string """
+    def _download_webpage_handle(self, url_or_request, video_id, note=None, errnote=None):
+        """ Returns a tuple (page content as string, URL handle) """
         urlh = self._request_webpage(url_or_request, video_id, note, errnote)
         content_type = urlh.headers.get('Content-Type', '')
         m = re.match(r'[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+\s*;\s*charset=(.+)', content_type)
@@ -142,7 +142,12 @@ class InfoExtractor(object):
             self.to_screen(u'Dumping request to ' + url)
             dump = base64.b64encode(webpage_bytes).decode('ascii')
             self._downloader.to_screen(dump)
-        return webpage_bytes.decode(encoding, 'replace')
+        content = webpage_bytes.decode(encoding, 'replace')
+        return (content, urlh)
+
+    def _download_webpage(self, url_or_request, video_id, note=None, errnote=None):
+        """ Returns the data of the page as a string """
+        return self._download_webpage_handle(url_or_request, video_id, note, errnote)[0]
 
     def to_screen(self, msg):
         """Print msg to screen, prefixing it with '[ie_name]'"""
@@ -152,6 +157,10 @@ class InfoExtractor(object):
         """Report information extraction."""
         self.to_screen(u'%s: Extracting information' % id_or_name)
 
+    def report_download_webpage(self, video_id):
+        """Report webpage download."""
+        self.to_screen(u'%s: Downloading webpage' % video_id)
+
     def report_age_confirmation(self):
         """Report attempt to confirm age."""
         self.to_screen(u'Confirming age')
@@ -202,7 +211,7 @@ class YoutubeIE(InfoExtractor):
                      ([0-9A-Za-z_-]+)                                         # here is it! the YouTube video ID
                      (?(1).+)?                                                # if we found the ID, everything can follow
                      $"""
-    _LANG_URL = r'http://www.youtube.com/?hl=en&persist_hl=1&gl=US&persist_gl=1&opt_out_ackd=1'
+    _LANG_URL = r'https://www.youtube.com/?hl=en&persist_hl=1&gl=US&persist_gl=1&opt_out_ackd=1'
     _LOGIN_URL = 'https://accounts.google.com/ServiceLogin'
     _AGE_URL = 'http://www.youtube.com/verify_age?next_url=/&gl=US&hl=en'
     _NEXT_URL_RE = r'[\?&]next_url=([^&]+)'
@@ -418,7 +427,7 @@ class YoutubeIE(InfoExtractor):
 
         # Log in
         login_form_strs = {
-                u'continue': u'http://www.youtube.com/signin?action_handle_signin=true&feature=sign_in_button&hl=en_US&nomobiletemp=1',
+                u'continue': u'https://www.youtube.com/signin?action_handle_signin=true&feature=sign_in_button&hl=en_US&nomobiletemp=1',
                 u'Email': username,
                 u'GALX': galx,
                 u'Passwd': password,
@@ -478,12 +487,12 @@ class YoutubeIE(InfoExtractor):
         # Extract original video URL from URL with redirection, like age verification, using next_url parameter
         mobj = re.search(self._NEXT_URL_RE, url)
         if mobj:
-            url = 'http://www.youtube.com/' + compat_urllib_parse.unquote(mobj.group(1)).lstrip('/')
+            url = 'https://www.youtube.com/' + compat_urllib_parse.unquote(mobj.group(1)).lstrip('/')
         video_id = self._extract_id(url)
 
         # Get video webpage
         self.report_video_webpage_download(video_id)
-        url = 'http://www.youtube.com/watch?v=%s&gl=US&hl=en&has_verified=1' % video_id
+        url = 'https://www.youtube.com/watch?v=%s&gl=US&hl=en&has_verified=1' % video_id
         request = compat_urllib_request.Request(url)
         try:
             video_webpage_bytes = compat_urllib_request.urlopen(request).read()
@@ -558,19 +567,18 @@ class YoutubeIE(InfoExtractor):
         mobj = re.search(r'id="eow-date.*?>(.*?)</span>', video_webpage, re.DOTALL)
         if mobj is not None:
             upload_date = ' '.join(re.sub(r'[/,-]', r' ', mobj.group(1)).split())
-            format_expressions = ['%d %B %Y', '%B %d %Y', '%b %d %Y']
-            for expression in format_expressions:
-                try:
-                    upload_date = datetime.datetime.strptime(upload_date, expression).strftime('%Y%m%d')
-                except:
-                    pass
+            upload_date = unified_strdate(upload_date)
 
         # description
         video_description = get_element_by_id("eow-description", video_webpage)
         if video_description:
             video_description = clean_html(video_description)
         else:
-            video_description = ''
+            fd_mobj = re.search(r'<meta name="description" content="([^"]+)"', video_webpage)
+            if fd_mobj:
+                video_description = unescapeHTML(fd_mobj.group(1))
+            else:
+                video_description = u''
 
         # subtitles
         video_subtitles = None
@@ -622,8 +630,7 @@ class YoutubeIE(InfoExtractor):
                 format_list = available_formats
             existing_formats = [x for x in format_list if x in url_map]
             if len(existing_formats) == 0:
-                self._downloader.report_error(u'no known formats available for video')
-                return
+                raise ExtractorError(u'no known formats available for video')
             if self._downloader.params.get('listformats', None):
                 self._print_formats(existing_formats)
                 return
@@ -643,11 +650,9 @@ class YoutubeIE(InfoExtractor):
                         video_url_list = [(rf, url_map[rf])]
                         break
                 if video_url_list is None:
-                    self._downloader.report_error(u'requested format not available')
-                    return
+                    raise ExtractorError(u'requested format not available')
         else:
-            self._downloader.report_error(u'no conn or url_encoded_fmt_stream_map information found in video info')
-            return
+            raise ExtractorError(u'no conn or url_encoded_fmt_stream_map information found in video info')
 
         results = []
         for format_param, video_real_url in video_url_list:
@@ -683,17 +688,10 @@ class MetacafeIE(InfoExtractor):
     _FILTER_POST = 'http://www.metacafe.com/f/index.php?inputType=filter&controllerGroup=user'
     IE_NAME = u'metacafe'
 
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
-
     def report_disclaimer(self):
         """Report disclaimer retrieval."""
         self.to_screen(u'Retrieving disclaimer')
 
-    def report_download_webpage(self, video_id):
-        """Report webpage download."""
-        self.to_screen(u'%s: Downloading webpage' % video_id)
-
     def _real_initialize(self):
         # Retrieve disclaimer
         request = compat_urllib_request.Request(self._DISCLAIMER)
@@ -786,16 +784,11 @@ class MetacafeIE(InfoExtractor):
             'ext':      video_extension.decode('utf-8'),
         }]
 
-
 class DailymotionIE(InfoExtractor):
     """Information Extractor for Dailymotion"""
 
     _VALID_URL = r'(?i)(?:https?://)?(?:www\.)?dailymotion\.[a-z]{2,3}/video/([^/]+)'
     IE_NAME = u'dailymotion'
-    _WORKING = False
-
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
 
     def _real_extract(self, url):
         # Extract id and simplified title from URL
@@ -878,13 +871,6 @@ class PhotobucketIE(InfoExtractor):
     _VALID_URL = r'(?:http://)?(?:[a-z0-9]+\.)?photobucket\.com/.*[\?\&]current=(.*\.flv)'
     IE_NAME = u'photobucket'
 
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
-
-    def report_download_webpage(self, video_id):
-        """Report webpage download."""
-        self.to_screen(u'%s: Downloading webpage' % video_id)
-
     def _real_extract(self, url):
         # Extract id from URL
         mobj = re.match(self._VALID_URL, url)
@@ -943,13 +929,6 @@ class YahooIE(InfoExtractor):
     _VPAGE_URL = r'(?:http://)?video\.yahoo\.com/watch/([0-9]+)/([0-9]+)(?:[#\?].*)?'
     IE_NAME = u'video.yahoo'
 
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
-
-    def report_download_webpage(self, video_id):
-        """Report webpage download."""
-        self.to_screen(u'%s: Downloading webpage' % video_id)
-
     def _real_extract(self, url, new_video=True):
         # Extract ID from URL
         mobj = re.match(self._VALID_URL, url)
@@ -1079,13 +1058,6 @@ class VimeoIE(InfoExtractor):
     _VALID_URL = r'(?P<proto>https?://)?(?:(?:www|player)\.)?vimeo\.com/(?:(?:groups|album)/[^/]+/)?(?P<direct_link>play_redirect_hls\?clip_id=)?(?:videos?/)?(?P<id>[0-9]+)'
     IE_NAME = u'vimeo'
 
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
-
-    def report_download_webpage(self, video_id):
-        """Report webpage download."""
-        self.to_screen(u'%s: Downloading webpage' % video_id)
-
     def _real_extract(self, url, new_video=True):
         # Extract ID from URL
         mobj = re.match(self._VALID_URL, url)
@@ -1101,13 +1073,7 @@ class VimeoIE(InfoExtractor):
 
         # Retrieve video webpage to extract further information
         request = compat_urllib_request.Request(url, None, std_headers)
-        try:
-            self.report_download_webpage(video_id)
-            webpage_bytes = compat_urllib_request.urlopen(request).read()
-            webpage = webpage_bytes.decode('utf-8')
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-            self._downloader.report_error(u'Unable to retrieve video webpage: %s' % compat_str(err))
-            return
+        webpage = self._download_webpage(request, video_id)
 
         # Now we begin extracting as much information as we can from what we
         # retrieved. First we extract the information common to all extractors,
@@ -1119,7 +1085,10 @@ class VimeoIE(InfoExtractor):
             config = webpage.split(' = {config:')[1].split(',assets:')[0]
             config = json.loads(config)
         except:
-            self._downloader.report_error(u'unable to extract info section')
+            if re.search('The creator of this video has not given you permission to embed it on this domain.', webpage):
+                self._downloader.report_error(u'The author has restricted the access to this video, try with the "--referer" option')
+            else:
+                self._downloader.report_error(u'unable to extract info section')
             return
 
         # Extract title
@@ -1196,13 +1165,6 @@ class ArteTvIE(InfoExtractor):
 
     IE_NAME = u'arte.tv'
 
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
-
-    def report_download_webpage(self, video_id):
-        """Report webpage download."""
-        self.to_screen(u'%s: Downloading webpage' % video_id)
-
     def fetch_webpage(self, url):
         request = compat_urllib_request.Request(url)
         try:
@@ -1227,7 +1189,7 @@ class ArteTvIE(InfoExtractor):
 
         for (i, key, err) in matchTuples:
             if mobj.group(i) is None:
-                self._downloader.trouble(err)
+                self._downloader.report_error(err)
                 return
             else:
                 info[key] = mobj.group(i)
@@ -1241,7 +1203,7 @@ class ArteTvIE(InfoExtractor):
             r'src="(.*?/videothek_js.*?\.js)',
             0,
             [
-                (1, 'url', u'ERROR: Invalid URL: %s' % url)
+                (1, 'url', u'Invalid URL: %s' % url)
             ]
         )
         http_host = url.split('/')[2]
@@ -1253,9 +1215,9 @@ class ArteTvIE(InfoExtractor):
                 '(rtmp://.*?)\'',
             re.DOTALL,
             [
-                (1, 'path',   u'ERROR: could not extract video path: %s' % url),
-                (2, 'player', u'ERROR: could not extract video player: %s' % url),
-                (3, 'url',    u'ERROR: could not extract video url: %s' % url)
+                (1, 'path',   u'could not extract video path: %s' % url),
+                (2, 'player', u'could not extract video player: %s' % url),
+                (3, 'url',    u'could not extract video url: %s' % url)
             ]
         )
         video_url = u'%s/%s' % (info.get('url'), info.get('path'))
@@ -1267,7 +1229,7 @@ class ArteTvIE(InfoExtractor):
             r'param name="movie".*?videorefFileUrl=(http[^\'"&]*)',
             0,
             [
-                (1, 'url', u'ERROR: Invalid URL: %s' % url)
+                (1, 'url', u'Invalid URL: %s' % url)
             ]
         )
         next_url = compat_urllib_parse.unquote(info.get('url'))
@@ -1276,7 +1238,7 @@ class ArteTvIE(InfoExtractor):
             r'<video lang="%s" ref="(http[^\'"&]*)' % video_lang,
             0,
             [
-                (1, 'url', u'ERROR: Could not find <video> tag: %s' % url)
+                (1, 'url', u'Could not find <video> tag: %s' % url)
             ]
         )
         next_url = compat_urllib_parse.unquote(info.get('url'))
@@ -1289,10 +1251,10 @@ class ArteTvIE(InfoExtractor):
                 '<url quality="hd">(.*?)</url>',
             re.DOTALL,
             [
-                (1, 'id',    u'ERROR: could not extract video id: %s' % url),
-                (2, 'title', u'ERROR: could not extract video title: %s' % url),
-                (3, 'date',  u'ERROR: could not extract video date: %s' % url),
-                (4, 'url',   u'ERROR: could not extract video url: %s' % url)
+                (1, 'id',    u'could not extract video id: %s' % url),
+                (2, 'title', u'could not extract video title: %s' % url),
+                (3, 'date',  u'could not extract video date: %s' % url),
+                (4, 'url',   u'could not extract video url: %s' % url)
             ]
         )
 
@@ -1326,14 +1288,11 @@ class GenericIE(InfoExtractor):
     _VALID_URL = r'.*'
     IE_NAME = u'generic'
 
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
-
     def report_download_webpage(self, video_id):
         """Report webpage download."""
         if not self._downloader.params.get('test', False):
             self._downloader.report_warning(u'Falling back on generic information extractor.')
-        self.to_screen(u'%s: Downloading webpage' % video_id)
+        super(GenericIE, self).report_download_webpage(video_id)
 
     def report_following_redirect(self, new_url):
         """Report information extraction."""
@@ -1468,9 +1427,6 @@ class YoutubeSearchIE(InfoExtractor):
     _max_youtube_results = 1000
     IE_NAME = u'youtube:search'
 
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
-
     def report_download_page(self, query, pagenum):
         """Report attempt to download search page with given number."""
         query = query.decode(preferredencoding())
@@ -1521,7 +1477,7 @@ class YoutubeSearchIE(InfoExtractor):
             api_response = json.loads(data)['data']
 
             if not 'items' in api_response:
-                self._downloader.trouble(u'[youtube] No video results')
+                self._downloader.report_error(u'[youtube] No video results')
                 return
 
             new_ids = list(video['id'] for video in api_response['items'])
@@ -1545,9 +1501,6 @@ class GoogleSearchIE(InfoExtractor):
     _max_google_results = 1000
     IE_NAME = u'video.google:search'
 
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
-
     def report_download_page(self, query, pagenum):
         """Report attempt to download playlist page with given number."""
         query = query.decode(preferredencoding())
@@ -1629,9 +1582,6 @@ class YahooSearchIE(InfoExtractor):
     _max_yahoo_results = 1000
     IE_NAME = u'video.yahoo:search'
 
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
-
     def report_download_page(self, query, pagenum):
         """Report attempt to download playlist page with given number."""
         query = query.decode(preferredencoding())
@@ -1725,18 +1675,11 @@ class YoutubePlaylistIE(InfoExtractor):
     _MAX_RESULTS = 50
     IE_NAME = u'youtube:playlist'
 
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
-
     @classmethod
     def suitable(cls, url):
         """Receives a URL and returns True if suitable for this IE."""
         return re.match(cls._VALID_URL, url, re.VERBOSE) is not None
 
-    def report_download_page(self, playlist_id, pagenum):
-        """Report attempt to download playlist page with given number."""
-        self._downloader.to_screen(u'[youtube] PL %s: Downloading page #%s' % (playlist_id, pagenum))
-
     def _real_extract(self, url):
         # Extract playlist id
         mobj = re.match(self._VALID_URL, url, re.VERBOSE)
@@ -1750,14 +1693,8 @@ class YoutubePlaylistIE(InfoExtractor):
         videos = []
 
         while True:
-            self.report_download_page(playlist_id, page_num)
-
             url = self._TEMPLATE_URL % (playlist_id, self._MAX_RESULTS, self._MAX_RESULTS * (page_num - 1) + 1)
-            try:
-                page = compat_urllib_request.urlopen(url).read().decode('utf8')
-            except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-                self._downloader.report_error(u'unable to download webpage: %s' % compat_str(err))
-                return
+            page = self._download_webpage(url, playlist_id, u'Downloading page #%s' % page_num)
 
             try:
                 response = json.loads(page)
@@ -1768,12 +1705,11 @@ class YoutubePlaylistIE(InfoExtractor):
             if 'feed' not in response:
                 self._downloader.report_error(u'Got a malformed response from YouTube API')
                 return
+            playlist_title = response['feed']['title']['$t']
             if 'entry' not in response['feed']:
                 # Number of videos is a multiple of self._MAX_RESULTS
                 break
 
-            playlist_title = response['feed']['title']['$t']
-
             videos += [ (entry['yt$position']['$t'], entry['content']['src'])
                         for entry in response['feed']['entry']
                         if 'content' in entry ]
@@ -1797,10 +1733,6 @@ class YoutubeChannelIE(InfoExtractor):
     _MORE_PAGES_URL = 'http://www.youtube.com/channel_ajax?action_load_more_videos=1&flow=list&paging=%s&view=0&sort=da&channel_id=%s'
     IE_NAME = u'youtube:channel'
 
-    def report_download_page(self, channel_id, pagenum):
-        """Report attempt to download channel page with given number."""
-        self._downloader.to_screen(u'[youtube] Channel %s: Downloading page #%s' % (channel_id, pagenum))
-
     def extract_videos_from_page(self, page):
         ids_in_page = []
         for mobj in re.finditer(r'href="/watch\?v=([0-9A-Za-z_-]+)&?', page):
@@ -1820,14 +1752,9 @@ class YoutubeChannelIE(InfoExtractor):
         video_ids = []
         pagenum = 1
 
-        self.report_download_page(channel_id, pagenum)
         url = self._TEMPLATE_URL % (channel_id, pagenum)
-        request = compat_urllib_request.Request(url)
-        try:
-            page = compat_urllib_request.urlopen(request).read().decode('utf8')
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-            self._downloader.report_error(u'unable to download webpage: %s' % compat_str(err))
-            return
+        page = self._download_webpage(url, channel_id,
+                                      u'Downloading page #%s' % pagenum)
 
         # Extract video identifiers
         ids_in_page = self.extract_videos_from_page(page)
@@ -1838,14 +1765,9 @@ class YoutubeChannelIE(InfoExtractor):
             while True:
                 pagenum = pagenum + 1
 
-                self.report_download_page(channel_id, pagenum)
                 url = self._MORE_PAGES_URL % (pagenum, channel_id)
-                request = compat_urllib_request.Request(url)
-                try:
-                    page = compat_urllib_request.urlopen(request).read().decode('utf8')
-                except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-                    self._downloader.report_error(u'unable to download webpage: %s' % compat_str(err))
-                    return
+                page = self._download_webpage(url, channel_id,
+                                              u'Downloading page #%s' % pagenum)
 
                 page = json.loads(page)
 
@@ -1872,14 +1794,6 @@ class YoutubeUserIE(InfoExtractor):
     _VIDEO_INDICATOR = r'/watch\?v=(.+?)[\<&]'
     IE_NAME = u'youtube:user'
 
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
-
-    def report_download_page(self, username, start_index):
-        """Report attempt to download user page."""
-        self._downloader.to_screen(u'[youtube] user %s: Downloading video ids from %d to %d' %
-                (username, start_index, start_index + self._GDATA_PAGE_SIZE))
-
     def _real_extract(self, url):
         # Extract username
         mobj = re.match(self._VALID_URL, url)
@@ -1899,15 +1813,10 @@ class YoutubeUserIE(InfoExtractor):
 
         while True:
             start_index = pagenum * self._GDATA_PAGE_SIZE + 1
-            self.report_download_page(username, start_index)
 
-            request = compat_urllib_request.Request(self._GDATA_URL % (username, self._GDATA_PAGE_SIZE, start_index))
-
-            try:
-                page = compat_urllib_request.urlopen(request).read().decode('utf-8')
-            except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-                self._downloader.report_error(u'unable to download webpage: %s' % compat_str(err))
-                return
+            gdata_url = self._GDATA_URL % (username, self._GDATA_PAGE_SIZE, start_index)
+            page = self._download_webpage(gdata_url, username,
+                                          u'Downloading video ids from %d to %d' % (start_index, start_index + self._GDATA_PAGE_SIZE))
 
             # Extract video identifiers
             ids_in_page = []
@@ -1941,14 +1850,6 @@ class BlipTVUserIE(InfoExtractor):
     _PAGE_SIZE = 12
     IE_NAME = u'blip.tv:user'
 
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
-
-    def report_download_page(self, username, pagenum):
-        """Report attempt to download user page."""
-        self.to_screen(u'user %s: Downloading video ids from page %d' %
-                (username, pagenum))
-
     def _real_extract(self, url):
         # Extract username
         mobj = re.match(self._VALID_URL, url)
@@ -1960,15 +1861,9 @@ class BlipTVUserIE(InfoExtractor):
 
         page_base = 'http://m.blip.tv/pr/show_get_full_episode_list?users_id=%s&lite=0&esi=1'
 
-        request = compat_urllib_request.Request(url)
-
-        try:
-            page = compat_urllib_request.urlopen(request).read().decode('utf-8')
-            mobj = re.search(r'data-users-id="([^"]+)"', page)
-            page_base = page_base % mobj.group(1)
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-            self._downloader.report_error(u'unable to download webpage: %s' % compat_str(err))
-            return
+        page = self._download_webpage(url, username, u'Downloading user page')
+        mobj = re.search(r'data-users-id="([^"]+)"', page)
+        page_base = page_base % mobj.group(1)
 
 
         # Download video ids using BlipTV Ajax calls. Result size per
@@ -1980,14 +1875,9 @@ class BlipTVUserIE(InfoExtractor):
         pagenum = 1
 
         while True:
-            self.report_download_page(username, pagenum)
             url = page_base + "&page=" + str(pagenum)
-            request = compat_urllib_request.Request( url )
-            try:
-                page = compat_urllib_request.urlopen(request).read().decode('utf-8')
-            except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-                self._downloader.report_error(u'unable to download webpage: %s' % str(err))
-                return
+            page = self._download_webpage(url, username,
+                                          u'Downloading video ids from page %d' % pagenum)
 
             # Extract video identifiers
             ids_in_page = []
@@ -2019,10 +1909,6 @@ class DepositFilesIE(InfoExtractor):
 
     _VALID_URL = r'(?:http://)?(?:\w+\.)?depositfiles\.com/(?:../(?#locale))?files/(.+)'
 
-    def report_download_webpage(self, file_id):
-        """Report webpage download."""
-        self.to_screen(u'%s: Downloading webpage' % file_id)
-
     def _real_extract(self, url):
         file_id = url.split('/')[-1]
         # Rebuild url in english locale
@@ -2273,9 +2159,6 @@ class MyVideoIE(InfoExtractor):
     _VALID_URL = r'(?:http://)?(?:www\.)?myvideo\.de/watch/([0-9]+)/([^?/]+).*'
     IE_NAME = u'myvideo'
 
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
-
     def _real_extract(self,url):
         mobj = re.match(self._VALID_URL, url)
         if mobj is None:
@@ -2353,12 +2236,6 @@ class ComedyCentralIE(InfoExtractor):
         """Receives a URL and returns True if suitable for this IE."""
         return re.match(cls._VALID_URL, url, re.VERBOSE) is not None
 
-    def report_config_download(self, episode_id, media_id):
-        self.to_screen(u'%s: Downloading configuration for %s' % (episode_id, media_id))
-
-    def report_index_download(self, episode_id):
-        self.to_screen(u'%s: Downloading show index' % episode_id)
-
     def _print_formats(self, formats):
         print('Available formats:')
         for x in formats:
@@ -2392,24 +2269,15 @@ class ComedyCentralIE(InfoExtractor):
             else:
                 epTitle = mobj.group('episode')
 
-        req = compat_urllib_request.Request(url)
         self.report_extraction(epTitle)
-        try:
-            htmlHandle = compat_urllib_request.urlopen(req)
-            html = htmlHandle.read()
-            webpage = html.decode('utf-8')
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-            self._downloader.report_error(u'unable to download webpage: %s' % compat_str(err))
-            return
+        webpage,htmlHandle = self._download_webpage_handle(url, epTitle)
         if dlNewest:
             url = htmlHandle.geturl()
             mobj = re.match(self._VALID_URL, url, re.VERBOSE)
             if mobj is None:
-                self._downloader.report_error(u'Invalid redirected URL: ' + url)
-                return
+                raise ExtractorError(u'Invalid redirected URL: ' + url)
             if mobj.group('episode') == '':
-                self._downloader.report_error(u'Redirected URL is still not specific: ' + url)
-                return
+                raise ExtractorError(u'Redirected URL is still not specific: ' + url)
             epTitle = mobj.group('episode')
 
         mMovieParams = re.findall('(?:<param name="movie" value="|var url = ")(http://media.mtvnservices.com/([^"]*(?:episode|video).*?:.*?))"', webpage)
@@ -2421,19 +2289,15 @@ class ComedyCentralIE(InfoExtractor):
 
             altMovieParams = re.findall('data-mgid="([^"]*(?:episode|video).*?:.*?)"', webpage)
             if len(altMovieParams) == 0:
-                self._downloader.report_error(u'unable to find Flash URL in webpage ' + url)
-                return
+                raise ExtractorError(u'unable to find Flash URL in webpage ' + url)
             else:
                 mMovieParams = [("http://media.mtvnservices.com/" + altMovieParams[0], altMovieParams[0])]
 
         uri = mMovieParams[0][1]
         indexUrl = 'http://shadow.comedycentral.com/feeds/video_player/mrss/?' + compat_urllib_parse.urlencode({'uri': uri})
-        self.report_index_download(epTitle)
-        try:
-            indexXml = compat_urllib_request.urlopen(indexUrl).read()
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-            self._downloader.report_error(u'unable to download episode index: ' + compat_str(err))
-            return
+        indexXml = self._download_webpage(indexUrl, epTitle,
+                                          u'Downloading show index',
+                                          u'unable to download episode index')
 
         results = []
 
@@ -2444,17 +2308,12 @@ class ComedyCentralIE(InfoExtractor):
             shortMediaId = mediaId.split(':')[-1]
             showId = mediaId.split(':')[-2].replace('.com', '')
             officialTitle = itemEl.findall('./title')[0].text
-            officialDate = itemEl.findall('./pubDate')[0].text
+            officialDate = unified_strdate(itemEl.findall('./pubDate')[0].text)
 
             configUrl = ('http://www.comedycentral.com/global/feeds/entertainment/media/mediaGenEntertainment.jhtml?' +
                         compat_urllib_parse.urlencode({'uri': mediaId}))
-            configReq = compat_urllib_request.Request(configUrl)
-            self.report_config_download(epTitle, shortMediaId)
-            try:
-                configXml = compat_urllib_request.urlopen(configReq).read()
-            except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-                self._downloader.report_error(u'unable to download webpage: %s' % compat_str(err))
-                return
+            configXml = self._download_webpage(configUrl, epTitle,
+                                               u'Downloading configuration for %s' % shortMediaId)
 
             cdoc = xml.etree.ElementTree.fromstring(configXml)
             turls = []
@@ -2511,9 +2370,6 @@ class EscapistIE(InfoExtractor):
     _VALID_URL = r'^(https?://)?(www\.)?escapistmagazine\.com/videos/view/(?P<showname>[^/]+)/(?P<episode>[^/?]+)[/?]?.*$'
     IE_NAME = u'escapist'
 
-    def report_config_download(self, showName):
-        self.to_screen(u'%s: Downloading configuration' % showName)
-
     def _real_extract(self, url):
         mobj = re.match(self._VALID_URL, url)
         if mobj is None:
@@ -2523,14 +2379,7 @@ class EscapistIE(InfoExtractor):
         videoId = mobj.group('episode')
 
         self.report_extraction(showName)
-        try:
-            webPage = compat_urllib_request.urlopen(url)
-            webPageBytes = webPage.read()
-            m = re.match(r'text/html; charset="?([^"]+)"?', webPage.headers['Content-Type'])
-            webPage = webPageBytes.decode(m.group(1) if m else 'utf-8')
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-            self._downloader.report_error(u'unable to download webpage: ' + compat_str(err))
-            return
+        webPage = self._download_webpage(url, showName)
 
         descMatch = re.search('<meta name="description" content="([^"]*)"', webPage)
         description = unescapeHTML(descMatch.group(1))
@@ -2541,14 +2390,9 @@ class EscapistIE(InfoExtractor):
         configUrlMatch = re.search('config=(.*)$', playerUrl)
         configUrl = compat_urllib_parse.unquote(configUrlMatch.group(1))
 
-        self.report_config_download(showName)
-        try:
-            configJSON = compat_urllib_request.urlopen(configUrl)
-            m = re.match(r'text/html; charset="?([^"]+)"?', configJSON.headers['Content-Type'])
-            configJSON = configJSON.read().decode(m.group(1) if m else 'utf-8')
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-            self._downloader.report_error(u'unable to download configuration: ' + compat_str(err))
-            return
+        configJSON = self._download_webpage(configUrl, showName,
+                                            u'Downloading configuration',
+                                            u'unable to download configuration')
 
         # Technically, it's JavaScript, not JSON
         configJSON = configJSON.replace("'", '"')
@@ -2711,9 +2555,6 @@ class SoundcloudIE(InfoExtractor):
     _VALID_URL = r'^(?:https?://)?(?:www\.)?soundcloud\.com/([\w\d-]+)/([\w\d-]+)'
     IE_NAME = u'soundcloud'
 
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
-
     def report_resolve(self, video_id):
         """Report information extraction."""
         self.to_screen(u'%s: Resolving id' % video_id)
@@ -2729,40 +2570,32 @@ class SoundcloudIE(InfoExtractor):
         # extract simple title (uploader + slug of song title)
         slug_title =  mobj.group(2)
         simple_title = uploader + u'-' + slug_title
+        full_title = '%s/%s' % (uploader, slug_title)
 
-        self.report_resolve('%s/%s' % (uploader, slug_title))
+        self.report_resolve(full_title)
 
         url = 'http://soundcloud.com/%s/%s' % (uploader, slug_title)
         resolv_url = 'http://api.soundcloud.com/resolve.json?url=' + url + '&client_id=b45b1aa10f1ac2941910a7f0d10f8e28'
-        request = compat_urllib_request.Request(resolv_url)
-        try:
-            info_json_bytes = compat_urllib_request.urlopen(request).read()
-            info_json = info_json_bytes.decode('utf-8')
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-            self._downloader.report_error(u'unable to download video webpage: %s' % compat_str(err))
-            return
+        info_json = self._download_webpage(resolv_url, full_title, u'Downloading info JSON')
 
         info = json.loads(info_json)
         video_id = info['id']
-        self.report_extraction('%s/%s' % (uploader, slug_title))
+        self.report_extraction(full_title)
 
         streams_url = 'https://api.sndcdn.com/i1/tracks/' + str(video_id) + '/streams?client_id=b45b1aa10f1ac2941910a7f0d10f8e28'
-        request = compat_urllib_request.Request(streams_url)
-        try:
-            stream_json_bytes = compat_urllib_request.urlopen(request).read()
-            stream_json = stream_json_bytes.decode('utf-8')
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-            self._downloader.report_error(u'unable to download stream definitions: %s' % compat_str(err))
-            return
+        stream_json = self._download_webpage(streams_url, full_title,
+                                             u'Downloading stream definitions',
+                                             u'unable to download stream definitions')
 
         streams = json.loads(stream_json)
         mediaURL = streams['http_mp3_128_url']
+        upload_date = unified_strdate(info['created_at'])
 
         return [{
             'id':       info['id'],
             'url':      mediaURL,
             'uploader': info['user']['username'],
-            'upload_date':  info['created_at'],
+            'upload_date': upload_date,
             'title':    info['title'],
             'ext':      u'mp3',
             'description': info['description'],
@@ -2778,10 +2611,7 @@ class SoundcloudSetIE(InfoExtractor):
      """
 
     _VALID_URL = r'^(?:https?://)?(?:www\.)?soundcloud\.com/([\w\d-]+)/sets/([\w\d-]+)'
-    IE_NAME = u'soundcloud'
-
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
+    IE_NAME = u'soundcloud:set'
 
     def report_resolve(self, video_id):
         """Report information extraction."""
@@ -2790,7 +2620,7 @@ class SoundcloudSetIE(InfoExtractor):
     def _real_extract(self, url):
         mobj = re.match(self._VALID_URL, url)
         if mobj is None:
-            self._downloader.trouble(u'ERROR: invalid URL: %s' % url)
+            self._downloader.report_error(u'invalid URL: %s' % url)
             return
 
         # extract uploader (which is in the url)
@@ -2798,39 +2628,29 @@ class SoundcloudSetIE(InfoExtractor):
         # extract simple title (uploader + slug of song title)
         slug_title =  mobj.group(2)
         simple_title = uploader + u'-' + slug_title
+        full_title = '%s/sets/%s' % (uploader, slug_title)
 
-        self.report_resolve('%s/sets/%s' % (uploader, slug_title))
+        self.report_resolve(full_title)
 
         url = 'http://soundcloud.com/%s/sets/%s' % (uploader, slug_title)
         resolv_url = 'http://api.soundcloud.com/resolve.json?url=' + url + '&client_id=b45b1aa10f1ac2941910a7f0d10f8e28'
-        request = compat_urllib_request.Request(resolv_url)
-        try:
-            info_json_bytes = compat_urllib_request.urlopen(request).read()
-            info_json = info_json_bytes.decode('utf-8')
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-            self._downloader.trouble(u'ERROR: unable to download video webpage: %s' % compat_str(err))
-            return
+        info_json = self._download_webpage(resolv_url, full_title)
 
         videos = []
         info = json.loads(info_json)
         if 'errors' in info:
             for err in info['errors']:
-                self._downloader.trouble(u'ERROR: unable to download video webpage: %s' % compat_str(err['error_message']))
+                self._downloader.report_error(u'unable to download video webpage: %s' % compat_str(err['error_message']))
             return
 
+        self.report_extraction(full_title)
         for track in info['tracks']:
             video_id = track['id']
-            self.report_extraction('%s/sets/%s' % (uploader, slug_title))
 
             streams_url = 'https://api.sndcdn.com/i1/tracks/' + str(video_id) + '/streams?client_id=b45b1aa10f1ac2941910a7f0d10f8e28'
-            request = compat_urllib_request.Request(streams_url)
-            try:
-                stream_json_bytes = compat_urllib_request.urlopen(request).read()
-                stream_json = stream_json_bytes.decode('utf-8')
-            except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-                self._downloader.trouble(u'ERROR: unable to download stream definitions: %s' % compat_str(err))
-                return
+            stream_json = self._download_webpage(streams_url, video_id, u'Downloading track info JSON')
 
+            self.report_extraction(video_id)
             streams = json.loads(stream_json)
             mediaURL = streams['http_mp3_128_url']
 
@@ -2838,7 +2658,7 @@ class SoundcloudSetIE(InfoExtractor):
                 'id':       video_id,
                 'url':      mediaURL,
                 'uploader': track['user']['username'],
-                'upload_date':  track['created_at'],
+                'upload_date':  unified_strdate(track['created_at']),
                 'title':    track['title'],
                 'ext':      u'mp3',
                 'description': track['description'],
@@ -2860,7 +2680,7 @@ class InfoQIE(InfoExtractor):
         self.report_extraction(url)
 
         # Extract video URL
-        mobj = re.search(r"jsclassref='([^']*)'", webpage)
+        mobj = re.search(r"jsclassref ?= ?'([^']*)'", webpage)
         if mobj is None:
             self._downloader.report_error(u'unable to extract video url')
             return
@@ -2903,9 +2723,6 @@ class MixcloudIE(InfoExtractor):
     _VALID_URL = r'^(?:https?://)?(?:www\.)?mixcloud\.com/([\w\d-]+)/([\w\d-]+)'
     IE_NAME = u'mixcloud'
 
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
-
     def report_download_json(self, file_id):
         """Report JSON download."""
         self.to_screen(u'Downloading json')
@@ -3013,10 +2830,6 @@ class StanfordOpenClassroomIE(InfoExtractor):
     _VALID_URL = r'^(?:https?://)?openclassroom.stanford.edu(?P<path>/?|(/MainFolder/(?:HomePage|CoursePage|VideoPage)\.php([?]course=(?P<course>[^&]+)(&video=(?P<video>[^&]+))?(&.*)?)?))$'
     IE_NAME = u'stanfordoc'
 
-    def report_download_webpage(self, objid):
-        """Report information extraction."""
-        self.to_screen(u'%s: Downloading webpage' % objid)
-
     def _real_extract(self, url):
         mobj = re.match(self._VALID_URL, url)
         if mobj is None:
@@ -3176,7 +2989,7 @@ class MTVIE(InfoExtractor):
             format = ext + '-' + rendition.attrib['width'] + 'x' + rendition.attrib['height'] + '_' + rendition.attrib['bitrate']
             video_url = rendition.find('./src').text
         except KeyError:
-            self._downloader.trouble('Invalid rendition field.')
+            self._downloader.report_error('Invalid rendition field.')
             return
 
         info = {
@@ -3195,10 +3008,6 @@ class MTVIE(InfoExtractor):
 class YoukuIE(InfoExtractor):
     _VALID_URL =  r'(?:http://)?v\.youku\.com/v_show/id_(?P<ID>[A-Za-z0-9]+)\.html'
 
-    def report_download_webpage(self, file_id):
-        """Report webpage download."""
-        self.to_screen(u'%s: Downloading webpage' % file_id)
-
     def _gen_sid(self):
         nowTime = int(time.time() * 1000)
         random1 = random.randint(1000,1998)
@@ -3236,18 +3045,11 @@ class YoukuIE(InfoExtractor):
 
         info_url = 'http://v.youku.com/player/getPlayList/VideoIDS/' + video_id
 
-        request = compat_urllib_request.Request(info_url, None, std_headers)
-        try:
-            self.report_download_webpage(video_id)
-            jsondata = compat_urllib_request.urlopen(request).read()
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-            self._downloader.report_error(u'Unable to retrieve video webpage: %s' % compat_str(err))
-            return
+        jsondata = self._download_webpage(info_url, video_id)
 
         self.report_extraction(video_id)
         try:
-            jsonstr = jsondata.decode('utf-8')
-            config = json.loads(jsonstr)
+            config = json.loads(jsondata)
 
             video_title =  config['data'][0]['title']
             seed = config['data'][0]['seed']
@@ -3308,10 +3110,6 @@ class XNXXIE(InfoExtractor):
     VIDEO_TITLE_RE = r'<title>(.*?)\s+-\s+XNXX.COM'
     VIDEO_THUMB_RE = r'url_bigthumb=(.*?)&amp;'
 
-    def report_webpage(self, video_id):
-        """Report information extraction"""
-        self.to_screen(u'%s: Downloading webpage' % video_id)
-
     def _real_extract(self, url):
         mobj = re.match(self._VALID_URL, url)
         if mobj is None:
@@ -3319,15 +3117,8 @@ class XNXXIE(InfoExtractor):
             return
         video_id = mobj.group(1)
 
-        self.report_webpage(video_id)
-
         # Get webpage content
-        try:
-            webpage_bytes = compat_urllib_request.urlopen(url).read()
-            webpage = webpage_bytes.decode('utf-8')
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-            self._downloader.report_error(u'unable to download video webpage: %s' % err)
-            return
+        webpage = self._download_webpage(url, video_id)
 
         result = re.search(self.VIDEO_URL_RE, webpage)
         if result is None:
@@ -3365,9 +3156,6 @@ class GooglePlusIE(InfoExtractor):
     _VALID_URL = r'(?:https://)?plus\.google\.com/(?:[^/]+/)*?posts/(\w+)'
     IE_NAME = u'plus.google'
 
-    def __init__(self, downloader=None):
-        InfoExtractor.__init__(self, downloader)
-
     def report_extract_entry(self, url):
         """Report downloading extry"""
         self.to_screen(u'Downloading entry: %s' % url)
@@ -3402,12 +3190,7 @@ class GooglePlusIE(InfoExtractor):
 
         # Step 1, Retrieve post webpage to extract further information
         self.report_extract_entry(post_url)
-        request = compat_urllib_request.Request(post_url)
-        try:
-            webpage = compat_urllib_request.urlopen(request).read().decode('utf-8')
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-            self._downloader.report_error(u'Unable to retrieve entry webpage: %s' % compat_str(err))
-            return
+        webpage = self._download_webpage(post_url, video_id, u'Downloading entry webpage')
 
         # Extract update date
         upload_date = None
@@ -3444,12 +3227,7 @@ class GooglePlusIE(InfoExtractor):
             self._downloader.report_error(u'unable to extract video page URL')
 
         video_page = mobj.group(1)
-        request = compat_urllib_request.Request(video_page)
-        try:
-            webpage = compat_urllib_request.urlopen(request).read().decode('utf-8')
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-            self._downloader.report_error(u'Unable to retrieve video webpage: %s' % compat_str(err))
-            return
+        webpage = self._download_webpage(video_page, video_id, u'Downloading video page')
         self.report_extract_vid_page(video_page)
 
 
@@ -3536,14 +3314,10 @@ class JustinTVIE(InfoExtractor):
                 (channel, offset, offset + self._JUSTIN_PAGE_LIMIT))
 
     # Return count of items, list of *valid* items
-    def _parse_page(self, url):
-        try:
-            urlh = compat_urllib_request.urlopen(url)
-            webpage_bytes = urlh.read()
-            webpage = webpage_bytes.decode('utf-8', 'ignore')
-        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
-            self._downloader.report_error(u'unable to download video info JSON: %s' % compat_str(err))
-            return
+    def _parse_page(self, url, video_id):
+        webpage = self._download_webpage(url, video_id,
+                                         u'Downloading video info JSON',
+                                         u'unable to download video info JSON')
 
         response = json.loads(webpage)
         if type(response) != list:
@@ -3595,7 +3369,7 @@ class JustinTVIE(InfoExtractor):
             if paged:
                 self.report_download_page(video_id, offset)
             page_url = api + ('?offset=%d&limit=%d' % (offset, limit))
-            page_count, page_info = self._parse_page(page_url)
+            page_count, page_info = self._parse_page(page_url, video_id)
             info.extend(page_info)
             if not paged or page_count != limit:
                 break
@@ -3623,7 +3397,7 @@ class FunnyOrDieIE(InfoExtractor):
         if not m:
             m = re.search(r'<title>(?P<title>[^<]+?)</title>', webpage)
             if not m:
-                self._downloader.trouble(u'Cannot find video title')
+                self._downloader.report_error(u'Cannot find video title')
         title = clean_html(m.group('title'))
 
         m = re.search(r'<meta property="og:description" content="(?P<desc>.*?)"', webpage)
@@ -3642,7 +3416,8 @@ class FunnyOrDieIE(InfoExtractor):
         return [info]
 
 class SteamIE(InfoExtractor):
-    _VALID_URL = r"""http://store.steampowered.com/
+    _VALID_URL = r"""http://store\.steampowered\.com/
+                (agecheck/)?
                 (?P<urltype>video|app)/ #If the page is only for videos or for a game
                 (?P<gameID>\d+)/?
                 (?P<videoID>\d*)(?P<extra>\??) #For urltype == video we sometimes get the videoID
@@ -3712,24 +3487,23 @@ class WorldStarHipHopIE(InfoExtractor):
     IE_NAME = u'WorldStarHipHop'
 
     def _real_extract(self, url):
-        _src_url = r"""(http://hw-videos.*(?:mp4|flv))"""
-
-        webpage_src = compat_urllib_request.urlopen(url).read()
-        webpage_src = webpage_src.decode('utf-8')
-
-        mobj = re.search(_src_url, webpage_src)
+        _src_url = r'so\.addVariable\("file","(.*?)"\)'
 
         m = re.match(self._VALID_URL, url)
         video_id = m.group('id')
 
+        webpage_src = self._download_webpage(url, video_id) 
+
+        mobj = re.search(_src_url, webpage_src)
+
         if mobj is not None:
-            video_url = mobj.group()
+            video_url = mobj.group(1)
             if 'mp4' in video_url:
                 ext = 'mp4'
             else:
                 ext = 'flv'
         else:
-            self._downloader.trouble(u'ERROR: Cannot find video url for %s' % video_id)
+            self._downloader.report_error(u'Cannot find video url for %s' % video_id)
             return
 
         _title = r"""<title>(.*)</title>"""
@@ -3841,7 +3615,7 @@ class YouPornIE(InfoExtractor):
             self._downloader.report_warning(u'unable to extract video date')
             upload_date = None
         else:
-            upload_date = result.group('date').strip()
+            upload_date = unified_strdate(result.group('date').strip())
 
         # Get the video uploader
         result = re.search(r'Submitted:</label>(?P<uploader>.*)</li>', webpage)
@@ -3948,7 +3722,7 @@ class PornotubeIE(InfoExtractor):
         if result is None:
             self._downloader.report_error(u'unable to extract video title')
             return
-        upload_date = result.group('date')
+        upload_date = unified_strdate(result.group('date'))
 
         info = {'id': video_id,
                 'url': video_url,
@@ -4075,12 +3849,13 @@ class KeekIE(InfoExtractor):
         return [info]
 
 class TEDIE(InfoExtractor):
-    _VALID_URL=r'''http://www.ted.com/
+    _VALID_URL=r'''http://www\.ted\.com/
                    (
                         ((?P<type_playlist>playlists)/(?P<playlist_id>\d+)) # We have a playlist
                         |
                         ((?P<type_talk>talks)) # We have a simple talk
                    )
+                   (/lang/(.*?))? # The url may contain the language
                    /(?P<name>\w+) # Here goes the name and then ".html"
                    '''
 
@@ -4249,7 +4024,7 @@ class LiveLeakIE(InfoExtractor):
     def _real_extract(self, url):
         mobj = re.match(self._VALID_URL, url)
         if mobj is None:
-            self._downloader.trouble(u'ERROR: invalid URL: %s' % url)
+            self._downloader.report_error(u'invalid URL: %s' % url)
             return
 
         video_id = mobj.group('video_id')
@@ -4264,7 +4039,7 @@ class LiveLeakIE(InfoExtractor):
 
         m = re.search(r'<meta property="og:title" content="(?P<title>.*?)"', webpage)
         if not m:
-            self._downloader.trouble(u'Cannot find video title')
+            self._downloader.report_error(u'Cannot find video title')
         title = unescapeHTML(m.group('title')).replace('LiveLeak.com -', '').strip()
 
         m = re.search(r'<meta property="og:description" content="(?P<desc>.*?)"', webpage)
@@ -4330,6 +4105,117 @@ class ARDIE(InfoExtractor):
             info["url"] = stream["video_url"]
         return [info]
 
+class TumblrIE(InfoExtractor):
+    _VALID_URL = r'http://(?P<blog_name>.*?)\.tumblr\.com/((post)|(video))/(?P<id>\d*)/(.*?)'
+
+    def _real_extract(self, url):
+        m_url = re.match(self._VALID_URL, url)
+        video_id = m_url.group('id')
+        blog = m_url.group('blog_name')
+
+        url = 'http://%s.tumblr.com/post/%s/' % (blog, video_id)
+        webpage = self._download_webpage(url, video_id)
+
+        re_video = r'src=\\x22(?P<video_url>http://%s\.tumblr\.com/video_file/%s/(.*?))\\x22 type=\\x22video/(?P<ext>.*?)\\x22' % (blog, video_id)
+        video = re.search(re_video, webpage)
+        if video is None:
+            self.to_screen("No video founded")
+            return []
+        video_url = video.group('video_url')
+        ext = video.group('ext')
+
+        re_thumb = r'posters(.*?)\[\\x22(?P<thumb>.*?)\\x22'  # We pick the first poster
+        thumb = re.search(re_thumb, webpage).group('thumb').replace('\\', '')
+
+        # The only place where you can get a title, it's not complete,
+        # but searching in other places doesn't work for all videos
+        re_title = r'<title>(?P<title>.*?)</title>'
+        title = unescapeHTML(re.search(re_title, webpage, re.DOTALL).group('title'))
+
+        return [{'id': video_id,
+                 'url': video_url,
+                 'title': title,
+                 'thumbnail': thumb,
+                 'ext': ext
+                 }]
+
+class BandcampIE(InfoExtractor):
+    _VALID_URL = r'http://.*?\.bandcamp\.com/track/(?P<title>.*)'
+
+    def _real_extract(self, url):
+        mobj = re.match(self._VALID_URL, url)
+        title = mobj.group('title')
+        webpage = self._download_webpage(url, title)
+        # We get the link to the free download page
+        m_download = re.search(r'freeDownloadPage: "(.*?)"', webpage)
+        if m_download is None:
+            self._downloader.report_error('No free songs founded')
+            return
+        download_link = m_download.group(1)
+        id = re.search(r'var TralbumData = {(.*?)id: (?P<id>\d*?)$', 
+                       webpage, re.MULTILINE|re.DOTALL).group('id')
+
+        download_webpage = self._download_webpage(download_link, id,
+                                                  'Downloading free downloads page')
+        # We get the dictionary of the track from some javascrip code
+        info = re.search(r'items: (.*?),$',
+                         download_webpage, re.MULTILINE).group(1)
+        info = json.loads(info)[0]
+        # We pick mp3-320 for now, until format selection can be easily implemented.
+        mp3_info = info[u'downloads'][u'mp3-320']
+        # If we try to use this url it says the link has expired
+        initial_url = mp3_info[u'url']
+        re_url = r'(?P<server>http://(.*?)\.bandcamp\.com)/download/track\?enc=mp3-320&fsig=(?P<fsig>.*?)&id=(?P<id>.*?)&ts=(?P<ts>.*)$'
+        m_url = re.match(re_url, initial_url)
+        #We build the url we will use to get the final track url
+        # This url is build in Bandcamp in the script download_bunde_*.js
+        request_url = '%s/statdownload/track?enc=mp3-320&fsig=%s&id=%s&ts=%s&.rand=665028774616&.vrs=1' % (m_url.group('server'), m_url.group('fsig'), id, m_url.group('ts'))
+        final_url_webpage = self._download_webpage(request_url, id, 'Requesting download url')
+        # If we could correctly generate the .rand field the url would be
+        #in the "download_url" key
+        final_url = re.search(r'"retry_url":"(.*?)"', final_url_webpage).group(1)
+
+        track_info = {'id':id,
+                      'title' : info[u'title'],
+                      'ext' : 'mp3',
+                      'url' : final_url,
+                      'thumbnail' : info[u'thumb_url'],
+                      'uploader' : info[u'artist']
+                      }
+
+        return [track_info]
+
+class RedTubeIE(InfoExtractor):
+    """Information Extractor for redtube"""
+    _VALID_URL = r'(?:http://)?(?:www\.)?redtube\.com/(?P<id>[0-9]+)'
+
+    def _real_extract(self,url):
+        mobj = re.match(self._VALID_URL, url)
+        if mobj is None:
+            raise ExtractorError(u'Invalid URL: %s' % url)
+
+        video_id = mobj.group('id')
+        video_extension = 'mp4'        
+        webpage = self._download_webpage(url, video_id)
+        self.report_extraction(video_id)
+        mobj = re.search(r'<source src="'+'(.+)'+'" type="video/mp4">',webpage)
+
+        if mobj is None:
+            raise ExtractorError(u'Unable to extract media URL')
+
+        video_url = mobj.group(1)
+        mobj = re.search('<h1 class="videoTitle slidePanelMovable">(.+)</h1>',webpage)
+        if mobj is None:
+            raise ExtractorError(u'Unable to extract title')
+        video_title = mobj.group(1)
+
+        return [{
+            'id':       video_id,
+            'url':      video_url,
+            'ext':      video_extension,
+            'title':    video_title,
+        }]
+
 
 def gen_extractors():
     """ Return a list of an instance of every supported extractor.
@@ -4384,6 +4270,9 @@ def gen_extractors():
         SpiegelIE(),
         LiveLeakIE(),
         ARDIE(),
+        TumblrIE(),
+        BandcampIE(),
+        RedTubeIE(),
         GenericIE()
     ]