Add a workaround for terminals without bidi support (Fixes #1912)

This commit is contained in:
Philipp Hagemeister 2013-12-09 04:08:51 +01:00
parent ffe62508e4
commit 0783b09b92
2 changed files with 59 additions and 14 deletions

View file

@ -133,6 +133,8 @@ class YoutubeDL(object):
nocheckcertificate:Do not verify SSL certificates nocheckcertificate:Do not verify SSL certificates
proxy: URL of the proxy server to use proxy: URL of the proxy server to use
socket_timeout: Time to wait for unresponsive hosts, in seconds socket_timeout: Time to wait for unresponsive hosts, in seconds
bidi_workaround: Work around buggy terminals without bidirectional text
support, using fridibi
The following parameters are not used by YoutubeDL itself, they are used by The following parameters are not used by YoutubeDL itself, they are used by
the FileDownloader: the FileDownloader:
@ -156,8 +158,45 @@ def __init__(self, params=None):
self._download_retcode = 0 self._download_retcode = 0
self._num_downloads = 0 self._num_downloads = 0
self._screen_file = [sys.stdout, sys.stderr][params.get('logtostderr', False)] self._screen_file = [sys.stdout, sys.stderr][params.get('logtostderr', False)]
self._err_file = sys.stderr
self.params = {} if params is None else params self.params = {} if params is None else params
# Pipe messsages through fribidi
if params.get('bidi_workaround', False):
# fribidi does not support ungetting, so force newlines
params['progress_with_newline'] = True
for fid in ['_screen_file', '_err_file']:
class FribidiOut(object):
def __init__(self, outfile, errfile):
self.outfile = outfile
self.process = subprocess.Popen(
['fribidi'],
stdin=subprocess.PIPE,
stdout=outfile,
stderr=errfile)
def write(self, s):
res = self.process.stdin.write(s)
self.flush()
return res
def flush(self):
return self.process.stdin.flush()
def isatty(self):
return self.outfile.isatty()
try:
vout = FribidiOut(getattr(self, fid), self._err_file)
setattr(self, fid, vout)
except OSError as ose:
if ose.errno == 2:
self.report_warning(u'Could not find fribidi executable, ignoring --bidi-workaround . Make sure that fribidi is an executable file in one of the directories in your $PATH.')
break
else:
raise
if (sys.version_info >= (3,) and sys.platform != 'win32' and if (sys.version_info >= (3,) and sys.platform != 'win32' and
sys.getfilesystemencoding() in ['ascii', 'ANSI_X3.4-1968'] sys.getfilesystemencoding() in ['ascii', 'ANSI_X3.4-1968']
and not params['restrictfilenames']): and not params['restrictfilenames']):
@ -206,10 +245,14 @@ def add_post_processor(self, pp):
pp.set_downloader(self) pp.set_downloader(self)
def to_screen(self, message, skip_eol=False): def to_screen(self, message, skip_eol=False):
"""Print message to stdout if not in quiet mode."""
return self.to_stdout(message, skip_eol, check_quiet=True)
def to_stdout(self, message, skip_eol=False, check_quiet=False):
"""Print message to stdout if not in quiet mode.""" """Print message to stdout if not in quiet mode."""
if self.params.get('logger'): if self.params.get('logger'):
self.params['logger'].debug(message) self.params['logger'].debug(message)
elif not self.params.get('quiet', False): elif not check_quiet or not self.params.get('quiet', False):
terminator = [u'\n', u''][skip_eol] terminator = [u'\n', u''][skip_eol]
output = message + terminator output = message + terminator
write_string(output, self._screen_file) write_string(output, self._screen_file)
@ -221,9 +264,7 @@ def to_stderr(self, message):
self.params['logger'].error(message) self.params['logger'].error(message)
else: else:
output = message + u'\n' output = message + u'\n'
if 'b' in getattr(self._screen_file, 'mode', '') or sys.version_info[0] < 3: # Python 2 lies about the mode of sys.stdout/sys.stderr write_string(output, self._err_file)
output = output.encode(preferredencoding())
sys.stderr.write(output)
def to_console_title(self, message): def to_console_title(self, message):
if not self.params.get('consoletitle', False): if not self.params.get('consoletitle', False):
@ -294,7 +335,7 @@ def report_warning(self, message):
Print the message to stderr, it will be prefixed with 'WARNING:' Print the message to stderr, it will be prefixed with 'WARNING:'
If stderr is a tty file the 'WARNING:' will be colored If stderr is a tty file the 'WARNING:' will be colored
''' '''
if sys.stderr.isatty() and os.name != 'nt': if self._err_file.isatty() and os.name != 'nt':
_msg_header = u'\033[0;33mWARNING:\033[0m' _msg_header = u'\033[0;33mWARNING:\033[0m'
else: else:
_msg_header = u'WARNING:' _msg_header = u'WARNING:'
@ -306,7 +347,7 @@ def report_error(self, message, tb=None):
Do the same as trouble, but prefixes the message with 'ERROR:', colored Do the same as trouble, but prefixes the message with 'ERROR:', colored
in red if stderr is a tty file. in red if stderr is a tty file.
''' '''
if sys.stderr.isatty() and os.name != 'nt': if self._err_file.isatty() and os.name != 'nt':
_msg_header = u'\033[0;31mERROR:\033[0m' _msg_header = u'\033[0;31mERROR:\033[0m'
else: else:
_msg_header = u'ERROR:' _msg_header = u'ERROR:'
@ -695,22 +736,22 @@ def process_info(self, info_dict):
# Forced printings # Forced printings
if self.params.get('forcetitle', False): if self.params.get('forcetitle', False):
compat_print(info_dict['fulltitle']) self.to_stdout(info_dict['fulltitle'])
if self.params.get('forceid', False): if self.params.get('forceid', False):
compat_print(info_dict['id']) self.to_stdout(info_dict['id'])
if self.params.get('forceurl', False): if self.params.get('forceurl', False):
# For RTMP URLs, also include the playpath # For RTMP URLs, also include the playpath
compat_print(info_dict['url'] + info_dict.get('play_path', u'')) self.to_stdout(info_dict['url'] + info_dict.get('play_path', u''))
if self.params.get('forcethumbnail', False) and info_dict.get('thumbnail') is not None: if self.params.get('forcethumbnail', False) and info_dict.get('thumbnail') is not None:
compat_print(info_dict['thumbnail']) self.to_stdout(info_dict['thumbnail'])
if self.params.get('forcedescription', False) and info_dict.get('description') is not None: if self.params.get('forcedescription', False) and info_dict.get('description') is not None:
compat_print(info_dict['description']) self.to_stdout(info_dict['description'])
if self.params.get('forcefilename', False) and filename is not None: if self.params.get('forcefilename', False) and filename is not None:
compat_print(filename) self.to_stdout(filename)
if self.params.get('forceformat', False): if self.params.get('forceformat', False):
compat_print(info_dict['format']) self.to_stdout(info_dict['format'])
if self.params.get('forcejson', False): if self.params.get('forcejson', False):
compat_print(json.dumps(info_dict)) self.to_stdout(json.dumps(info_dict))
# Do nothing else if in simulate mode # Do nothing else if in simulate mode
if self.params.get('simulate', False): if self.params.get('simulate', False):

View file

@ -204,6 +204,9 @@ def _hide_login_info(opts):
general.add_option( general.add_option(
'--socket-timeout', dest='socket_timeout', '--socket-timeout', dest='socket_timeout',
type=float, default=None, help=optparse.SUPPRESS_HELP) type=float, default=None, help=optparse.SUPPRESS_HELP)
general.add_option(
'--bidi-workaround', dest='bidi_workaround', action='store_true',
help=u'Work around terminals that lack bidirectional text support. Requires fribidi executable in PATH')
selection.add_option('--playlist-start', selection.add_option('--playlist-start',
@ -684,6 +687,7 @@ def _real_main(argv=None):
'nocheckcertificate': opts.no_check_certificate, 'nocheckcertificate': opts.no_check_certificate,
'proxy': opts.proxy, 'proxy': opts.proxy,
'socket_timeout': opts.socket_timeout, 'socket_timeout': opts.socket_timeout,
'bidi_workaround': opts.bidi_workaround,
} }
with YoutubeDL(ydl_opts) as ydl: with YoutubeDL(ydl_opts) as ydl: