Blob Blame History Raw
# -*- coding: utf-8 -*-

# Changelog:
#   [2008-03-14] Initial version with support for Ogg Vorbis, FLAC and MP3

PLUGIN_NAME = u"ReplayGain"
PLUGIN_AUTHOR = u"Philipp Wolfer"
PLUGIN_DESCRIPTION = """Calculate ReplayGain for selected files and albums."""
PLUGIN_VERSION = "0.1"
PLUGIN_API_VERSIONS = ["0.10", "0.15"]


from PyQt4 import QtCore
from subprocess import check_call
from picard.album import Album
from picard.track import Track
from picard.file import File
from picard.util import encode_filename, decode_filename, partial
from picard.ui.options import register_options_page, OptionsPage
from picard.config import BoolOption, IntOption, TextOption
from picard.ui.itemviews import (BaseAction, register_file_action,
                                 register_album_action)
from picard.plugins.replaygain.ui_options_replaygain import Ui_ReplayGainOptionsPage

# Path to various replay gain tools. There must be a tool for every supported
# audio file format.
REPLAYGAIN_COMMANDS = {
   "Ogg Vorbis": ("replaygain_vorbisgain_command", "replaygain_vorbisgain_options"),
   "MPEG-1 Audio": ("replaygain_mp3gain_command", "replaygain_mp3gain_options"),
   "FLAC": ("replaygain_metaflac_command", "replaygain_metaflac_options"),
   }

def calculate_replay_gain_for_files(files, format, tagger):
    """Calculates the replay gain for a list of files in album mode."""
    file_list = ['%s' % encode_filename(f.filename) for f in files]
    
    if REPLAYGAIN_COMMANDS.has_key(format) \
        and tagger.config.setting[REPLAYGAIN_COMMANDS[format][0]]:
        command = tagger.config.setting[REPLAYGAIN_COMMANDS[format][0]]
        options = tagger.config.setting[REPLAYGAIN_COMMANDS[format][1]].split(' ')
        tagger.log.debug('%s %s %s' % (command, ' '.join(options), decode_filename(' '.join(file_list))))
        check_call([command] + options + file_list)
    else:
        raise Exception, 'ReplayGain: Unsupported format %s' % (format)

class ReplayGain(BaseAction):
    NAME = N_("Calculate replay &gain...")
    
    def _add_file_to_queue(self, file):
        self.tagger.other_queue.put((
            partial(self._calculate_replaygain, file),
            partial(self._replaygain_callback, file),
            QtCore.Qt.NormalEventPriority))

    def callback(self, objs):
        for obj in objs:
            if isinstance(obj, Track):
                for files in obj.linked_files:
                    self._add_file_to_queue(file)
            elif isinstance(obj, File):
                self._add_file_to_queue(obj)

    def _calculate_replaygain(self, file):
        self.tagger.window.set_statusbar_message(N_('Calculating replay gain for "%s"...'), file.filename)
        calculate_replay_gain_for_files([file], file.NAME, self.tagger)
    
    def _replaygain_callback(self, file, result=None, error=None):
        if not error:
            self.tagger.window.set_statusbar_message(N_('Replay gain for "%s" successfully calculated.'), file.filename)
        else:
            self.tagger.window.set_statusbar_message(N_('Could not calculate replay gain for "%s".'), file.filename)

class AlbumGain(BaseAction):
    NAME = N_("Calculate album &gain...")
    
    def callback(self, objs):
        albums = [o for o in objs if isinstance(o, Album)]
        for album in albums:
            self.tagger.other_queue.put((
                partial(self._calculate_albumgain, album),
                partial(self._albumgain_callback, album),
                QtCore.Qt.NormalEventPriority))
 
    def split_files_by_type(self, files):
        """Split the given files by filetype into separate lists."""
        files_by_format = {}
        
        for file in files:
            if not files_by_format.has_key(file.NAME):
                files_by_format[file.NAME] = [file]
            else:
                files_by_format[file.NAME].append(file)
        
        return files_by_format
    
    def _calculate_albumgain(self, album):
        self.tagger.window.set_statusbar_message(N_('Calculating album gain for "%s"...'), album.metadata["album"])
        filelist = [t.linked_files[0] for t in album.tracks if t.is_linked()]
        
        for format, files in self.split_files_by_type(filelist).iteritems():
            calculate_replay_gain_for_files(files, format, self.tagger)
    
    def _albumgain_callback(self, album, result=None, error=None):
        if not error:
            self.tagger.window.set_statusbar_message(N_('Album gain for "%s" successfully calculated.'), album.metadata["album"])
        else:
            self.tagger.window.set_statusbar_message(N_('Could not calculate album gain for "%s".'), album.metadata["album"])
 
class ReplayGainOptionsPage(OptionsPage):

    NAME = "replaygain"
    TITLE = "ReplayGain"
    PARENT = "plugins"

    options = [
        TextOption("setting", "replaygain_vorbisgain_command", "vorbisgain"),
        TextOption("setting", "replaygain_vorbisgain_options", "-asf"),
        TextOption("setting", "replaygain_mp3gain_command", "mp3gain"),
        TextOption("setting", "replaygain_mp3gain_options", "-a"),
        TextOption("setting", "replaygain_metaflac_command", "metaflac"),
        TextOption("setting", "replaygain_metaflac_options", "--add-replay-gain"),
    ]

    def __init__(self, parent=None):
        super(ReplayGainOptionsPage, self).__init__(parent)
        self.ui = Ui_ReplayGainOptionsPage()
        self.ui.setupUi(self)

    def load(self):
        self.ui.vorbisgain_command.setText(self.config.setting["replaygain_vorbisgain_command"])
        self.ui.mp3gain_command.setText(self.config.setting["replaygain_mp3gain_command"])
        self.ui.metaflac_command.setText(self.config.setting["replaygain_metaflac_command"])
    
    def save(self):
        self.config.setting["replaygain_vorbisgain_command"] = unicode(self.ui.vorbisgain_command.text())
        self.config.setting["replaygain_mp3gain_command"] = unicode(self.ui.mp3gain_command.text())
        self.config.setting["replaygain_metaflac_command"] = unicode(self.ui.metaflac_command.text())
       
register_file_action(ReplayGain())
register_album_action(AlbumGain())
register_options_page(ReplayGainOptionsPage)