Implement and test attach command

This commit is contained in:
Thomas Scholtes 2014-07-06 17:13:19 +02:00
parent 8852bd24d0
commit acf9e21dc3
4 changed files with 148 additions and 3 deletions

View file

@ -15,8 +15,10 @@
import re
import urlparse
import optparse
from argparse import ArgumentParser
from beets.plugins import find_plugins
from beets import dbcore
from beets.dbcore.query import Query, AndQuery
@ -232,9 +234,12 @@ class AttachmentFactory(object):
"""
self._collectors.append(collector)
def register_plugin(self, plugin):
self.register_discoverer(plugin.attachment_discoverer)
self.register_collector(plugin.attachment_collector)
def register_plugins(self, plugins):
for plugin in plugins:
if hasattr(plugin, 'attachment_discoverer'):
self.register_discoverer(plugin.attachment_discoverer)
if hasattr(plugin, 'attachment_collector'):
self.register_collector(plugin.attachment_collector)
def _discover_types(self, path):
types = []
@ -252,6 +257,8 @@ class AttachmentFactory(object):
all_meta = {}
for collector in self._collectors:
try:
# TODO maybe we should provide file handle for checking
# content
meta = collector(type, path)
if isinstance(meta, dict):
all_meta.update(meta)
@ -300,7 +307,54 @@ class AttachmentCommand(ArgumentParser):
pass
class AttachCommand(object):
"""Duck type for ui.Subcommand
"""
def __init__(self):
self.name = 'attach'
self.parser = optparse.OptionParser()
self.aliases = ()
self.help = 'create an attachment for an album or a ' \
'track and move the attachment'
self.hide = False
self.parser.add_option(
'-c', '--copy', action='store_true', dest='copy',
help='copy attachment intead of moving them'
)
self.parser.add_option(
'--track', action='store_true', dest='track',
help='attach path to the tracks matched by the query'
)
self.parser.add_option(
'-t', '--type', dest='type',
help='create one attachment with this type',
)
def func(self, lib, opts, args):
# FIXME prevents circular dependency
from beets.ui import decargs
factory = AttachmentFactory(lib)
factory.register_plugins(find_plugins())
path = args.pop(0)
if opts.track:
entities = lib.items(decargs(args))
else:
entities = lib.albums(decargs(args))
for entity in entities:
if opts.type:
factory.create(path, opts.type, entity).add()
else:
for attachment in factory.discover(path, entity):
attachment.add()
class AttachmentRefQuery(Query):
"""Matches any attachment whose entity is `entity`.
"""
def __init__(self, entity):
self.entity = entity
@ -314,6 +368,8 @@ class AttachmentRefQuery(Query):
class AttachmentEntityQuery(Query):
"""Matches any attachment whose entity matches `entity_query`.
"""
def __init__(self, entity_query):
self.query = entity_query

View file

@ -38,6 +38,7 @@ from beets.util import syspath, normpath, ancestry, displayable_path
from beets.util.functemplate import Template
from beets import library
from beets import config
from beets import attachments
from beets.util.confit import _package_path
VARIOUS_ARTISTS = u'Various Artists'
@ -78,6 +79,9 @@ def _do_query(lib, query, album, also_items=True):
return items, albums
default_commands.append(attachments.AttachCommand())
# fields: Shows a list of available fields for queries and format strings.
def fields_func(lib, opts, args):

View file

@ -170,6 +170,14 @@ class TestHelper(object):
beets.plugins.load_plugins(plugins)
beets.plugins.find_plugins()
def add_plugin(self, plugin):
"""Add a plugin instance to the list returned by
`plugins.find_plugins()`.
"""
def create_plugin():
return plugin
beets.plugins._classes.add(create_plugin)
def unload_plugins(self):
"""Unload all plugins and remove the from the configuration.
"""

View file

@ -13,8 +13,13 @@
# included in all copies or substantial portions of the Software.
import os
from tempfile import mkstemp
from _common import unittest
from helper import TestHelper
import beets.ui
from beets.plugins import BeetsPlugin
from beets.attachments import AttachmentFactory
from beets.library import Library, Album, Item
@ -87,6 +92,78 @@ class EntityAttachmentsTest(unittest.TestCase):
[attachment.id])
class AttachCommandTest(unittest.TestCase, TestHelper):
def setUp(self):
self.setup_beets()
self.setup_log_attachment_plugin()
self.tmp_files = []
def tearDown(self):
self.teardown_beets()
self.unload_plugins()
for p in self.tmp_files:
os.remove(p)
def test_attach_to_album(self):
album = Album(album='albumtitle')
self.lib.add(album)
attachment_path = self.mkstemp('.log')
self.runcli('attach', attachment_path, 'albumtitle')
attachment = album.attachments().get()
self.assertEqual(attachment.type, 'log')
def test_attach_to_album_and_move(self):
self.skipTest('Not implemented')
def test_file_relative_to_album_dir(self):
self.skipTest('Not implemented')
def test_attach_to_item(self):
item = Item(title='tracktitle')
self.lib.add(item)
attachment_path = self.mkstemp('.log')
self.runcli('attach', '--track', attachment_path, 'tracktitle')
attachment = item.attachments().get()
self.assertEqual(attachment.type, 'log')
def test_attach_to_item_and_move(self):
self.skipTest('Not implemented')
def test_user_type(self):
album = Album(album='albumtitle')
self.lib.add(album)
attachment_path = self.mkstemp()
self.runcli('attach', '-t', 'customtype', attachment_path, 'albumtitle')
attachment = album.attachments().get()
self.assertEqual(attachment.type, 'customtype')
def test_unknown_warning(self):
self.skipTest('Not implemented')
# Helpers
def runcli(self, *args):
beets.ui._raw_main(list(args), self.lib)
def mkstemp(self, suffix=''):
(handle, path) = mkstemp(suffix)
os.close(handle)
self.tmp_files.append(path)
return path
def setup_log_attachment_plugin(self):
def log_discoverer(path):
if path.endswith('.log'):
return 'log'
log_plugin = BeetsPlugin()
log_plugin.attachment_discoverer = log_discoverer
self.add_plugin(log_plugin)
def suite():
return unittest.TestLoader().loadTestsFromName(__name__)