M3U Playlist Generator

Directly create playlists from arbitrary search queries on tag metadata in audio files. Crawl a music library directory, check expressions against tags, and output matches for M3U playlists.

Various music players provide their very own way to filter or search a music library and export the result as playlist. This is hardly automatable and the used queries can often not be saved for themselves. A small script can turn a persisted search query into such a “dynamic playlist” in a unified way.

Various file formats and tags are supported, see the mutagen library documentation for details.

Usage

Given a music library path to be scanned recursively for media files, print entries that satisfy the given filter expression.

usage: playlist-gen.py [-h] [--library-path PATH] [--relative-to PATH] [--sort-by KEY] [--json] [XML]

Crawl a music library directory, match expressions against tags, and print filenames for M3U playlists.

positional arguments:
  XML                  filter matches in XML format to evaluate (default: None)

options:
  -h, --help           show this help message and exit
  --library-path PATH  music library to crawl (default: .)
  --relative-to PATH   produce relative filenames
  --sort-by KEY        comma-separated list of sort keys (ascending, insensitive)
  --json               dump JSON instead of M3U filenames (default: False)

The search query is optional but usually comes as XML file, see below for details. Results can also be sorted by multiple tags, for example:

./playlist-gen.py --library-path ~/Music/ --sort-by artist,title MyPlaylist.xml > MyPlaylist.m3u

Direct input is supported as convenience for trivial filters, such that no extra file is needed:

echo '<match tag="genre">Rock</match>' | ./playlist-gen.py --library-path ~/Music/ /dev/stdin

Switching from filename-only to direct JSON output generates a dump of all available information, which also allows further processing.

Per default, full absolute filenames are printed. If the playlist should be “portable” across filesystems, a parent directory for relative paths can be chosen. All output is UTF-8 encoded, such that both the .m3u and .m3u8 file extensions are suitable.

Filter Query Format

Search filters can be given in arbitrarily nested XML syntax and are applied to each crawled input file. The underlying operation bases on regular expressions for tags.

<match tag="tagname">expression</match>
Evaluate a tag value by the given regular expression, which is case-insensitive per default. An absent tag is treated as empty string.
<and>…</and>
All children have to match, false if empty.
<or>…</or>
Any child has to match, true if empty.
<not>…</not>
Does not match if any child matches, thus false if empty.

Depending on the file type, available tag names for matching are, amongst others:

When in doubt, all available information can be inspected by using the JSON dump feature:

./playlist-gen.py --library-path ~/Music/ --json | jq -C

A more elaborated example to create a playlist from certain genres and directories could look like:

<and>
  <match tag="filename">/Favorites/Rock/</match>
  <or>
    <match tag="genre">Hard Rock</match>
    <match tag="genre">^(Progressive[ -]?)?Rock$</match>
    <and>
      <match tag="artist">The Band</match>
      <match tag="album">Epic Odyssey</match>
    </and>
  </or>
  <not>
    <match tag="filename">/Annoying &amp; Bad/</match>
  </not>
</and>

Extended M3U Converter

Regardless of how M3U playlists are generated, they are merely text files with one filename per line. Extended M3U makes use of special # comments for additional metadata, i.e., track duration and display title. This can allow certain players to skip reading file headers for each entry in advance. A second small script adds corresponding metadata on the fly:

usage: m3u2e.py [-h] [-c PATH] [--template STR]

Accept filenames (m3u playlist) from stdin, read media files for metadata, and add corresponding extended m3u headers.
Files must be accessible at the given path and are passed through unchanged in any case.

options:
  -h, --help      show this help message and exit
  -c PATH         change directory for relative filenames
  --template STR  template to be interpolated with tags (default: %{artist} - %{title})

Existing playlists can be converted or directly created by default standard i/o redirections, for example:

./m3u2e.py <MyPlaylist.m3u >MyPlaylist.ext.m3u
find . -name '*.mp3' | ./m3u2e.py > MyPlaylist.m3u
./playlist-gen.py --relative-to ~/Music … | ./m3u2e.py -c ~/Music > MyPlaylist.m3u

Installation

The tagging library as sole dependency can be installed for example via:

apt-get install python3-mutagen
# pip install mutagen # alternatively

The scripts themselves are self-contained and can be directly invoked. Tested for Python 3.8+.

Code & Download