Skip to content
Adrian Sampson edited this page Apr 7, 2018 · 25 revisions

Here, we'll design the oft-requested attachments feature (see #111).

Use Cases

Importing

Upon import of audio files into the Beets library, users may wish to...

  • Move the accompanying .log file into the same directory as the imported audio files.
  • Move the accompanying .CUE file into the same directory as the imported audio files.
  • Move album artwork associated with the audio files There could be multiple image files.
  • Have the ability to move arbitrary files associated with the audio files, by way of a prompt questioning the user whether or not to import 'file-x.x' or by way of user declaration in an optional command, "include files of .xx".
  • Declare a white list of file extensions they wish to import with the audio files.
  • Get files by recursivley searching sub-directories
  • Enable copy/move of Artist level images to corresponding Artist in beets structure, that is if pinkfloyd.jpg is present in the pink floyd artist folder move it to the pink floyd root level in the beets output

Querying and Modifying

  • Track the attachments in the database
  • Query the database to see all albums with attachments and those without. ie. Find all albums without a .log file (so a user can re-rip those albums, for example).
  • Move the attachments (should attachments be allowed to be stored anywhere other than the album folder, or can we think of an example in which the user would want to associate an attachment with an album that would need to be stored elsewhere?)
  • Rename the attachments

Optional

  • A configuration option to convert auto-downloaded image formats to the preferred format of the user (jpg/png) would allow for easy standardization in filetypes. (Does this feature belong in the attachments section?)
  • Incorporate a log checker function to be able to see a list of all "100% Accurate Rip" Logs and those that aren't 100%.
  • This becomes a small issue when renaming files. The log files will look for certain filenames and will score less than 100% because beets has renamed them. During discussion, it seems that the renaming is and should be the desired effect, and that the log file will have to stay the way it is, at less than 100.
  • Convert cue sheets / log files to UTF-8.
  • If a log or cue file is found, rename the file to $artist - $album.cue/log or other given variables names.
  • If several images are present in the original folder, put them in an "artwork" directory in the new library folder (front.jpg, back.jpg, cd.jpg). This however, may cause an issue with media players that rely on an artwork file in the same directory as the files being played.
  • Possibility to use the CUE file to split large FLAC files
  • Possibility to detect which attachments are meant to exist and those that are clutter

Beets functionality

Beets would need new functionality to cover the above use cases, this is a list of what Beets would need to do.

  • Upon import, Beets would need to be able to analyse a folder and prompt the user that additional attachments exist.
  • Beets would need to be able to offer options to the user when importing, ie. beets import --attachments=log,cue ...
  • Include functionality so that Beets reviews the library filesystem and if attachments exist in folders (for example, users have manually copied these attachments over - common amongst users who use log files), that haven't been logged in the database, it should "import" them and associate them with the album in question.
  • Include functionality so that existing attachments can be modified (renamed, moved(?), and queried/searched).

CLI Overview

Here's a general way that the CLI could be organized. Please feel free to edit this as necessary, as it's very much a work in progress.

import

beet import --attachments=jpg,cue,log

A slight modification to the existing import function would be necessary, and would be largely preferred to using an attach command and doing everything manually. When importing normally, beets will also be scanning for files with extensions meeting those specified with the --attachments flag. If they are found, then they would be attached to the album exactly as if they were run through the beet attach command that follows.

attach

beet attach [-cpel] ATTACHMENT QUERY

If ATTACHMENT is a file attach it to all the albums matching the supplied query. If ATTACHMENT is a directory attach each file contained in the directory or its subdirectories to the albums matching the query.

If the -l, --local option is given it resolves ATTACHMENT relative to the albums’ base directory. This means that for any album matching the query the file $albumpath/ATTACHMENT is attached to the album. Here $albumpath is the path to the directory containing all the album tracks. It is currently also used by the coverart path.

All non-image files will be attached normally. In the case of an image file (jpg/png), beets will assume that they are some kind of artwork (front cover or otherwise), and then prompt the user to enter what kind of artwork they're supplying. We should support all the different image types that are defined by ID3v2. All artwork other than the front cover should be stored in an 'artwork' subdirectory in the album folder. The exception to this is artist images. ID3v2 has an 'artist image' specification, and if the user, when prompted, selects the 'artist' option, the file will be placed in the artist directory, above the album directory as 'artist.jpg'.

For cases other than artwork, we can account for a few of them. Common files would be .log files and .cue files. Beets can be on the lookout for these and rename as necessary (or as specified by user in config.yaml).

  • -m, Moves the given attachment instead of copying it from its source location.
  • -p, Pretend to attach everything, but don't actually modify anything.
  • -e, If the user at any point is picking what kind of artwork is being attached, and they select the front cover, if this option is enabled, that front artwork will automatically be embedded into the media files of that album.

coverart

beet coverart [-f FORMAT] [-n] [-t TYPE] QUERY

List the cover art attachments for albums or items matching QUERY.

  • -f FORMAT specifies a template string for the output similar to the list command.

  • -t TYPE restrict the output to images with the given cover art type. TYPE is one of the names for ID3v2 cover art types.

  • -n inverts the result. It lists all albums or items matching QUERY that do not have a cover art attachment. If the type option is present it only shows the items that do not have a cover art attachment of type TYPE.

beet coverart [-le] -a IMAGE [-i | -t TYPE] QUERY

Attach the file IMAGE to each album matching QUERY and move it to the correct location. If IMAGE is a directory, import all image files in that directory as cover art.

  • If the -l flag is present interpret IMAGE as a path relative to the each albums directory. This is the same as for the attach command.

  • With the -i, --interactive flag, the command interactively asks the user whether a file should be attached an what its type should be.

  • -e, --embed Write added cover art to tags.

