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:
- use
add_help=Falseto keep Python from hijacking the-hand--helparguments - use
parse_known_args()so that it doesn't complain about unknown arguments - use
argv[1:]becauseargv[0]is the script name - log to the systemd journal via simple
syslogcalls (which is supported by journald out of the box)
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.