mirror of
https://github.com/beetbox/beets.git
synced 2026-02-22 07:14:24 +01:00
Attachment doc and tests
This commit is contained in:
parent
addac17b2c
commit
c463c6ed84
4 changed files with 267 additions and 3 deletions
|
|
@ -605,6 +605,7 @@ class AttachmentFactory(object):
|
|||
Uses the functions from `register_detector` and the
|
||||
`attachments.types` configuration.
|
||||
"""
|
||||
# TODO Make list unique
|
||||
for detector in self._detectors:
|
||||
try:
|
||||
type = detector(path)
|
||||
|
|
|
|||
194
docs/guides/attachments.rst
Normal file
194
docs/guides/attachments.rst
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
Attachments
|
||||
===========
|
||||
|
||||
Beets also gives you tools to organize the non-audio files that are
|
||||
part of your music collection, e.g. cover images, digital booklets, rip
|
||||
logs, etc. These are called *attachments*. Each attachment has at least
|
||||
a path and a type attribute, and a track or album (an *entity*) it is
|
||||
attached to. The type attribute provides a basic taxonomy for your
|
||||
attachments and allows plugins to provide additional functionality for
|
||||
specific types.
|
||||
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
TODO: Introduction
|
||||
|
||||
|
||||
Attaching Single Files
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Suppose you have downloaded the cover of the Beatles’ “Revolver” album
|
||||
and the file is called `cover.jpg`. You can attach it to the album with
|
||||
the following command: ::
|
||||
|
||||
$ beet attach /path/to/cover.jpg --type cover album:Revolver
|
||||
add cover attachment /path/to/cover.jpg to 'The Beatles - Revolver'
|
||||
|
||||
The file at ``/path/to/cover.jpg`` has now been moved to the album
|
||||
directory and you can query the attachment with ::
|
||||
|
||||
$ beet attachls type:cover e:album:Revolver
|
||||
cover: /music/Revolver/cover.jpg
|
||||
|
||||
The query arguments for that `attachls` command work like the arguments
|
||||
for the usual :ref:`ls <list-cmd>` command, with one addition: You can match against
|
||||
the album or track (the *entity*) the file is attached to using the
|
||||
``e:`` prefix. For more on the `attachls` command see :ref:`the
|
||||
reference <attachls-cmd>`.
|
||||
|
||||
Maybe you want your cover images to have a different name, say
|
||||
`front.jpg`. You can change the default paths for you attachments
|
||||
through the configuration file: ::
|
||||
|
||||
attachments:
|
||||
paths:
|
||||
- type: cover
|
||||
path: front.$ext
|
||||
|
||||
This moves all attachments of type cover are to `front.ext` in the
|
||||
corresponding album directory, where `ext` is the extension of the
|
||||
source file. ::
|
||||
|
||||
$ beet attach /path/to/cover.jpg --type cover album:Revolver
|
||||
add cover attachment /path/to/cover.jpg to 'The Beatles - Revolver'
|
||||
|
||||
$ beet attachls type:cover e:album:Revolver
|
||||
cover: /music/Revolver/front.jpg
|
||||
|
||||
|
||||
Beets can also be configured to automatically detect the type of an
|
||||
attachment from its filename. ::
|
||||
|
||||
attachments:
|
||||
types:
|
||||
cover.*: cover
|
||||
|
||||
The :ref:`types configuration <conf-attachments-types>` is a map from
|
||||
glob patterns or regular expressions to type names. You can now omit
|
||||
the ``--type`` option and beet will detect the type automatically ::
|
||||
|
||||
$ beet attach /path/to/cover.jpg album:Revolver
|
||||
add cover attachment /path/to/cover.jpg to 'The Beatles - Revolver'
|
||||
|
||||
$ beet attachls type:cover e:album:Revolver
|
||||
cover: /music/Revolver/cover.jpg
|
||||
|
||||
Of course you can still specify another type on the command line.
|
||||
|
||||
|
||||
Importing Attachments
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Beets will automatically create attachments when you import new music.
|
||||
|
||||
Since you already have “Revolver” in your library, suppose you want to
|
||||
also add the “Abbey Road” album. You have ripped the album and moved it
|
||||
to the ``/import`` directory. The directory also contains the files
|
||||
``cover.jpg`` and ``booklet.pdf`` that you want to create attachments
|
||||
for. To automatically detect the types, we add the following to our
|
||||
configuration: ::
|
||||
|
||||
attachments:
|
||||
types:
|
||||
cover.*: cover
|
||||
booklet.pdf: booklet
|
||||
|
||||
In addition to adding the album to your library, the ``beet import
|
||||
/import`` command will now print the lines ::
|
||||
|
||||
add cover attachment /import/cover.jpg to 'The Beatles - Revolver'
|
||||
add booklet attachment /import/booklet.pdf to 'The Beatles - Revolver'
|
||||
|
||||
and you can confirm it with ::
|
||||
|
||||
$ beet attachls "e:Abbey Road"
|
||||
/music/Abbey Road/cover.jpg
|
||||
/music/Abbey Road/booklet.pdf
|
||||
|
||||
For each album that is about to be imported, beets looks at all the
|
||||
non-music files contained in the album’s source directory. Beets then
|
||||
tries to determine the type of each file and, if successful, creates an
|
||||
attachment of this type. Files with no type are ignored. The file
|
||||
manipulations for attachments mirror that of the music files and can be
|
||||
configured through the ``import.move`` and ``import.copy`` options.
|
||||
|
||||
|
||||
Import Attachments Only
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you have used beets before, you may already have some files in your
|
||||
library that you want to attach with beets. Instead of repeating the
|
||||
`attach` command for each of those files, there is a :ref:`attach-import
|
||||
command <attach-import-cmd>`. This command is similar to a reimport
|
||||
with ``beet import``, but it just creates attachments and skip all
|
||||
audio files.
|
||||
|
||||
As an example, suppose you have a ``cover.jpg`` file in some of your
|
||||
album directories and you want them to be added as a ``cover``
|
||||
attachment to their corresponding album. First make sure the type of
|
||||
the file is recognised by beets. ::
|
||||
|
||||
attachments:
|
||||
types:
|
||||
cover.jpg: cover
|
||||
|
||||
Then run ::
|
||||
|
||||
$ beet attach-import
|
||||
add cover attachment /music/Revolver/cover.jpg to 'The Beatles - Revolver'
|
||||
add cover attachment /music/Abbey Road/cover.jpg to 'The Beatles - Abbey Road'
|
||||
...
|
||||
|
||||
and all cover images will be attached to their albums.
|
||||
|
||||
|
||||
.. _attachment-plugins:
|
||||
|
||||
Attachment Plugins
|
||||
------------------
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
Reference
|
||||
=========
|
||||
|
||||
|
||||
Command-Line
|
||||
------------
|
||||
|
||||
``attach``
|
||||
^^^^^^^^^^
|
||||
|
||||
``attachls``
|
||||
^^^^^^^^^^^^
|
||||
|
||||
``attach-import``
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
.. _conf-attachments-types:
|
||||
|
||||
types
|
||||
^^^^^
|
||||
|
||||
paths
|
||||
^^^^^
|
||||
|
||||
|
||||
To Do
|
||||
=====
|
||||
|
||||
* Fallback type for discover and import
|
||||
* Ignore dot files
|
||||
* Interactive type input on import (create issue)
|
||||
* Documentation for multiple types (do we need them)
|
||||
* Document track attachments
|
||||
* Move attachments with same path
|
||||
* Automatically determine query from path for `attach`
|
||||
* Remove warning for unknown files
|
||||
* Additional template variables overwritten by flex attrs
|
||||
|
|
@ -11,4 +11,5 @@ guide.
|
|||
main
|
||||
tagger
|
||||
advanced
|
||||
attachments
|
||||
migration
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class AttachmentTestHelper(TestHelper):
|
|||
return self._factory
|
||||
|
||||
def touch(self, path, dir=None, content=''):
|
||||
# TODO move into TestHelper
|
||||
if dir:
|
||||
path = os.path.join(dir, path)
|
||||
|
||||
|
|
@ -133,7 +134,7 @@ class AttachmentTestHelper(TestHelper):
|
|||
def cli_output(self, *args):
|
||||
with capture_stdout() as output:
|
||||
self.runcli(*args)
|
||||
return output.getvalue().split('\n')
|
||||
return [l for l in output.getvalue().split('\n') if l]
|
||||
|
||||
def libpath(self, *components):
|
||||
components = \
|
||||
|
|
@ -141,6 +142,64 @@ class AttachmentTestHelper(TestHelper):
|
|||
return os.path.join(self.libdir, *components)
|
||||
|
||||
|
||||
class AttachmentDocTest(unittest.TestCase, AttachmentTestHelper):
|
||||
"""Tests for the guide of the attachment documentation.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.setup_beets()
|
||||
self.config['path_formats'] = {'default': '$album/$track $title'}
|
||||
|
||||
def tearDown(self):
|
||||
self.teardown_beets()
|
||||
|
||||
@unittest.skip
|
||||
def test_attache_single_file_with_type(self):
|
||||
self.add_album(name='Revolver')
|
||||
attachment_path = self.touch('cover.jpg')
|
||||
|
||||
output = self.cli_output('attach', attachment_path, '--type',
|
||||
'cover', 'album:Revolver')
|
||||
self.assertIn("add cover attachment {0} to 'The Beatles - Revolver'"
|
||||
.format(attachment_path), output)
|
||||
|
||||
output = self.cli_output('attachls', 'type:cover', 'e:album:Revolver')
|
||||
self.assertIn('cover: {0}/Revolver/cover.jpg'
|
||||
.format(self.libdir), output)
|
||||
|
||||
def test_attache_single_file_with_type_and_path_config(self):
|
||||
self.config['attachments']['paths'] = [{
|
||||
'type': 'cover',
|
||||
'path': 'front.$ext',
|
||||
}]
|
||||
self.add_album(name='Revolver')
|
||||
attachment_path = self.touch('cover.jpg')
|
||||
|
||||
self.runcli('attach', attachment_path, '--type',
|
||||
'cover', 'album:Revolver')
|
||||
|
||||
output = self.cli_output('attachls', 'type:cover', 'e:album:Revolver')
|
||||
self.assertIn('cover: {0}/Revolver/front.jpg'
|
||||
.format(self.libdir), output)
|
||||
|
||||
@unittest.skip
|
||||
def test_import_cover_and_booklet(self):
|
||||
importer = self.create_importer()
|
||||
album_dir = os.path.join(self.importer.paths[0], 'album 0')
|
||||
cover_path = self.touch(album_dir, 'cover.jpg')
|
||||
booklet_path = self.touch(album_dir, 'booklet.pdf')
|
||||
|
||||
with capture_stdout() as output:
|
||||
importer.run()
|
||||
output = output.getValue().split('\n')
|
||||
self.assertIn("add cover attachment {0} to 'Artist - Album 0'"
|
||||
.format(cover_path), output)
|
||||
self.assertIn("add booklet attachment {0} to 'Artist - Album 0'"
|
||||
.format(booklet_path), output)
|
||||
|
||||
# TODO attach-import
|
||||
|
||||
|
||||
class AttachmentDestinationTest(unittest.TestCase, AttachmentTestHelper):
|
||||
"""Test the `attachment.destination` property.
|
||||
"""
|
||||
|
|
@ -263,6 +322,7 @@ class AttachmentTest(unittest.TestCase, AttachmentTestHelper):
|
|||
self.teardown_beets()
|
||||
|
||||
# attachment.move()
|
||||
# TODO move attachments with same path
|
||||
|
||||
def test_move(self):
|
||||
attachment = self.create_album_attachment(self.touch('a'))
|
||||
|
|
@ -424,7 +484,10 @@ class AttachmentFactoryTest(unittest.TestCase, AttachmentTestHelper):
|
|||
self.assertEqual(len(attachments), 1)
|
||||
self.assertEqual(attachments[0].type, 'image')
|
||||
|
||||
# TODO Glob and RegExp
|
||||
# TODO Glob and RegExp.
|
||||
# * Globs dont match files starting with a dot
|
||||
# * Add extended bash globs.
|
||||
# * Regexp must match full basename
|
||||
def test_detect_config_types(self):
|
||||
self.config['attachments']['types'] = {
|
||||
'.*\.jpg': 'image'
|
||||
|
|
@ -601,7 +664,10 @@ class EntityAttachmentsTest(unittest.TestCase, AttachmentTestHelper):
|
|||
|
||||
|
||||
class AttachmentImportTest(unittest.TestCase, AttachmentTestHelper):
|
||||
"""Attachments should be created in the importer.
|
||||
"""Import process should discover and add attachments.
|
||||
|
||||
Since the importer uses the `AttachmentFactory.discover()` method more
|
||||
comprehensive tests can be found in that test case.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
|
|
@ -651,6 +717,8 @@ class AttachmentImportTest(unittest.TestCase, AttachmentTestHelper):
|
|||
os.path.splitext(item.path)[0] + ' - cover.jpg'
|
||||
)
|
||||
|
||||
# TODO interactive type input
|
||||
|
||||
|
||||
class AttachCommandTest(unittest.TestCase, AttachmentTestHelper):
|
||||
"""Tests the `beet attach FILE QUERY...` command
|
||||
|
|
|
|||
Loading…
Reference in a new issue