joe@computer:~$ beet coverart -e -i -a /artwork/directory humbug
Attaching /artwork/directory/front.jpg. Please pick the appropriate artwork type:
    front, back, leaflet, media, artist, during_recording, during_performance, artist_logo, other
    Pick one: front
    Embedding artwork into tags... success. 10 files embedded.
Attaching /artwork/directory/back.jpg. Please pick the appropriate artwork type:
    front, back, leaflet, media, artist, during_recording, during_performance, artist_logo, other
    Pick one: back
    Embedding artwork into tags... success. 10 files embedded.

The location to store cover art may be customized by the configuration:

coverart:
  store:
     front: $albumpath/cover
     back: $albumpath/cover-back

We also might want to give heuristics for mapping file names to types using regular expressions.

  types:
     "(front|cover|folder)": front
     "back": back

Examples

Some modification to the beet list function would also be necessary. Here is a suggested modification:

beet list --attachments QUERY

The extra --attachments flag would let the list command know that we're looking for associated files to albums, rather than singletons/albums themselves. This would print a list of all albums meeting the query.

joe@computer:~$ beet list --attachments "arctic monkeys"
Found 2 entries which match supplied query.
1) Arctic Monkeys - Favourite Worst Nightmare
2) Arctic Monkeys - Humbug
Please specify which entry you'd like to see attachment data on: 

The user then specifies which number entry they'd like to view the data on. They enter the corresponding number, and all attachment data is printed. The only issue I'm seeing with this is that inherently, the beet list command does nothing other than list items, as it should. We do need some way to edit the attachments, so in this instance I've suggested that the command, after listing attachments, asks the user if they'd like to edit any of it. This may be a bad idea, or may be the only convenient way to do it. Not quite sure at this point.

Please specify which entry you'd like to see attachment data on: 2
Found 5 attachments.
1) folder.jpg (Front cover art, stored as "~/music/Arctic Monkeys/Humbug/folder.jpg")
2) back.jpg (Back cover art, stored as "~/music/Arctic Monkeys/Humbug/artwork/back.jpg")
3) artist_logo.jpg (Artist logo, stored as "~/music/Arctic Monkeys/Humbug/artwork/logo.jpg")
4) artist.jpg (Artist/band image, stored as "~/music/Arctic Monkeys/artist.jpg")
5) Humbug.log (Ripping log, stored as "~/music/Arctic Monkeys/Humbug/Humbug.log")
Would you like to edit any attachment data? (y/N): y
Please specify an attachment number to edit (or q to quit): 1

Selected front cover art (folder.jpg, ~/music/Arctic Monkeys/Humbug/folder.jpg"). What would you like to do?
	rename, move, convert, type, un-attach, delete
	Pick one: rename
	Pick a new name for "folder.jpg": artwork.jpg
	Renamed "folder.jpg" to "artwork.jpg" (~/music/Arctic Monkeys/Humbug/artwork.jpg").
Would you like to return to the attachments list? (Y/n): Y

Found 5 attachments.
1) artwork.jpg (Front cover art, stored as "~/music/Arctic Monkeys/Humbug/artwork.jpg")
2) back.jpg (Back cover art, stored as "~/music/Arctic Monkeys/Humbug/artwork/back.jpg")
3) artist_logo.jpg (Artist logo, stored as "~/music/Arctic Monkeys/Humbug/artwork/logo.jpg")
4) artist.jpg (Artist/band image, stored as "~/music/Arctic Monkeys/artist.jpg")
5) Humbug.log (Ripping log, stored as "~/music/Arctic Monkeys/Humbug/Humbug.log")
Please specify an attachment number to edit (or q to quit): q

joe@computer:~$

Path Configuration

Similar to tracks, each attachment has a default path. This path is configurable through templates depending on the attachment type.

The following configuration example introduces the main features for configuring the paths. They are explained in detail below.

attachments:
  paths:
    # Default if nothing else matches
    - "${entity_prefix}${basename}"

    # Different templates depending on entity type
    - type: riplog
      # Path is relative to ${album_dir}.
      path: rip.log
    - type: riplog
      ref_type: track
      path: "${track_base}.log"

    # Use flexible attributes
    - type: coverart
      path: "cover-${covertype}.${ext}"
    - type: coverart
      covertype: front
      path: "cover.${ext}"

    # For albums: 'albumpath/Album Artist - Album Name.jpg'
    # For tracks: 'track/path/without/ext.jpg'
    - type: froncover
      ext:  .jpg

    # Path relative to the `directory` configuration
    - type: cuesheet
      path: "${libdir}/cuesheets/${ref}.cue"
      

Template selection

The attachments.paths configuration is a list of path specifications. Each path specification includes a path template in the path or ext values. The remaining key-value pairs give a query to match against the attachment. We use the path template of the last query to match. Note that the query requires an exact match, that is type: cover does not match an attachment with type coverart.

If a path specification is just a string it is assumed to match all attachments and the string is treated as the template.

Template variables

All attributes and flexible attributes of an attachment and its entity are available as template variables. If there is an attribute of the same name on both the attachment and its entity, the former takes precedence.

In addition we have the following useful variables.

  • $entity_prefix. For album attachments this is the album directory ($entity_dir). For tracks this is "${track_base} - ". depending on the reference type.
  • $entity_dir. The album directory or the directory containing the singleton track. The directory includes a trailing slash.
  • $ext_prefix. For album attachments this is "${entity_dir}/${albumartist} - ${album}". For track attachments it is $track_base.
  • $track_base. The track path without its extension.
  • $libdir. The value of the directory configuration value.

Path expansion

If the template of the path key evaluates to a relative path it is prepended with entity_prefix template variable. If the path template is taken from the ext key it is instead preprended to the ext_prefix variable. Note that paths that are inside the beets directory are stored as relative to the beets directory.