Music Player Daemon Community Wiki

522pages on
this wiki
Add New Page
Talk4 Share

A script to convert APEv2 ReplayGain tags, such as those written by mp3gain and foobar2000, to ID3v2 ReplayGain tags which MPD can read. Also see does not support recursing through subdirectories. If you would like to process all of the MP3 files in, for example, ~/music, use find:

find ~/music -type f -iname '*.mp3' -print0 | xargs -0 mp3gain 
find ~/music -type f -iname '*.mp3' -exec -df {} \;

An alternate bash wrapper script for this process which is able to continue even if mp3gain chokes on a file is mp3gainhelper.shon GitHub.

Requires Mutagen. In Ubuntu run "sudo apt-get install python-mutagen"

NOTE: If your mp3gain writes out tags in all uppercase as it does in Ubuntu Hardy, you'll need this version instead.

#! /usr/bin/env python 
import sys 
from optparse import OptionParser 
import mutagen 
from mutagen.apev2 import APEv2 
from mutagen.id3 import ID3, TXXX 
def convert_gain(gain): 
   if gain[-3:] == " dB": 
       gain = gain[:-3] 
       gain = float(gain) 
   except ValueError: 
       raise ValueError, "invalid gain value" 
   return "%.2f dB" % gain 
def convert_peak(peak): 
       peak = float(peak) 
   except ValueError: 
       raise ValueError, "invalid peak value" 
   return "%.6f" % peak 
   ("mp3gain_album_minmax", None), 
   ("mp3gain_minmax", None), 
   ("replaygain_album_gain", convert_gain), 
   ("replaygain_album_peak", convert_peak), 
   ("replaygain_track_gain", convert_gain), 
   ("replaygain_track_peak", convert_peak), 
class Logger(object): 
   def __init__(self, log_level, prog_name): 
       self.log_level = log_level 
       self.prog_name = prog_name 
       self.filename = None 
   def prefix(self, msg): 
       if self.filename is None: 
           return msg 
       return "%s: %s" % (self.filename, msg) 
   def debug(self, msg): 
       if self.log_level >= 4: 
           print self.prefix(msg) 
   def info(self, msg): 
       if self.log_level >= 3: 
           print self.prefix(msg) 
   def warning(self, msg): 
       if self.log_level >= 2: 
           print self.prefix("WARNING: %s" % msg) 
   def error(self, msg): 
       if self.log_level >= 1: 
           sys.stderr.write("%s: %s\n" % (self.prog_name, msg)) 
   def critical(self, msg, retval=1): 
class Ape2Id3(object): 
   def __init__(self, logger, force=False): 
       self.log = logger 
       self.force = force 
   def convert_tag(self, name, value): 
   def copy_replaygain_tag(self, apev2, id3, name, converter=None): 
       self.log.debug("processing '%s' tag" % name) 
       if name not in apev2: 
 "no APEv2 '%s' tag found, skipping tag" % name) 
           return False 
       if not self.force and ("TXXX:%s" % name) in id3: 
 "ID3 '%s' tag already exists, skpping tag" % name) 
           return False 
       value = str(apev2[name]) 
       if callable(converter): 
           self.log.debug("converting APEv2 '%s' tag from '%s'" % 
                          (name, value)) 
               value = converter(value) 
           except ValueError: 
               self.log.warning("invalid value for APEv2 '%s' tag" % name) 
               return False 
           self.log.debug("converted APEv2 '%s' tag to '%s'" % (name, value)) 
       id3.add(TXXX(encoding=1, desc=name, text=value))"added ID3 '%s' tag with value '%s'" % (name, value)) 
       return True 
   def copy_replaygain_tags(self, filename): 
       self.log.filename = filename 
       self.log.debug("begin processing file") 
           apev2 = APEv2(filename) 
       except mutagen.apev2.error: 
 "no APEv2 tag found, skipping file") 
       except IOError: 
           e = sys.exc_info() 
           self.log.error("%s" % e[1]) 
           id3 = ID3(filename) 
       except mutagen.id3.error: 
 "no ID3 tag found, creating one") 
           id3 = ID3() 
       modified = False 
       for name, converter in REPLAYGAIN_TAGS: 
           copied = self.copy_replaygain_tag(apev2, id3, name, converter) 
           if copied: 
               modified = True 
       if modified: 
           self.log.debug("saving modified ID3 tag") 
       self.log.debug("done processing file") 
       self.log.filename = None 
def main(prog_name, options, args): 
   logger = Logger(options.log_level, prog_name) 
   ape2id3 = Ape2Id3(logger, force=options.force) 
   for filename in args: 
if __name__ == "__main__": 
   parser = OptionParser(version="0.1", usage="%prog [OPTION]... FILE...", 
                         description="Copy APEv2 ReplayGain tags on " 
                                     "FILE(s) to ID3v2.") 
   parser.add_option("-q", "--quiet", dest="log_level", 
                     action="store_const", const=0, default=1, 
                     help="do not output error messages") 
   parser.add_option("-v", "--verbose", dest="log_level", 
                     action="store_const", const=3, 
                     help="output warnings and informational messages") 
   parser.add_option("-d", "--debug", dest="log_level", 
                     action="store_const", const=4, 
                     help="output debug messages") 
   parser.add_option("-f", "--force", dest="force", 
                     action="store_true", default=False, 
                     help="force overwriting of existing ID3v2 " 
                          "ReplayGain tags") 
   prog_name = parser.get_prog_name() 
   options, args = parser.parse_args() 
   if len(args) < 1: 
       parser.error("no files specified") 
       main(prog_name, options, args) 
   except KeyboardInterrupt: 
# vim: set expandtab shiftwidth=4 softtabstop=4 textwidth=79:

Ad blocker interference detected!

Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.