aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Sawicki <contact@grub4k.xyz>2023-05-24 20:35:07 +0200
committerGitHub <noreply@github.com>2023-05-24 20:35:07 +0200
commit8417f26b8a819cd7ffcd4e000ca3e45033e670fb (patch)
treee0227cf0f6b96fc89235d259effad6bd5ec8891c
parent7aeda6cc9e73ada0b0a0b6a6748c66bef63a20a8 (diff)
downloadhypervideo-pre-8417f26b8a819cd7ffcd4e000ca3e45033e670fb.tar.lz
hypervideo-pre-8417f26b8a819cd7ffcd4e000ca3e45033e670fb.tar.xz
hypervideo-pre-8417f26b8a819cd7ffcd4e000ca3e45033e670fb.zip
[core] Implement `--color` flag (#6904)
Authored by: Grub4K
-rw-r--r--README.md9
-rw-r--r--yt_dlp/YoutubeDL.py36
-rw-r--r--yt_dlp/__init__.py6
-rw-r--r--yt_dlp/downloader/common.py3
-rw-r--r--yt_dlp/options.py24
5 files changed, 66 insertions, 12 deletions
diff --git a/README.md b/README.md
index d0eaba747..25ed3b844 100644
--- a/README.md
+++ b/README.md
@@ -425,8 +425,12 @@ If you fork the project on GitHub, you can run your fork's [build workflow](.git
--no-wait-for-video Do not wait for scheduled streams (default)
--mark-watched Mark videos watched (even with --simulate)
--no-mark-watched Do not mark videos watched (default)
- --no-colors Do not emit color codes in output (Alias:
- --no-colours)
+ --color [STREAM:]POLICY Whether to emit color codes in output,
+ optionally prefixed by the STREAM (stdout or
+ stderr) to apply the setting to. Can be one
+ of "always", "auto" (default), "never", or
+ "no_color" (use non color terminal
+ sequences). Can be used multiple times
--compat-options OPTS Options that can help keep compatibility
with youtube-dl or youtube-dlc
configurations by reverting some of the
@@ -2148,6 +2152,7 @@ While these options are redundant, they are still expected to be used due to the
--playlist-end NUMBER -I :NUMBER
--playlist-reverse -I ::-1
--no-playlist-reverse Default
+ --no-colors --color no_color
#### Not recommended
diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py
index cd82b2772..e1e558836 100644
--- a/yt_dlp/YoutubeDL.py
+++ b/yt_dlp/YoutubeDL.py
@@ -415,7 +415,12 @@ class YoutubeDL:
- Raise utils.DownloadCancelled(msg) to abort remaining
downloads when a video is rejected.
match_filter_func in utils.py is one example for this.
- no_color: Do not emit color codes in output.
+ color: A Dictionary with output stream names as keys
+ and their respective color policy as values.
+ Can also just be a single color policy,
+ in which case it applies to all outputs.
+ Valid stream names are 'stdout' and 'stderr'.
+ Valid color policies are one of 'always', 'auto', 'no_color' or 'never'.
geo_bypass: Bypass geographic restriction via faking X-Forwarded-For
HTTP header
geo_bypass_country:
@@ -537,6 +542,7 @@ class YoutubeDL:
data will be downloaded and processed by extractor.
You can reduce network I/O by disabling it if you don't
care about HLS. (only for youtube)
+ no_color: Same as `color='no_color'`
"""
_NUMERIC_FIELDS = {
@@ -603,9 +609,24 @@ class YoutubeDL:
except Exception as e:
self.write_debug(f'Failed to enable VT mode: {e}')
+ if self.params.get('no_color'):
+ if self.params.get('color') is not None:
+ self.report_warning('Overwriting params from "color" with "no_color"')
+ self.params['color'] = 'no_color'
+
+ term_allow_color = os.environ.get('TERM', '').lower() != 'dumb'
+
+ def process_color_policy(stream):
+ stream_name = {sys.stdout: 'stdout', sys.stderr: 'stderr'}[stream]
+ policy = traverse_obj(self.params, ('color', (stream_name, None), {str}), get_all=False)
+ if policy in ('auto', None):
+ return term_allow_color and supports_terminal_sequences(stream)
+ assert policy in ('always', 'never', 'no_color')
+ return {'always': True, 'never': False}.get(policy, policy)
+
self._allow_colors = Namespace(**{
- type_: not self.params.get('no_color') and supports_terminal_sequences(stream)
- for type_, stream in self._out_files.items_ if type_ != 'console'
+ name: process_color_policy(stream)
+ for name, stream in self._out_files.items_ if name != 'console'
})
# The code is left like this to be reused for future deprecations
@@ -974,7 +995,7 @@ class YoutubeDL:
text = text.encode(encoding, 'ignore').decode(encoding)
if fallback is not None and text != original_text:
text = fallback
- return format_text(text, f) if allow_colors else text if fallback is None else fallback
+ return format_text(text, f) if allow_colors is True else text if fallback is None else fallback
def _format_out(self, *args, **kwargs):
return self._format_text(self._out_files.out, self._allow_colors.out, *args, **kwargs)
@@ -3769,9 +3790,14 @@ class YoutubeDL:
def get_encoding(stream):
ret = str(getattr(stream, 'encoding', 'missing (%s)' % type(stream).__name__))
+ additional_info = []
+ if os.environ.get('TERM', '').lower() == 'dumb':
+ additional_info.append('dumb')
if not supports_terminal_sequences(stream):
from .utils import WINDOWS_VT_MODE # Must be imported locally
- ret += ' (No VT)' if WINDOWS_VT_MODE is False else ' (No ANSI)'
+ additional_info.append('No VT' if WINDOWS_VT_MODE is False else 'No ANSI')
+ if additional_info:
+ ret = f'{ret} ({",".join(additional_info)})'
return ret
encoding_str = 'Encodings: locale %s, fs %s, pref %s, %s' % (
diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py
index 9563d784a..137c9503f 100644
--- a/yt_dlp/__init__.py
+++ b/yt_dlp/__init__.py
@@ -436,6 +436,10 @@ def validate_options(opts):
elif ed and proto == 'default':
default_downloader = ed.get_basename()
+ for policy in opts.color.values():
+ if policy not in ('always', 'auto', 'no_color', 'never'):
+ raise ValueError(f'"{policy}" is not a valid color policy')
+
warnings, deprecation_warnings = [], []
# Common mistake: -f best
@@ -894,7 +898,7 @@ def parse_options(argv=None):
'playlist_items': opts.playlist_items,
'xattr_set_filesize': opts.xattr_set_filesize,
'match_filter': opts.match_filter,
- 'no_color': opts.no_color,
+ 'color': opts.color,
'ffmpeg_location': opts.ffmpeg_location,
'hls_prefer_native': opts.hls_prefer_native,
'hls_use_mpegts': opts.hls_use_mpegts,
diff --git a/yt_dlp/downloader/common.py b/yt_dlp/downloader/common.py
index c48a2ff8a..477ec3c8a 100644
--- a/yt_dlp/downloader/common.py
+++ b/yt_dlp/downloader/common.py
@@ -296,7 +296,8 @@ class FileDownloader:
self._multiline = BreaklineStatusPrinter(self.ydl._out_files.out, lines)
else:
self._multiline = MultilinePrinter(self.ydl._out_files.out, lines, not self.params.get('quiet'))
- self._multiline.allow_colors = self._multiline._HAVE_FULLCAP and not self.params.get('no_color')
+ self._multiline.allow_colors = self.ydl._allow_colors.out and self.ydl._allow_colors.out != 'no_color'
+ self._multiline._HAVE_FULLCAP = self.ydl._allow_colors.out
def _finish_multiline_status(self):
self._multiline.end()
diff --git a/yt_dlp/options.py b/yt_dlp/options.py
index 838d79fcb..fecc27403 100644
--- a/yt_dlp/options.py
+++ b/yt_dlp/options.py
@@ -34,6 +34,7 @@ from .utils import (
join_nonempty,
orderedSet_from_options,
remove_end,
+ variadic,
write_string,
)
from .version import CHANNEL, __version__
@@ -250,7 +251,7 @@ def create_parser():
if multiple_args:
val = [val, *value[1:]]
elif default_key is not None:
- keys, val = [default_key], value
+ keys, val = variadic(default_key), value
else:
raise optparse.OptionValueError(
f'wrong {opt_str} formatting; it should be {option.metavar}, not "{value}"')
@@ -440,8 +441,25 @@ def create_parser():
help='Do not mark videos watched (default)')
general.add_option(
'--no-colors', '--no-colours',
- action='store_true', dest='no_color', default=False,
- help='Do not emit color codes in output (Alias: --no-colours)')
+ action='store_const', dest='color', const={
+ 'stdout': 'no_color',
+ 'stderr': 'no_color',
+ },
+ help=optparse.SUPPRESS_HELP)
+ general.add_option(
+ '--color',
+ dest='color', metavar='[STREAM:]POLICY', default={}, type='str',
+ action='callback', callback=_dict_from_options_callback,
+ callback_kwargs={
+ 'allowed_keys': 'stdout|stderr',
+ 'default_key': ['stdout', 'stderr'],
+ 'process': str.strip,
+ }, help=(
+ 'Whether to emit color codes in output, optionally prefixed by '
+ 'the STREAM (stdout or stderr) to apply the setting to. '
+ 'Can be one of "always", "auto" (default), "never", or '
+ '"no_color" (use non color terminal sequences). '
+ 'Can be used multiple times'))
general.add_option(
'--compat-options',
metavar='OPTS', dest='compat_opts', default=set(), type='str',