mirror of
https://github.com/beetbox/beets.git
synced 2025-12-26 02:24:33 +01:00
Merge pull request #1281 from tomjaspers/configurable-colors
Colors are now user configurable. Conflicts: beets/ui/__init__.py beets/ui/commands.py
This commit is contained in:
commit
927a53d59b
9 changed files with 112 additions and 36 deletions
|
|
@ -41,7 +41,6 @@ max_filename_length: 0
|
|||
plugins: []
|
||||
pluginpath: []
|
||||
threaded: yes
|
||||
color: yes
|
||||
timeout: 5.0
|
||||
per_disc_numbering: no
|
||||
verbose: no
|
||||
|
|
@ -52,6 +51,15 @@ id3v23: no
|
|||
ui:
|
||||
terminal_width: 80
|
||||
length_diff_thresh: 10.0
|
||||
color: yes
|
||||
colors:
|
||||
text_success: green
|
||||
text_warning: yellow
|
||||
text_error: red
|
||||
text_highlight: red
|
||||
text_highlight_minor: lightgray
|
||||
action_default: turquoise
|
||||
action: blue
|
||||
|
||||
list_format_item: $artist - $album - $title
|
||||
list_format_album: $albumartist - $album
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ def input_options(options, require=False, prompt=None, fallback_prompt=None,
|
|||
is_default = False
|
||||
|
||||
# Colorize the letter shortcut.
|
||||
show_letter = colorize('turquoise' if is_default else 'blue',
|
||||
show_letter = colorize('action_default' if is_default else 'action',
|
||||
show_letter)
|
||||
|
||||
# Insert the highlighted letter back into the word.
|
||||
|
|
@ -223,7 +223,7 @@ def input_options(options, require=False, prompt=None, fallback_prompt=None,
|
|||
if numrange:
|
||||
if isinstance(default, int):
|
||||
default_name = unicode(default)
|
||||
default_name = colorize('turquoise', default_name)
|
||||
default_name = colorize('action_default', default_name)
|
||||
tmpl = '# selection (default %s)'
|
||||
prompt_parts.append(tmpl % default_name)
|
||||
prompt_part_lengths.append(len(tmpl % unicode(default)))
|
||||
|
|
@ -362,6 +362,12 @@ LIGHT_COLORS = ["darkgray", "red", "green", "yellow", "blue",
|
|||
"fuchsia", "turquoise", "white"]
|
||||
RESET_COLOR = COLOR_ESCAPE + "39;49;00m"
|
||||
|
||||
# These abstract COLOR_NAMES are lazily mapped on to the actual color in COLORS
|
||||
# as they are defined in the configuration files, see function: colorize
|
||||
COLOR_NAMES = ['text_success', 'text_warning', 'text_error', 'text_highlight',
|
||||
'text_highlight_minor', 'action_default', 'action']
|
||||
COLORS = None
|
||||
|
||||
|
||||
def _colorize(color, text):
|
||||
"""Returns a string that prints the given text in the given color
|
||||
|
|
@ -377,17 +383,28 @@ def _colorize(color, text):
|
|||
return escape + text + RESET_COLOR
|
||||
|
||||
|
||||
def colorize(color, text):
|
||||
def colorize(color_name, text):
|
||||
"""Colorize text if colored output is enabled. (Like _colorize but
|
||||
conditional.)
|
||||
"""
|
||||
if config['color']:
|
||||
if config['ui']['color']:
|
||||
global COLORS
|
||||
if not COLORS:
|
||||
COLORS = dict((name, config['ui']['colors'][name].get(unicode))
|
||||
for name in COLOR_NAMES)
|
||||
# In case a 3rd party plugin is still passing the actual color ('red')
|
||||
# instead of the abstract color name ('text_error')
|
||||
color = COLORS.get(color_name)
|
||||
if not color:
|
||||
log.debug(u'Invalid color_name: {0}', color_name)
|
||||
color = color_name
|
||||
return _colorize(color, text)
|
||||
else:
|
||||
return text
|
||||
|
||||
|
||||
def _colordiff(a, b, highlight='red', minor_highlight='lightgray'):
|
||||
def _colordiff(a, b, highlight='text_highlight',
|
||||
minor_highlight='text_highlight_minor'):
|
||||
"""Given two values, return the same pair of strings except with
|
||||
their differences highlighted in the specified color. Strings are
|
||||
highlighted intelligently to show differences; other values are
|
||||
|
|
@ -437,11 +454,11 @@ def _colordiff(a, b, highlight='red', minor_highlight='lightgray'):
|
|||
return u''.join(a_out), u''.join(b_out)
|
||||
|
||||
|
||||
def colordiff(a, b, highlight='red'):
|
||||
def colordiff(a, b, highlight='text_highlight'):
|
||||
"""Colorize differences between two values if color is enabled.
|
||||
(Like _colordiff but conditional.)
|
||||
"""
|
||||
if config['color']:
|
||||
if config['ui']['color']:
|
||||
return _colordiff(a, b, highlight)
|
||||
else:
|
||||
return unicode(a), unicode(b)
|
||||
|
|
@ -526,7 +543,8 @@ def _field_diff(field, old, new):
|
|||
if isinstance(oldval, basestring):
|
||||
oldstr, newstr = colordiff(oldval, newstr)
|
||||
else:
|
||||
oldstr, newstr = colorize('red', oldstr), colorize('red', newstr)
|
||||
oldstr = colorize('text_error', oldstr)
|
||||
newstr = colorize('text_error', newstr)
|
||||
|
||||
return u'{0} -> {1}'.format(oldstr, newstr)
|
||||
|
||||
|
|
@ -562,7 +580,7 @@ def show_model_changes(new, old=None, fields=None, always=False):
|
|||
|
||||
changes.append(u' {0}: {1}'.format(
|
||||
field,
|
||||
colorize('red', new.formatted()[field])
|
||||
colorize('text_highlight', new.formatted()[field])
|
||||
))
|
||||
|
||||
# Print changes.
|
||||
|
|
@ -845,6 +863,14 @@ def _configure(options):
|
|||
else:
|
||||
log.setLevel(logging.INFO)
|
||||
|
||||
# Ensure compatibility with old (top-level) color configuration.
|
||||
# Deprecation msg to motivate user to switch to config['ui']['color].
|
||||
if config['color'].exists():
|
||||
log.warning(u'Warning: top-level configuration of `color` '
|
||||
u'is deprecated. Configure color use under `ui`. '
|
||||
u'See documentation for more info.')
|
||||
config['ui']['color'].set(config['color'].get(bool))
|
||||
|
||||
config_path = config.user_config_path()
|
||||
if os.path.isfile(config_path):
|
||||
log.debug(u'user configuration: {0}',
|
||||
|
|
|
|||
|
|
@ -176,11 +176,11 @@ def dist_string(dist):
|
|||
"""
|
||||
out = '%.1f%%' % ((1 - dist) * 100)
|
||||
if dist <= config['match']['strong_rec_thresh'].as_number():
|
||||
out = ui.colorize('green', out)
|
||||
out = ui.colorize('text_success', out)
|
||||
elif dist <= config['match']['medium_rec_thresh'].as_number():
|
||||
out = ui.colorize('yellow', out)
|
||||
out = ui.colorize('text_warning', out)
|
||||
else:
|
||||
out = ui.colorize('red', out)
|
||||
out = ui.colorize('text_error', out)
|
||||
return out
|
||||
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ def penalty_string(distance, limit=None):
|
|||
if penalties:
|
||||
if limit and len(penalties) > limit:
|
||||
penalties = penalties[:limit] + ['...']
|
||||
return ui.colorize('yellow', '(%s)' % ', '.join(penalties))
|
||||
return ui.colorize('text_warning', '(%s)' % ', '.join(penalties))
|
||||
|
||||
|
||||
def show_change(cur_artist, cur_album, match):
|
||||
|
|
@ -270,7 +270,7 @@ def show_change(cur_artist, cur_album, match):
|
|||
# Disambiguation.
|
||||
disambig = disambig_string(match.info)
|
||||
if disambig:
|
||||
info.append(ui.colorize('lightgray', '(%s)' % disambig))
|
||||
info.append(ui.colorize('text_highlight_minor', '(%s)' % disambig))
|
||||
print_(' '.join(info))
|
||||
|
||||
# Tracks.
|
||||
|
|
@ -315,9 +315,9 @@ def show_change(cur_artist, cur_album, match):
|
|||
cur_track, new_track = format_index(item), format_index(track_info)
|
||||
if cur_track != new_track:
|
||||
if item.track in (track_info.index, track_info.medium_index):
|
||||
color = 'lightgray'
|
||||
color = 'text_highlight_minor'
|
||||
else:
|
||||
color = 'red'
|
||||
color = 'text_highlight'
|
||||
templ = ui.colorize(color, u' (#{0})')
|
||||
lhs += templ.format(cur_track)
|
||||
rhs += templ.format(new_track)
|
||||
|
|
@ -329,7 +329,7 @@ def show_change(cur_artist, cur_album, match):
|
|||
config['ui']['length_diff_thresh'].as_number():
|
||||
cur_length = ui.human_seconds_short(item.length)
|
||||
new_length = ui.human_seconds_short(track_info.length)
|
||||
templ = ui.colorize('red', u' ({0})')
|
||||
templ = ui.colorize('text_highlight', u' ({0})')
|
||||
lhs += templ.format(cur_length)
|
||||
rhs += templ.format(new_length)
|
||||
lhs_width += len(cur_length) + 3
|
||||
|
|
@ -364,14 +364,14 @@ def show_change(cur_artist, cur_album, match):
|
|||
line = ' ! %s (#%s)' % (track_info.title, format_index(track_info))
|
||||
if track_info.length:
|
||||
line += ' (%s)' % ui.human_seconds_short(track_info.length)
|
||||
print_(ui.colorize('yellow', line))
|
||||
print_(ui.colorize('text_warning', line))
|
||||
if match.extra_items:
|
||||
print_('Unmatched tracks:')
|
||||
for item in match.extra_items:
|
||||
line = ' ! %s (#%s)' % (item.title, format_index(item))
|
||||
if item.length:
|
||||
line += ' (%s)' % ui.human_seconds_short(item.length)
|
||||
print_(ui.colorize('yellow', line))
|
||||
print_(ui.colorize('text_warning', line))
|
||||
|
||||
|
||||
def show_item_change(item, match):
|
||||
|
|
@ -408,7 +408,7 @@ def show_item_change(item, match):
|
|||
# Disambiguation.
|
||||
disambig = disambig_string(match.info)
|
||||
if disambig:
|
||||
info.append(ui.colorize('lightgray', '(%s)' % disambig))
|
||||
info.append(ui.colorize('text_highlight_minor', '(%s)' % disambig))
|
||||
print_(' '.join(info))
|
||||
|
||||
|
||||
|
|
@ -567,7 +567,8 @@ def choose_candidate(candidates, singleton, rec, cur_artist=None,
|
|||
# Disambiguation
|
||||
disambig = disambig_string(match.info)
|
||||
if disambig:
|
||||
line.append(ui.colorize('lightgray', '(%s)' % disambig))
|
||||
line.append(ui.colorize('text_highlight_minor',
|
||||
'(%s)' % disambig))
|
||||
|
||||
print_(' '.join(line))
|
||||
|
||||
|
|
@ -997,7 +998,7 @@ def update_items(lib, query, album, move, pretend):
|
|||
# Item deleted?
|
||||
if not os.path.exists(syspath(item.path)):
|
||||
ui.print_(format(item))
|
||||
ui.print_(ui.colorize('red', u' deleted'))
|
||||
ui.print_(ui.colorize('text_error', u' deleted'))
|
||||
if not pretend:
|
||||
item.remove(True)
|
||||
affected_albums.add(item.album_id)
|
||||
|
|
|
|||
|
|
@ -447,9 +447,9 @@ class FetchArtPlugin(plugins.BeetsPlugin):
|
|||
if path:
|
||||
album.set_art(path, False)
|
||||
album.store()
|
||||
message = ui.colorize('green', 'found album art')
|
||||
message = ui.colorize('text_success', 'found album art')
|
||||
else:
|
||||
message = ui.colorize('red', 'no art found')
|
||||
message = ui.colorize('text_error', 'no art found')
|
||||
|
||||
self._log.info(u'{0}: {1}', album, message)
|
||||
|
||||
|
|
|
|||
|
|
@ -76,13 +76,14 @@ def play_music(lib, opts, args, log):
|
|||
item_type += 's' if len(selection) > 1 else ''
|
||||
|
||||
if not selection:
|
||||
ui.print_(ui.colorize('yellow', 'No {0} to play.'.format(item_type)))
|
||||
ui.print_(ui.colorize('text_warning',
|
||||
'No {0} to play.'.format(item_type)))
|
||||
return
|
||||
|
||||
# Warn user before playing any huge playlists.
|
||||
if len(selection) > 100:
|
||||
ui.print_(ui.colorize(
|
||||
'yellow',
|
||||
'text_warning',
|
||||
'You are about to queue {0} {1}.'.format(len(selection), item_type)
|
||||
))
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ Changelog
|
|||
|
||||
Features:
|
||||
|
||||
* The colors used are now configurable via the new config option ``colors``,
|
||||
nested under the option ``ui``. The `color` config option has been moved
|
||||
from top-level to under ``ui``. Beets will respect the old color setting,
|
||||
but will warn the user with a deprecation message. :bug:`1238`
|
||||
* A new :doc:`/plugins/filefilter` lets you write regular expressions to
|
||||
automatically avoid importing certain files. Thanks to :user:`mried`.
|
||||
:bug:`1186`
|
||||
|
|
|
|||
|
|
@ -162,13 +162,6 @@ Either ``yes`` or ``no``, indicating whether the autotagger should use
|
|||
multiple threads. This makes things faster but may behave strangely.
|
||||
Defaults to ``yes``.
|
||||
|
||||
color
|
||||
~~~~~
|
||||
|
||||
Either ``yes`` or ``no``; whether to use color in console output (currently
|
||||
only in the ``import`` command). Turn this off if your terminal doesn't
|
||||
support ANSI colors.
|
||||
|
||||
.. _list_format_item:
|
||||
|
||||
list_format_item
|
||||
|
|
@ -277,6 +270,49 @@ version of ID3. Enable this option to instead use the older ID3v2.3 standard,
|
|||
which is preferred by certain older software such as Windows Media Player.
|
||||
|
||||
|
||||
UI Options
|
||||
----------
|
||||
|
||||
The options that allow for customization of the visual appearance
|
||||
of the console interface.
|
||||
|
||||
These options are available in this section:
|
||||
|
||||
color
|
||||
~~~~~
|
||||
|
||||
Either ``yes`` or ``no``; whether to use color in console output (currently
|
||||
only in the ``import`` command). Turn this off if your terminal doesn't
|
||||
support ANSI colors.
|
||||
|
||||
.. note::
|
||||
|
||||
The `color` option was previously a top-level configuration. This is
|
||||
still respected, but a deprecation message will be shown until your
|
||||
top-level `color` configuration has been nested under `ui`.
|
||||
|
||||
colors
|
||||
~~~~~~
|
||||
|
||||
The colors that are used throughout the user interface. These are only used if
|
||||
the ``color`` option is set to ``yes``. For example, you might have a section
|
||||
in your configuration file that looks like this::
|
||||
|
||||
ui:
|
||||
color: yes
|
||||
colors:
|
||||
text_success: green
|
||||
text_warning: yellow
|
||||
text_error: red
|
||||
text_highlight: red
|
||||
text_highlight_minor: lightgray
|
||||
action_default: turquoise
|
||||
action: blue
|
||||
|
||||
Available colors: black, darkred, darkgreen, brown, darkblue, purple, teal,
|
||||
lightgray, darkgray, red, green, yellow, blue, fuchsia, turquoise, white
|
||||
|
||||
|
||||
Importer Options
|
||||
----------------
|
||||
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ class TestHelper(object):
|
|||
|
||||
self.config['plugins'] = []
|
||||
self.config['verbose'] = True
|
||||
self.config['color'] = False
|
||||
self.config['ui']['color'] = False
|
||||
self.config['threaded'] = False
|
||||
|
||||
self.libdir = os.path.join(self.temp_dir, 'libdir')
|
||||
|
|
|
|||
|
|
@ -923,7 +923,7 @@ class ShowChangeTest(_common.TestCase):
|
|||
items = items or self.items
|
||||
info = info or self.info
|
||||
mapping = dict(zip(items, info.tracks))
|
||||
config['color'] = False
|
||||
config['ui']['color'] = False
|
||||
album_dist = distance(items, info, mapping)
|
||||
album_dist._penalties = {'album': [dist]}
|
||||
commands.show_change(
|
||||
|
|
|
|||
Loading…
Reference in a new issue