Skip to content

Wrapping flac to set a default compression level

I'm using Whipper to rip CDs into FLAC files on the command-line. It comes with support for MusicBrainz and focuses on bit-perfect archival of the original audio data.

However, what it doesn't come with is a lot of configuration options, and choosing a compression level for your FLAC files (or customizing the CLI arguments passed to the flac command) is not something you can easily do. Which is why I wrote a custom wrapper script in Python to get around this.


Sure, the easy way would be to add a new feature to Whipper and introduce a configuration setting. However, the Whipper team hasn't been too enthusiastic about adding a setting or flag for this and is instead in favor of enabling users to configure external executable options in a more generic way, but that issue is open for almost 8 years now.

And the flac command isn't helping either: It only uses command-line arguments and supports neither a configuration file nor environment variables for setting defaults. Its default compression level is 5; if you want anything else (between 0 and 8), you'll have to use one of the command line switches between -0 (aka --fast) and -8 (aka --best).

But, since I have a ~/bin directory in my $PATH, with higher priority than /usr/bin, I can just create a flac script there and it will be called instead of the actual flac command. Which is exactly what I did.

The idea is to check whether the caller has explicitly requested a certain compression level on the command line, and if they didn't, we're injecting -8 into the arguments and call the original flac command with that.

I'm using Python, because I need to be able to understand both short options with a single dash, and long options with a double dash, and it feels like less of a hack to do this in Python than in a shell script. Here's the script I'm using, which you can also find in my dotfiles:

#!/usr/bin/env python3

import os
import sys
import syslog
from argparse import ArgumentParser


# Create an ArgumentParser that doesn't claim -h/--help.
parser = ArgumentParser(add_help=False)

# Parse all of the compression level options.
for level in range(9):
    argnames = [f"-{level}", f"--compression-level-{level}"]

    # Additional aliases for -0 and -8.
    if level == 0:
        argnames.append("--fast")
    elif level == 8:
        argnames.append("--best")

    # Store the requested level in the "level" argument.
    parser.add_argument(*argnames, dest="level", action="store_const", const=level)

# Parse the compression level.
args, _rest = parser.parse_known_args()

# Create new argv by prepending -8 if no compression level is set.
argv = (["-8"] + sys.argv[1:]) if args.level is None else sys.argv[1:]

# Log what we're doing. Can be viewed with:
#    journalctl --user -t flac-wrapper
syslog.openlog(ident="flac-wrapper", facility=syslog.LOG_USER)
syslog.syslog(syslog.LOG_INFO, "will call flac with: " + " ".join(argv))
syslog.closelog()

# exec() the real flac.
os.execv("/usr/bin/flac", argv)

The key things here are:

The script can possibly still be fooled somewhat because it doesn't know which flac options expect a value. I would expect a call like flac -o --fast input.wav to be interpreted like "write the output to a file named --fast", but that's actually not what flac is doing, so … I don't know, maybe there's no problem after all.

At some point in the future I'd still like to have that as a native feature in Whipper, just like ReplayGain integration … a guy can dream.