mirror of
https://github.com/beetbox/beets.git
synced 2025-12-16 05:34:47 +01:00
Merge pull request #1737 from diego-plan9/mbtracks
info plugin: Allow custom formatting and human-readable lengths
This commit is contained in:
commit
a333777946
4 changed files with 41 additions and 12 deletions
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
|
|||
|
|
@ -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__)
|
||||
|
|
|
|||
Loading…
Reference in a new issue