Last active
August 14, 2016 12:08
-
-
Save uriel1998/6333da780d44e59abbc1761700104329 to your computer and use it in GitHub Desktop.
A python script to convert APEv2 ReplayGain tags, such as those written by mp3gain and foobar2000, to ID3v2 ReplayGain tags which MPD can read. ape2id3.py does not support recursing through subdirectories. Requires Mutagen. In Ubuntu run "sudo apt-get install python-mutagen". From http://mpd.wikia.com/wiki/Hack:ape2id3.py , no attribution given.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /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] | |
try: | |
gain = float(gain) | |
except ValueError: | |
raise ValueError, "invalid gain value" | |
return "%.2f dB" % gain | |
def convert_peak(peak): | |
try: | |
peak = float(peak) | |
except ValueError: | |
raise ValueError, "invalid peak value" | |
return "%.6f" % peak | |
REPLAYGAIN_TAGS = ( | |
("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): | |
self.error(msg) | |
sys.exit(retval) | |
class Ape2Id3(object): | |
def __init__(self, logger, force=False): | |
self.log = logger | |
self.force = force | |
def convert_tag(self, name, value): | |
pass | |
def copy_replaygain_tag(self, apev2, id3, name, converter=None): | |
self.log.debug("processing '%s' tag" % name) | |
if name not in apev2: | |
self.log.info("no APEv2 '%s' tag found, skipping tag" % name) | |
return False | |
if not self.force and ("TXXX:%s" % name) in id3: | |
self.log.info("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)) | |
try: | |
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)) | |
self.log.info("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") | |
try: | |
apev2 = APEv2(filename) | |
except mutagen.apev2.error: | |
self.log.info("no APEv2 tag found, skipping file") | |
return | |
except IOError: | |
e = sys.exc_info() | |
self.log.error("%s" % e[1]) | |
return | |
try: | |
id3 = ID3(filename) | |
except mutagen.id3.error: | |
self.log.info("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") | |
id3.save(filename) | |
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: | |
ape2id3.copy_replaygain_tags(filename) | |
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") | |
try: | |
main(prog_name, options, args) | |
except KeyboardInterrupt: | |
pass | |
# vim: set expandtab shiftwidth=4 softtabstop=4 textwidth=79: |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment