About Articles Projects Links Apps Feed


A dynamic and extensible music library organizer


Demlo organizes your music library automatically and dynamically. It runs a chain of user-defined Lua scripts using variables such as tags and file properties. This way it yields virtually unlimited customization power to the user.


Here follows a sample output showing the “before-after” differences.

$ demlo fantasie_impromptu.flac
:: Load config: /home/johndoe/.config/demlo/demlorc
:: Load script: /usr/share/demlo/scripts/tag.lua
:: Load script: /usr/share/demlo/scripts/sub.lua
:: Load script: /usr/share/demlo/scripts/case.lua
:: Load script: /usr/share/demlo/scripts/punctuation.lua
:: Load script: /usr/share/demlo/scripts/encoding.lua
:: Load script: /usr/share/demlo/scripts/path.lua
:: Load script: /usr/share/demlo/scripts/cover.lua
:: Load script: /home/johndoe/.config/demlo/scripts/lossy.lua
==> fantasie_impromptu.flac

					       === FILE         ===
	 [/home/johndoe/fantasie_impromptu.flac] | path         | [/home/johndoe/music/Chopin/The Best Ever Piano ]
						 |              | [Classics (John Doe, 2014)/Fantasie-Impromptu in]
						 |              | [ C Sharp Minor, Op. 66.ogg]
					  [flac] | format       | [ogg]
				[bitrate=320000] | parameters   | [[-c:a libvorbis -q:a 10]]
					       === TAGS         ===
	    [john doe's classical collection II] | album        | [John Doe's Classical Collection II]
					      [] | album_artist | [Chopin]
					      [] | artist       | [Chopin]
					[chopin] | composer     | []
				    [02/13/2014] | date         | [2014]
				      [Classics] | genre        | [Classical]
				     [John_Doe ] | performer    | [John Doe]
   [Fantasie-Impromptu in c sharp MInor , Op.66] | title        | [Fantasie-Impromptu in C Sharp Minor, Op. 66]
					       === COVERS       ===
		  ['cover.jpg' [500x500] <jpeg>] | external     | [/home/johndoe/music/Chopin/The Best Ever Piano ]
						 |              | [Classics (John Doe, 2014)/Cover.jpg]


Demlo can address any of these recurring music library issues (and much more):

  • Lack of folder structure.
  • Tags are not homogeneous, case is wrong, some entries are filled and other not.
  • Bad audio quality.
  • Audio codecs are not always consistent. (Lossless/lossy…)
  • mp3’s id3tags hell…
  • Multiple covers, embedded and/or external, bad quality, etc.


A few scripts are provided by default. They give a good example of the possibilities offered by Demlo. Pick which one to load by default, rewrite some of them, or write new ones from scratch. You can preview changes to make sure everything looks satisfactory.

Call Demlo over a complete music library and it will process everything in one single run.

Scripts can be chained: this feature makes Demlo extremely flexible and powerful.

  • Move all files according to a unique, yet dynamic folder hierarchy. For instance, setting

    output.path = table.concat {library, '/', o.artist, '/',
        o.album ~= nil and (o.date ~= nil and o.album .. '/' or o.date .. ' - ' .. o.album .. '/') or '',
        track_padded ~= nil and '' or track_padded .. ' - ',

    will move john doe - 1. intro.ogg to

    • ~/music/John Doe/2013 - Great Album/01 - Intro.ogg if there is an album and a date,
    • ~/music/John Doe/Great Album/01 - Intro.ogg if there is an album but no date,
    • ~/music/John Doe/01 - Intro.ogg if there is no album.
  • Case checking: the default script case.lua will turn everything to title case or sentence case. It supports special cases such as Roman numerals, McDonald, etc. It also supports a list of exceptions, e.g. “DJ” and “feat”.
  • Encoding: the file format and codec is exposed to user scripts so that you can conditionally re-encode any file. The encoding is performed by FFmpeg. The encoding parameters can be fully customized. (Format, codec, bitrate, etc.)
  • Covers: Demlo makes it possible to automatically remove embedded covers, skip duplicates, skip covers below a quality threshold, resize big covers down, etc.
  • Different kinds of music: classical, soundtrack, and band music usually have different types of tags and different folder structures. It is possible to manage different music libraries easily by setting the desired root folder from command-line.
  • Demlo supports both external and embedded cuesheets. It is also able to parse “non-standard” sheets, which are not so uncommon.
  • Demlo can be interfaced with any other program, both ways. (X calls Demlo or Demlo calls X.) For instance you can edit tags with your favorite text editor.
  • Demlo has MusicBrainz and Cover Archive support for online tagging and cover fetching.



See demlo -h and the manual.


See the LICENSE file in the source code.

The Go rewrite

Demlo used to be written in pure Lua. This has led to numerous issues, such as poor parallelism, slow processing, unreliable dependencies, race conditions, clunky network support and broken unicode support.

Starting from version 2 onward, the core has been rewritten in Go to address all these issues. Many changes have been undertaken in the process:

  • Performance: Faster JSON (un)marshaling, faster checksums (no subprocess, partial checksums), better parallelization.
  • CLI flags: Reduced the number of flags and made them simpler. Some flags can be used more than once, which makes it possible to call scripts with spaces in their names.
  • Portability: Core detection now works on all OSes.
  • Race conditions: Remove all race conditions (cache, screen output, file access, temp files).
  • tags are no longer stripped away from input.streams[i].tags nor input.format.tags.
  • Cuesheet: Multi-file support.
  • Preview: Changes in path, format, parameters and covers are displayed using the same formatting than tags. Long lines are wrapped. Formatted preview is off when stdout is redirected, JSON formatting is used instead.
  • Full unicode support in scripts (string.len, string.gsub, etc.). Fix issues with [XY] regexps where X and Y are non-ASCII.
  • Scripts can be parameterized through global variables.
  • Config file is defined from the DEMLORC environment variable or default path, no more from a CLI flag. It greatly alleviates the complexity of flag parsing.
  • Sandboxes accept more functions from the Lua standard library, such as os.getenv.
  • input and output structures have changed. (See compatibility notice.)
  • Lua patterns have been replaced by Go regexps. (See compatibility notice.)
  • Extra audio streams and non-cover streams are stripped out.
  • Misc. fixes: Index creation for multi-track files, non-integer cover resolution, etc.
  • MusicBrainz: Clean queries, improved heuristic. The official artist name is used instead of the official album artist name. (It enforces homogeneity.)
  • The titlecase function has been moved to the script side and split into ’setcase’ and ’punctuation’.
  • Support for in-place tagging. This dramatically speeds up the process when the -rmsrc option is active.

Compatibility notice

User scripts still rely on input and output only. These structures have remained identical but for a few elements:

  • filename has been renamed to path.
  • output.bitrate has been replaced by the output.parameters array. Use it to pass arbitrary arguments to FFmpeg (codec, bitrate, etc.).
  • Cover options have been fully ravamped.

The Lua patterns of the string library have been replaced by Go regexps.



Date: 2014-11-12 (Last update: 2018-08-11)

Made with Emacs 27.2 (Org mode 9.4.4)

Creative Commons License