diff --git a/test/test_utils.py b/test/test_utils.py index a3a23fbb4..b8fc13aef 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -1,15 +1,22 @@ -# -*- coding: utf-8 -*- - # Various small unit tests +import sys import unittest +# Allow direct execution +import os +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + #from youtube_dl.utils import htmlentity_transform from youtube_dl.utils import timeconvert from youtube_dl.utils import sanitize_filename from youtube_dl.utils import unescapeHTML from youtube_dl.utils import orderedSet +if sys.version < (3,0): + _compat_str = lambda b: b.decode('unicode-escape') +else: + _compat_str = lambda s: s class TestUtil(unittest.TestCase): def test_timeconvert(self): @@ -17,56 +24,59 @@ class TestUtil(unittest.TestCase): self.assertTrue(timeconvert('bougrg') is None) def test_sanitize_filename(self): - self.assertEqual(sanitize_filename(u'abc'), u'abc') - self.assertEqual(sanitize_filename(u'abc_d-e'), u'abc_d-e') + self.assertEqual(sanitize_filename('abc'), 'abc') + self.assertEqual(sanitize_filename('abc_d-e'), 'abc_d-e') - self.assertEqual(sanitize_filename(u'123'), u'123') + self.assertEqual(sanitize_filename('123'), '123') - self.assertEqual(u'abc_de', sanitize_filename(u'abc/de')) - self.assertFalse(u'/' in sanitize_filename(u'abc/de///')) + self.assertEqual('abc_de', sanitize_filename('abc/de')) + self.assertFalse('/' in sanitize_filename('abc/de///')) - self.assertEqual(u'abc_de', sanitize_filename(u'abc/<>\\*|de')) - self.assertEqual(u'xxx', sanitize_filename(u'xxx/<>\\*|')) - self.assertEqual(u'yes no', sanitize_filename(u'yes? no')) - self.assertEqual(u'this - that', sanitize_filename(u'this: that')) + self.assertEqual('abc_de', sanitize_filename('abc/<>\\*|de')) + self.assertEqual('xxx', sanitize_filename('xxx/<>\\*|')) + self.assertEqual('yes no', sanitize_filename('yes? no')) + self.assertEqual('this - that', sanitize_filename('this: that')) - self.assertEqual(sanitize_filename(u'AT&T'), u'AT&T') - self.assertEqual(sanitize_filename(u'ä'), u'ä') - self.assertEqual(sanitize_filename(u'кириллица'), u'кириллица') + self.assertEqual(sanitize_filename('AT&T'), 'AT&T') + aumlaut = _compat_str('\xe4') + self.assertEqual(sanitize_filename(aumlaut), aumlaut) + tests = _compat_str('\u043a\u0438\u0440\u0438\u043b\u043b\u0438\u0446\u0430') + self.assertEqual(sanitize_filename(tests), tests) - forbidden = u'"\0\\/' + forbidden = '"\0\\/' for fc in forbidden: for fbc in forbidden: self.assertTrue(fbc not in sanitize_filename(fc)) def test_sanitize_filename_restricted(self): - self.assertEqual(sanitize_filename(u'abc', restricted=True), u'abc') - self.assertEqual(sanitize_filename(u'abc_d-e', restricted=True), u'abc_d-e') + self.assertEqual(sanitize_filename('abc', restricted=True), 'abc') + self.assertEqual(sanitize_filename('abc_d-e', restricted=True), 'abc_d-e') - self.assertEqual(sanitize_filename(u'123', restricted=True), u'123') + self.assertEqual(sanitize_filename('123', restricted=True), '123') - self.assertEqual(u'abc_de', sanitize_filename(u'abc/de', restricted=True)) - self.assertFalse(u'/' in sanitize_filename(u'abc/de///', restricted=True)) + self.assertEqual('abc_de', sanitize_filename('abc/de', restricted=True)) + self.assertFalse('/' in sanitize_filename('abc/de///', restricted=True)) - self.assertEqual(u'abc_de', sanitize_filename(u'abc/<>\\*|de', restricted=True)) - self.assertEqual(u'xxx', sanitize_filename(u'xxx/<>\\*|', restricted=True)) - self.assertEqual(u'yes_no', sanitize_filename(u'yes? no', restricted=True)) - self.assertEqual(u'this_-_that', sanitize_filename(u'this: that', restricted=True)) + self.assertEqual('abc_de', sanitize_filename('abc/<>\\*|de', restricted=True)) + self.assertEqual('xxx', sanitize_filename('xxx/<>\\*|', restricted=True)) + self.assertEqual('yes_no', sanitize_filename('yes? no', restricted=True)) + self.assertEqual('this_-_that', sanitize_filename('this: that', restricted=True)) - self.assertEqual(sanitize_filename(u'aäb中国的c', restricted=True), u'a_b_c') - self.assertTrue(sanitize_filename(u'ö', restricted=True) != u'') # No empty filename + tests =_compat_str('a\xe4b\u4e2d\u56fd\u7684c') + self.assertEqual(sanitize_filename(tests, restricted=True), 'a_b_c') + self.assertTrue(sanitize_filename(_compat_str('\xf6'), restricted=True) != '') # No empty filename - forbidden = u'"\0\\/&!: \'\t\n' + forbidden = '"\0\\/&!: \'\t\n' for fc in forbidden: for fbc in forbidden: self.assertTrue(fbc not in sanitize_filename(fc, restricted=True)) # Handle a common case more neatly - self.assertEqual(sanitize_filename(u'大声带 - Song', restricted=True), u'Song') - self.assertEqual(sanitize_filename(u'总统: Speech', restricted=True), u'Speech') + self.assertEqual(sanitize_filename(_compat_str('\u5927\u58f0\u5e26 - Song'), restricted=True), 'Song') + self.assertEqual(sanitize_filename(_compat_str('\u603b\u7edf: Speech'), restricted=True), 'Speech') # .. but make sure the file name is never empty - self.assertTrue(sanitize_filename(u'-', restricted=True) != u'') - self.assertTrue(sanitize_filename(u':', restricted=True) != u'') + self.assertTrue(sanitize_filename('-', restricted=True) != '') + self.assertTrue(sanitize_filename(':', restricted=True) != '') def test_ordered_set(self): self.assertEqual(orderedSet([1,1,2,3,4,4,5,6,7,3,5]), [1,2,3,4,5,6,7]) @@ -76,4 +86,7 @@ class TestUtil(unittest.TestCase): self.assertEqual(orderedSet([135,1,1,1]), [135,1]) def test_unescape_html(self): - self.assertEqual(unescapeHTML(u"%20;"), u"%20;") + self.assertEqual(unescapeHTML(_compat_str('%20;')), _compat_str('%20;')) + +if __name__ == '__main__': + unittest.main() diff --git a/youtube_dl/FileDownloader.py b/youtube_dl/FileDownloader.py index 5a7af2a7a..17f946e71 100644 --- a/youtube_dl/FileDownloader.py +++ b/youtube_dl/FileDownloader.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import httplib import math @@ -97,7 +96,7 @@ class FileDownloader(object): self.params = params if '%(stitle)s' in self.params['outtmpl']: - self.to_stderr(u'WARNING: %(stitle)s is deprecated. Use the %(title)s and the --restrict-filenames flag(which also secures %(uploader)s et al) instead.') + self.to_stderr(u('WARNING: %(stitle)s is deprecated. Use the %(title)s and the --restrict-filenames flag(which also secures %(uploader)s et al) instead.')) @staticmethod def format_bytes(bytes): @@ -175,9 +174,9 @@ class FileDownloader(object): def to_screen(self, message, skip_eol=False): """Print message to stdout if not in quiet mode.""" - assert type(message) == type(u'') + assert type(message) == type(u('')) if not self.params.get('quiet', False): - terminator = [u'\n', u''][skip_eol] + terminator = [u('\n'), u('')][skip_eol] output = message + terminator if 'b' not in self._screen_file.mode or sys.version_info[0] < 3: # Python 2 lies about the mode of sys.stdout/sys.stderr output = output.encode(preferredencoding(), 'ignore') @@ -186,8 +185,8 @@ class FileDownloader(object): def to_stderr(self, message): """Print message to stderr.""" - assert type(message) == type(u'') - sys.stderr.write((message + u'\n').encode(preferredencoding())) + assert type(message) == type(u('')) + sys.stderr.write((message + u('\n')).encode(preferredencoding())) def to_cons_title(self, message): """Set console/terminal window title to message.""" @@ -232,14 +231,14 @@ class FileDownloader(object): def temp_name(self, filename): """Returns a temporary filename for the given filename.""" - if self.params.get('nopart', False) or filename == u'-' or \ + if self.params.get('nopart', False) or filename == u('-') or \ (os.path.exists(encodeFilename(filename)) and not os.path.isfile(encodeFilename(filename))): return filename - return filename + u'.part' + return filename + u('.part') def undo_temp_name(self, filename): - if filename.endswith(u'.part'): - return filename[:-len(u'.part')] + if filename.endswith(u('.part')): + return filename[:-len(u('.part'))] return filename def try_rename(self, old_filename, new_filename): @@ -249,7 +248,7 @@ class FileDownloader(object): os.rename(encodeFilename(old_filename), encodeFilename(new_filename)) except (IOError, OSError): _, err, _ = sys.exc_info() - self.trouble(u'ERROR: unable to rename file') + self.trouble(u('ERROR: unable to rename file')) def try_utime(self, filename, last_modified_hdr): """Try to set the last-modified time of the given file.""" @@ -271,55 +270,55 @@ class FileDownloader(object): def report_writedescription(self, descfn): """ Report that the description file is being written """ - self.to_screen(u'[info] Writing video description to: ' + descfn) + self.to_screen(u('[info] Writing video description to: ') + descfn) def report_writesubtitles(self, srtfn): """ Report that the subtitles file is being written """ - self.to_screen(u'[info] Writing video subtitles to: ' + srtfn) + self.to_screen(u('[info] Writing video subtitles to: ') + srtfn) def report_writeinfojson(self, infofn): """ Report that the metadata file has been written """ - self.to_screen(u'[info] Video description metadata as JSON to: ' + infofn) + self.to_screen(u('[info] Video description metadata as JSON to: ') + infofn) def report_destination(self, filename): """Report destination filename.""" - self.to_screen(u'[download] Destination: ' + filename) + self.to_screen(u('[download] Destination: ') + filename) def report_progress(self, percent_str, data_len_str, speed_str, eta_str): """Report download progress.""" if self.params.get('noprogress', False): return - self.to_screen(u'\r[download] %s of %s at %s ETA %s' % + self.to_screen(u('\r[download] %s of %s at %s ETA %s') % (percent_str, data_len_str, speed_str, eta_str), skip_eol=True) - self.to_cons_title(u'youtube-dl - %s of %s at %s ETA %s' % + self.to_cons_title(u('youtube-dl - %s of %s at %s ETA %s') % (percent_str.strip(), data_len_str.strip(), speed_str.strip(), eta_str.strip())) def report_resuming_byte(self, resume_len): """Report attempt to resume at given byte.""" - self.to_screen(u'[download] Resuming download at byte %s' % resume_len) + self.to_screen(u('[download] Resuming download at byte %s') % resume_len) def report_retry(self, count, retries): """Report retry in case of HTTP error 5xx""" - self.to_screen(u'[download] Got server HTTP error. Retrying (attempt %d of %d)...' % (count, retries)) + self.to_screen(u('[download] Got server HTTP error. Retrying (attempt %d of %d)...') % (count, retries)) def report_file_already_downloaded(self, file_name): """Report file has already been fully downloaded.""" try: - self.to_screen(u'[download] %s has already been downloaded' % file_name) + self.to_screen(u('[download] %s has already been downloaded') % file_name) except (UnicodeEncodeError): _, err, _ = sys.exc_info() - self.to_screen(u'[download] The file has already been downloaded') + self.to_screen(u('[download] The file has already been downloaded')) def report_unable_to_resume(self): """Report it was impossible to resume download.""" - self.to_screen(u'[download] Unable to resume') + self.to_screen(u('[download] Unable to resume')) def report_finish(self): """Report download finished.""" if self.params.get('noprogress', False): - self.to_screen(u'[download] Download completed') + self.to_screen(u('[download] Download completed')) else: - self.to_screen(u'') + self.to_screen(u('')) def increment_downloads(self): """Increment the ordinal that assigns a number to each file.""" @@ -330,14 +329,14 @@ class FileDownloader(object): try: template_dict = dict(info_dict) template_dict['epoch'] = int(time.time()) - template_dict['autonumber'] = u'%05d' % self._num_downloads + template_dict['autonumber'] = u('%05d') % self._num_downloads template_dict = dict((k, sanitize_filename(u(v), self.params.get('restrictfilenames'))) for k,v in template_dict.items()) filename = self.params['outtmpl'] % template_dict return filename except (ValueError, KeyError): _, err, _ = sys.exc_info() - self.trouble(u'ERROR: invalid system charset or erroneous output template') + self.trouble(u('ERROR: invalid system charset or erroneous output template')) return None def _match_entry(self, info_dict): @@ -348,12 +347,12 @@ class FileDownloader(object): if matchtitle: matchtitle = matchtitle.decode('utf8') if not re.search(matchtitle, title, re.IGNORECASE): - return u'[download] "' + title + '" title did not match pattern "' + matchtitle + '"' + return u('[download] "') + title + '" title did not match pattern "' + matchtitle + '"' rejecttitle = self.params.get('rejecttitle', False) if rejecttitle: rejecttitle = rejecttitle.decode('utf8') if re.search(rejecttitle, title, re.IGNORECASE): - return u'"' + title + '" title matched reject pattern "' + rejecttitle + '"' + return u('"') + title + '" title matched reject pattern "' + rejecttitle + '"' return None def process_info(self, info_dict): @@ -364,7 +363,7 @@ class FileDownloader(object): reason = self._match_entry(info_dict) if reason is not None: - self.to_screen(u'[download] ' + reason) + self.to_screen(u('[download] ') + reason) return max_downloads = self.params.get('max_downloads') @@ -401,12 +400,12 @@ class FileDownloader(object): os.makedirs(dn) except (OSError, IOError): _, err, _ = sys.exc_info() - self.trouble(u'ERROR: unable to create directory ' + u(err)) + self.trouble(u('ERROR: unable to create directory ') + u(err)) return if self.params.get('writedescription', False): try: - descfn = filename + u'.description' + descfn = filename + u('.description') self.report_writedescription(descfn) descfile = open(encodeFilename(descfn), 'wb') try: @@ -414,14 +413,14 @@ class FileDownloader(object): finally: descfile.close() except (OSError, IOError): - self.trouble(u'ERROR: Cannot write description file ' + descfn) + self.trouble(u('ERROR: Cannot write description file ') + descfn) return if self.params.get('writesubtitles', False) and 'subtitles' in info_dict and info_dict['subtitles']: # subtitles download errors are already managed as troubles in relevant IE # that way it will silently go on when used with unsupporting IE try: - srtfn = filename.rsplit('.', 1)[0] + u'.srt' + srtfn = filename.rsplit('.', 1)[0] + u('.srt') self.report_writesubtitles(srtfn) srtfile = open(encodeFilename(srtfn), 'wb') try: @@ -429,16 +428,16 @@ class FileDownloader(object): finally: srtfile.close() except (OSError, IOError): - self.trouble(u'ERROR: Cannot write subtitles file ' + descfn) + self.trouble(u('ERROR: Cannot write subtitles file ') + descfn) return if self.params.get('writeinfojson', False): - infofn = filename + u'.info.json' + infofn = filename + u('.info.json') self.report_writeinfojson(infofn) try: json.dump except (NameError,AttributeError): - self.trouble(u'ERROR: No JSON encoder found. Update to Python 2.6+, setup a json module, or leave out --write-info-json.') + self.trouble(u('ERROR: No JSON encoder found. Update to Python 2.6+, setup a json module, or leave out --write-info-json.')) return try: infof = open(encodeFilename(infofn), 'wb') @@ -448,7 +447,7 @@ class FileDownloader(object): finally: infof.close() except (OSError, IOError): - self.trouble(u'ERROR: Cannot write metadata to JSON file ' + infofn) + self.trouble(u('ERROR: Cannot write metadata to JSON file ') + infofn) return if not self.params.get('skip_download', False): @@ -462,11 +461,11 @@ class FileDownloader(object): raise UnavailableVideoError except (urllib2.URLError, httplib.HTTPException, socket.error): _, err, _ = sys.exc_info() - self.trouble(u'ERROR: unable to download video data: %s' % str(err)) + self.trouble(u('ERROR: unable to download video data: %s') % str(err)) return except (ContentTooShortError, ): _, err, _ = sys.exc_info() - self.trouble(u'ERROR: content too short (expected %s bytes and served %s)' % (err.expected, err.downloaded)) + self.trouble(u('ERROR: content too short (expected %s bytes and served %s)') % (err.expected, err.downloaded)) return if success: @@ -474,7 +473,7 @@ class FileDownloader(object): self.post_process(filename, info_dict) except (PostProcessingError): _, err, _ = sys.exc_info() - self.trouble(u'ERROR: postprocessing: %s' % str(err)) + self.trouble(u('ERROR: postprocessing: %s') % str(err)) return def download(self, url_list): @@ -500,13 +499,13 @@ class FileDownloader(object): self.increment_downloads() self.process_info(video) except UnavailableVideoError: - self.trouble(u'\nERROR: unable to download video') + self.trouble(u('\nERROR: unable to download video')) # Suitable InfoExtractor had been found; go to next URL break if not suitable_found: - self.trouble(u'ERROR: no suitable InfoExtractor: %s' % url) + self.trouble(u('ERROR: no suitable InfoExtractor: %s') % url) return self._download_retcode @@ -527,7 +526,7 @@ class FileDownloader(object): try: subprocess.call(['rtmpdump', '-h'], stdout=(file(os.path.devnull, 'w')), stderr=subprocess.STDOUT) except (OSError, IOError): - self.trouble(u'ERROR: RTMP download detected but "rtmpdump" could not be run') + self.trouble(u('ERROR: RTMP download detected but "rtmpdump" could not be run')) return False # Download using rtmpdump. rtmpdump returns exit code 2 when @@ -541,11 +540,11 @@ class FileDownloader(object): shell_quote = lambda args: ' '.join(map(pipes.quote, args)) except ImportError: shell_quote = repr - self.to_screen(u'[debug] rtmpdump command line: ' + shell_quote(args)) + self.to_screen(u('[debug] rtmpdump command line: ') + shell_quote(args)) retval = subprocess.call(args) while retval == 2 or retval == 1: prevsize = os.path.getsize(encodeFilename(tmpfilename)) - self.to_screen(u'\r[rtmpdump] %s bytes' % prevsize, skip_eol=True) + self.to_screen(u('\r[rtmpdump] %s bytes') % prevsize, skip_eol=True) time.sleep(5.0) # This seems to be needed retval = subprocess.call(basic_args + ['-e'] + [[], ['-k', '1']][retval == 1]) cursize = os.path.getsize(encodeFilename(tmpfilename)) @@ -553,15 +552,15 @@ class FileDownloader(object): break # Some rtmp streams seem abort after ~ 99.8%. Don't complain for those if prevsize == cursize and retval == 2 and cursize > 1024: - self.to_screen(u'\r[rtmpdump] Could not download the whole video. This can happen for some advertisements.') + self.to_screen(u('\r[rtmpdump] Could not download the whole video. This can happen for some advertisements.')) retval = 0 break if retval == 0: - self.to_screen(u'\r[rtmpdump] %s bytes' % os.path.getsize(encodeFilename(tmpfilename))) + self.to_screen(u('\r[rtmpdump] %s bytes') % os.path.getsize(encodeFilename(tmpfilename))) self.try_rename(tmpfilename, filename) return True else: - self.trouble(u'\nERROR: rtmpdump exited with code %d' % retval) + self.trouble(u('\nERROR: rtmpdump exited with code %d') % retval) return False def _do_download(self, filename, info_dict): @@ -649,7 +648,7 @@ class FileDownloader(object): self.report_retry(count, retries) if count > retries: - self.trouble(u'ERROR: giving up after %s retries' % retries) + self.trouble(u('ERROR: giving up after %s retries') % retries) return False data_len = data.info().get('Content-length', None) @@ -677,13 +676,13 @@ class FileDownloader(object): self.report_destination(filename) except (OSError, IOError): _, err, _ = sys.exc_info() - self.trouble(u'ERROR: unable to open for writing: %s' % str(err)) + self.trouble(u('ERROR: unable to open for writing: %s') % str(err)) return False try: stream.write(data_block) except (IOError, OSError): _, err, _ = sys.exc_info() - self.trouble(u'\nERROR: unable to write data: %s' % str(err)) + self.trouble(u('\nERROR: unable to write data: %s') % str(err)) return False if not self.params.get('noresizebuffer', False): block_size = self.best_block_size(after - before, len(data_block)) @@ -701,7 +700,7 @@ class FileDownloader(object): self.slow_down(start, byte_counter - resume_len) if stream is None: - self.trouble(u'\nERROR: Did not get any data blocks') + self.trouble(u('\nERROR: Did not get any data blocks')) return False stream.close() self.report_finish() diff --git a/youtube_dl/InfoExtractors.py b/youtube_dl/InfoExtractors.py index 5fa2be342..2f9096057 100644 --- a/youtube_dl/InfoExtractors.py +++ b/youtube_dl/InfoExtractors.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import datetime import HTMLParser @@ -153,7 +152,7 @@ class YoutubeIE(InfoExtractor): '45': '720x1280', '46': '1080x1920', } - IE_NAME = u'youtube' + IE_NAME = u('youtube') def suitable(self, url): """Receives a URL and returns True if suitable for this IE.""" @@ -161,39 +160,39 @@ class YoutubeIE(InfoExtractor): def report_lang(self): """Report attempt to set language.""" - self._downloader.to_screen(u'[youtube] Setting language') + self._downloader.to_screen(u('[youtube] Setting language')) def report_login(self): """Report attempt to log in.""" - self._downloader.to_screen(u'[youtube] Logging in') + self._downloader.to_screen(u('[youtube] Logging in')) def report_age_confirmation(self): """Report attempt to confirm age.""" - self._downloader.to_screen(u'[youtube] Confirming age') + self._downloader.to_screen(u('[youtube] Confirming age')) def report_video_webpage_download(self, video_id): """Report attempt to download video webpage.""" - self._downloader.to_screen(u'[youtube] %s: Downloading video webpage' % video_id) + self._downloader.to_screen(u('[youtube] %s: Downloading video webpage') % video_id) def report_video_info_webpage_download(self, video_id): """Report attempt to download video info webpage.""" - self._downloader.to_screen(u'[youtube] %s: Downloading video info webpage' % video_id) + self._downloader.to_screen(u('[youtube] %s: Downloading video info webpage') % video_id) def report_video_subtitles_download(self, video_id): """Report attempt to download video info webpage.""" - self._downloader.to_screen(u'[youtube] %s: Downloading video subtitles' % video_id) + self._downloader.to_screen(u('[youtube] %s: Downloading video subtitles') % video_id) def report_information_extraction(self, video_id): """Report attempt to extract video information.""" - self._downloader.to_screen(u'[youtube] %s: Extracting video information' % video_id) + self._downloader.to_screen(u('[youtube] %s: Extracting video information') % video_id) def report_unavailable_format(self, video_id, format): """Report extracted video URL.""" - self._downloader.to_screen(u'[youtube] %s: Format %s not available' % (video_id, format)) + self._downloader.to_screen(u('[youtube] %s: Format %s not available') % (video_id, format)) def report_rtmp_download(self): """Indicate the download will use the RTMP protocol.""" - self._downloader.to_screen(u'[youtube] RTMP download detected') + self._downloader.to_screen(u('[youtube] RTMP download detected')) def _closed_captions_xml_to_srt(self, xml_string): srt = '' @@ -239,7 +238,7 @@ class YoutubeIE(InfoExtractor): raise netrc.NetrcParseError('No authenticators for %s' % self._NETRC_MACHINE) except (IOError, netrc.NetrcParseError): _, err, _ = sys.exc_info() - self._downloader.to_stderr(u'WARNING: parsing .netrc: %s' % u(err)) + self._downloader.to_stderr(u('WARNING: parsing .netrc: %s') % u(err)) return # Set language @@ -249,7 +248,7 @@ class YoutubeIE(InfoExtractor): urllib2.urlopen(request).read() except (urllib2.URLError, httplib.HTTPException, socket.error): _, err, _ = sys.exc_info() - self._downloader.to_stderr(u'WARNING: unable to set language: %s' % u(err)) + self._downloader.to_stderr(u('WARNING: unable to set language: %s') % u(err)) return # No authentication to be performed @@ -269,11 +268,11 @@ class YoutubeIE(InfoExtractor): self.report_login() login_results = urllib2.urlopen(request).read() if re.search(r'(?i)