Tagging and managing music with beets

Last updated: July 8th, 2025

Table of contents

Why metadata matters

Tagged music files come with significant benefits. To name just a few:

Beets as a music manager

If you’ve never tagged music before or want to change your workflow, maybe you can use beets to manage your library. You can gain many of the benefits mentioned before through fetching and applying metadata from sources like MusicBrainz. Plugins fill in the gaps.

You may like to keep the documentation for beets in a separate tab while you read this. This blog post provides an overview, but the docs provide more exhaustive detail.

How to install beets

You can install beets with a package manager, either the one your operating system provides or a Python package manager like pip.

If you use OpenBSD, you can install beets with pkg_add:

# pkg_add beets

How to configure beets

By default, beets places imported music in ~/Music. If you need to change that, add the desired path to the config.yaml file like so:

directory: /path/to/music/library

Often you can go straight to importing music without any further modifications after this. Otherwise, the documentation on configuration can help you if you run into any issues.

How to import music

Beets needs a music library to work with, which means you need to import some music.

$ beet import /path/to/album

If the similarity score meets the threshold, beets tags the music automatically and moves on. Otherwise, beets asks for more details.

How to query music

To list your music, use beet ls. But beware: this lists all music currently managed by beets.

To narrow down your query, you can use the available metadata fields. You can list those metadata fields with beet fields. The output includes fields like album, artist, year, country, and others.

Some plugins provide metadata fields as well. For instance, with the lastgenre plugin installed, you can query by genre like this.

$ beet ls genre:'Progressive Rock'

What plugins does beets have?

Beets includes many high quality plugins that extend its capabilities. I show only a fraction of them here, so make sure to read through the documentation on plugins afterward.

Album art with fetchart

If you use a minimal music player without album art display capabilities, then this may not matter to you. Still, with more full-featured media applications, missing artwork bothers some people.

To fetch album art with the fetchart plugin, add the following to your config.yaml:

plugins: fetchart

Then, update the library.

$ beet fetchart

Genre metadata with lastgenre

Out of the box, beets doesn’t deal with genres at all because MusicBrainz doesn’t have that information.

To add genre information to your collection by pulling it from Last.fm, put this inside your config.yaml:

plugins: lastgenre

Then, update the library.

$ beet lastgenre

Cue sheets and how to use them

Beets needs a separate file for each track to tag music. Yet sometimes only one Free Lossless Audio Codec (FLAC) file exists for an entire album. Although many other formats besides FLAC exist, pretend for now that your album came with one FLAC file that contains all the tracks.

In this case, look for a text file that describes the album’s track layout with timestamps. People refer to a text file like this as a “cue sheet.” With a cue sheet, you can split that single FLAC file into separate files by track, through a process known as “cue splitting.”

Given a cue sheet and a FLAC file with many tracks, you can perform cue splitting like this.

  1. Install shntool for cue splitting, and cuetools to tag the resulting files.

    # pkg_add shntool cuetools
    
  2. Navigate to the album in question.

    $ cd /path/to/album
    
  3. Split the FLAC file.

    $ shnsplit -f example.cue -o flac example.flac
    

    -f points to the cue sheet. -o specifies the encoder, which defaults to Waveform Audio File Format (WAV).

    By default, shnsplit creates files with this format: split-track01.flac. beet import renames files according to their metadata, so the filenames don’t matter.

  4. Rename the FLAC file so that it ends in .bak.

    $ mv example.flac{,.bak}
    

    You need to perform this step so that cuetag won’t target the original FLAC file later on.

  5. Tag the split files with the original metadata.

    $ cuetag example.cue ./*.flac
    

    ./*.flac targets all FLAC files in the current directory. See shellcheck’s wiki entry for SC2035 for an explanation of why I use ./*.flac instead of *.flac.

  6. If satisfied, delete the original FLAC file.

    $ rm example.flac.bak
    
  7. Now that you performed cue splitting, you can import the music.

    $ beet import .
    

Scripting cue splitting

Given that this process can feel tedious, I wrote a small shell script to automate cue splitting. splitflac works like so:

$ splitflac example.cue example.flac

By default, splitflac doesn’t delete the original FLAC file. To do so on a successful split, pass -d.

$ splitflac -d example.cue example.flac