mirror of
https://github.com/beetbox/beets.git
synced 2026-01-18 06:05:06 +01:00
parent
43e9044843
commit
482008bf1d
5 changed files with 84 additions and 16 deletions
|
|
@ -19,6 +19,7 @@ from __future__ import (division, absolute_import, print_function,
|
|||
unicode_literals)
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets import ui
|
||||
|
|
@ -77,7 +78,7 @@ def update_summary(summary, tags):
|
|||
|
||||
|
||||
def print_data(data):
|
||||
path = data.pop('path')
|
||||
path = data.pop('path', None)
|
||||
formatted = {}
|
||||
for key, value in data.iteritems():
|
||||
if isinstance(value, list):
|
||||
|
|
@ -85,6 +86,9 @@ def print_data(data):
|
|||
if value is not None:
|
||||
formatted[key] = value
|
||||
|
||||
if len(formatted) == 0:
|
||||
return
|
||||
|
||||
maxwidth = max(len(key) for key in formatted)
|
||||
lineformat = u'{{0:>{0}}}: {{1}}'.format(maxwidth)
|
||||
|
||||
|
|
@ -107,6 +111,9 @@ class InfoPlugin(BeetsPlugin):
|
|||
help='show library fields instead of tags')
|
||||
cmd.parser.add_option('-s', '--summarize', action='store_true',
|
||||
help='summarize the tags of all files')
|
||||
cmd.parser.add_option('-i', '--include-keys', default=[],
|
||||
action='append', dest='included_keys',
|
||||
help='comma separated list of keys to show')
|
||||
return [cmd]
|
||||
|
||||
def run(self, lib, opts, args):
|
||||
|
|
@ -128,6 +135,11 @@ class InfoPlugin(BeetsPlugin):
|
|||
else:
|
||||
data_collector = tag_data
|
||||
|
||||
included_keys = []
|
||||
for keys in opts.included_keys:
|
||||
included_keys.extend(keys.split(','))
|
||||
key_filter = make_key_filter(included_keys)
|
||||
|
||||
first = True
|
||||
summary = {}
|
||||
for data_emitter in data_collector(lib, ui.decargs(args)):
|
||||
|
|
@ -137,6 +149,9 @@ class InfoPlugin(BeetsPlugin):
|
|||
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:
|
||||
|
|
@ -147,3 +162,33 @@ class InfoPlugin(BeetsPlugin):
|
|||
|
||||
if opts.summarize:
|
||||
print_data(summary)
|
||||
|
||||
|
||||
def make_key_filter(include):
|
||||
"""Return a function that filters a dictionary.
|
||||
|
||||
The returned filter takes a dictionary and returns another
|
||||
dictionary that only includes the key-value pairs where the key
|
||||
glob-matches one of the keys in `include`.
|
||||
"""
|
||||
if not include:
|
||||
return identity
|
||||
|
||||
matchers = []
|
||||
for key in include:
|
||||
key = re.escape(key)
|
||||
key = key.replace(r'\*', '.*')
|
||||
matchers.append(re.compile(key + '$'))
|
||||
|
||||
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
|
||||
|
||||
|
||||
def identity(val):
|
||||
return val
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ Features:
|
|||
``art_filename`` configuration option. :bug:`1258`
|
||||
* :doc:`/plugins/fetchart`: There's a new Wikipedia image source that uses
|
||||
DBpedia to find albums. Thanks to Tom Jaspers. :bug:`1194`
|
||||
* :doc:`/plugins/info`: New options ``-i`` to display only given
|
||||
properties. :bug:`1287`
|
||||
|
||||
Core changes:
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,18 @@ your library::
|
|||
|
||||
$ beet info beatles
|
||||
|
||||
Command-line options include:
|
||||
If you just want to see specific properties you can use the
|
||||
``--include-keys`` option to filter them. The argument is a
|
||||
comma-separated list of simple glob patterns where ``*`` matches any
|
||||
string. For example::
|
||||
|
||||
$ beet info -i 'title,mb*' beatles
|
||||
|
||||
Will only show the ``title`` property and all properties starting with
|
||||
``mb``. You can add the ``-i`` option multiple times to the command
|
||||
line.
|
||||
|
||||
Additional command-line options include:
|
||||
|
||||
* ``--library`` or ``-l``: Show data from the library database instead of the
|
||||
files' tags.
|
||||
|
|
|
|||
|
|
@ -407,7 +407,7 @@ class TestHelper(object):
|
|||
def run_with_output(self, *args):
|
||||
with capture_stdout() as out:
|
||||
self.run_command(*args)
|
||||
return out.getvalue()
|
||||
return out.getvalue().decode('utf-8')
|
||||
|
||||
# Safe file operations
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ from test._common import unittest
|
|||
from test.helper import TestHelper
|
||||
|
||||
from beets.mediafile import MediaFile
|
||||
from beets.util import displayable_path
|
||||
|
||||
|
||||
class InfoTest(unittest.TestCase, TestHelper):
|
||||
|
|
@ -52,17 +53,17 @@ class InfoTest(unittest.TestCase, TestHelper):
|
|||
self.assertNotIn('composer:', out)
|
||||
|
||||
def test_item_query(self):
|
||||
items = self.add_item_fixtures(count=2)
|
||||
items[0].album = 'xxxx'
|
||||
items[0].write()
|
||||
items[0].album = 'yyyy'
|
||||
items[0].store()
|
||||
item1, item2 = self.add_item_fixtures(count=2)
|
||||
item1.album = 'xxxx'
|
||||
item1.write()
|
||||
item1.album = 'yyyy'
|
||||
item1.store()
|
||||
|
||||
out = self.run_with_output('album:yyyy')
|
||||
self.assertIn(items[0].path, out)
|
||||
self.assertIn(b'album: xxxx', out)
|
||||
self.assertIn(displayable_path(item1.path), out)
|
||||
self.assertIn(u'album: xxxx', out)
|
||||
|
||||
self.assertNotIn(items[1].path, out)
|
||||
self.assertNotIn(displayable_path(item2.path), out)
|
||||
|
||||
def test_item_library_query(self):
|
||||
item, = self.add_item_fixtures()
|
||||
|
|
@ -70,8 +71,8 @@ class InfoTest(unittest.TestCase, TestHelper):
|
|||
item.store()
|
||||
|
||||
out = self.run_with_output('--library', 'album:xxxx')
|
||||
self.assertIn(item.path, out)
|
||||
self.assertIn(b'album: xxxx', out)
|
||||
self.assertIn(displayable_path(item.path), out)
|
||||
self.assertIn(u'album: xxxx', out)
|
||||
|
||||
def test_collect_item_and_path(self):
|
||||
path = self.create_mediafile_fixture()
|
||||
|
|
@ -88,9 +89,18 @@ class InfoTest(unittest.TestCase, TestHelper):
|
|||
mediafile.save()
|
||||
|
||||
out = self.run_with_output('--summarize', 'album:AAA', path)
|
||||
self.assertIn('album: AAA', out)
|
||||
self.assertIn('tracktotal: 5', out)
|
||||
self.assertIn('title: [various]', out)
|
||||
self.assertIn(u'album: AAA', out)
|
||||
self.assertIn(u'tracktotal: 5', out)
|
||||
self.assertIn(u'title: [various]', out)
|
||||
|
||||
def test_include_pattern(self):
|
||||
item = self.add_item(album='xxxx')
|
||||
|
||||
out = self.run_with_output('--library', 'album:xxxx',
|
||||
'--include-keys', '*lbu*')
|
||||
self.assertIn(displayable_path(item.path), out)
|
||||
self.assertNotIn(u'title:', out)
|
||||
self.assertIn(u'album: xxxx', out)
|
||||
|
||||
|
||||
def suite():
|
||||
|
|
|
|||
Loading…
Reference in a new issue