Merge pull request #1737 from diego-plan9/mbtracks

info plugin: Allow custom formatting and human-readable lengths
This commit is contained in:
Adrian Sampson 2015-11-26 10:31:12 -08:00
commit a333777946
4 changed files with 41 additions and 12 deletions

View file

@ -25,6 +25,7 @@ import re
from beets.plugins import BeetsPlugin
from beets import ui
from beets import mediafile
from beets.library import Item
from beets.util import displayable_path, normpath, syspath
@ -51,8 +52,10 @@ def tag_data_emitter(path):
for field in fields:
tags[field] = getattr(mf, field)
tags['art'] = mf.art is not None
tags['path'] = displayable_path(path)
return tags
# create a temporary Item to take advantage of __format__
item = Item.from_path(syspath(path))
return tags, item
return emitter
@ -64,8 +67,9 @@ def library_data(lib, args):
def library_data_emitter(item):
def emitter():
data = dict(item.formatted())
data['path'] = displayable_path(item.path)
return data
data.pop('path', None) # path is fetched from item
return data, item
return emitter
@ -78,8 +82,20 @@ def update_summary(summary, tags):
return summary
def print_data(data):
path = data.pop('path', None)
def print_data(data, item=None, fmt=None):
"""Print, with optional formatting, the fields of a single element.
If no format string `fmt` is passed, the entries on `data` are printed one
in each line, with the format 'field: value'. If `fmt` is not `None`, the
`item` is printed according to `fmt`, using the `Item.__format__`
machinery.
"""
if fmt:
# use fmt specified by the user
ui.print_(format(item, fmt))
return
path = displayable_path(item.path) if item else None
formatted = {}
for key, value in data.iteritems():
if isinstance(value, list):
@ -115,6 +131,7 @@ class InfoPlugin(BeetsPlugin):
cmd.parser.add_option('-i', '--include-keys', default=[],
action='append', dest='included_keys',
help='comma separated list of keys to show')
cmd.parser.add_format_option(target='item')
return [cmd]
def run(self, lib, opts, args):
@ -145,20 +162,18 @@ class InfoPlugin(BeetsPlugin):
summary = {}
for data_emitter in data_collector(lib, ui.decargs(args)):
try:
data = data_emitter()
data, item = data_emitter()
except (mediafile.UnreadableFileError, IOError) as ex:
self._log.error(u'cannot read file: {0}', ex)
continue
path = data.get('path')
data = key_filter(data)
data['path'] = path # always show path
if opts.summarize:
update_summary(summary, data)
else:
if not first:
ui.print_()
print_data(data)
print_data(data, item, opts.format)
first = False
if opts.summarize:
@ -181,14 +196,14 @@ def make_key_filter(include):
key = key.replace(r'\*', '.*')
matchers.append(re.compile(key + '$'))
def filter(data):
def filter_(data):
filtered = dict()
for key, value in data.items():
if any(map(lambda m: m.match(key), matchers)):
filtered[key] = value
return filtered
return filter
return filter_
def identity(val):

View file

@ -24,6 +24,8 @@ New:
*exclude* matching music from the results. For example, ``beet list -a
beatles ^album:1`` will find all your albums by the Beatles except for their
singles compilation, "1." See :ref:`not_query`. :bug:`819` :bug:`1728`
* :doc:`/plugins/info`: The plugin now accepts the ``-f/--format`` option for
customizing how items are displayed. :bug:`1737`
For developers:

View file

@ -36,6 +36,10 @@ Additional command-line options include:
* ``--summarize`` or ``-s``: Merge all the information from multiple files
into a single list of values. If the tags differ across the files, print
``[various]``.
* ``--format`` or ``-f``: Specify a specific format with which to print every
item. This uses the same template syntax as beets :doc:`path formats
</reference/pathformat>`.
.. _id3v2: http://id3v2.sourceforge.net
.. _mp3info: http://www.ibiblio.org/mp3info/

View file

@ -52,6 +52,7 @@ class InfoTest(unittest.TestCase, TestHelper):
self.assertIn('disctitle: DDD', out)
self.assertIn('genres: a; b; c', out)
self.assertNotIn('composer:', out)
self.remove_mediafile_fixtures()
def test_item_query(self):
item1, item2 = self.add_item_fixtures(count=2)
@ -93,6 +94,7 @@ class InfoTest(unittest.TestCase, TestHelper):
self.assertIn(u'album: AAA', out)
self.assertIn(u'tracktotal: 5', out)
self.assertIn(u'title: [various]', out)
self.remove_mediafile_fixtures()
def test_include_pattern(self):
item, = self.add_item_fixtures()
@ -105,6 +107,12 @@ class InfoTest(unittest.TestCase, TestHelper):
self.assertNotIn(u'title:', out)
self.assertIn(u'album: xxxx', out)
def test_custom_format(self):
self.add_item_fixtures()
out = self.run_with_output('--library', '--format',
'$track. $title - $artist ($length)')
self.assertEqual(u'02. tïtle 0 - the artist (1.1)\n', out)
def suite():
return unittest.TestLoader().loadTestsFromName(__name__)