a2ee5c6
# -*- coding: utf-8 -*-
a2ee5c6
a2ee5c6
# Changelog:
a2ee5c6
#   [2008-03-14] Initial version with support for Ogg Vorbis, FLAC and MP3
a2ee5c6
a2ee5c6
PLUGIN_NAME = u"ReplayGain"
a2ee5c6
PLUGIN_AUTHOR = u"Philipp Wolfer"
a2ee5c6
PLUGIN_DESCRIPTION = """Calculate ReplayGain for selected files and albums."""
a2ee5c6
PLUGIN_VERSION = "0.1"
a2ee5c6
PLUGIN_API_VERSIONS = ["0.10", "0.15"]
a2ee5c6
a2ee5c6
a2ee5c6
from PyQt4 import QtCore
a2ee5c6
from subprocess import check_call
a2ee5c6
from picard.album import Album
a2ee5c6
from picard.track import Track
a2ee5c6
from picard.file import File
a2ee5c6
from picard.util import encode_filename, decode_filename, partial
a2ee5c6
from picard.ui.options import register_options_page, OptionsPage
a2ee5c6
from picard.config import BoolOption, IntOption, TextOption
a2ee5c6
from picard.ui.itemviews import (BaseAction, register_file_action,
a2ee5c6
                                 register_album_action)
a2ee5c6
from picard.plugins.replaygain.ui_options_replaygain import Ui_ReplayGainOptionsPage
a2ee5c6
a2ee5c6
# Path to various replay gain tools. There must be a tool for every supported
a2ee5c6
# audio file format.
a2ee5c6
REPLAYGAIN_COMMANDS = {
a2ee5c6
   "Ogg Vorbis": ("replaygain_vorbisgain_command", "replaygain_vorbisgain_options"),
a2ee5c6
   "MPEG-1 Audio": ("replaygain_mp3gain_command", "replaygain_mp3gain_options"),
a2ee5c6
   "FLAC": ("replaygain_metaflac_command", "replaygain_metaflac_options"),
a2ee5c6
   }
a2ee5c6
a2ee5c6
def calculate_replay_gain_for_files(files, format, tagger):
a2ee5c6
    """Calculates the replay gain for a list of files in album mode."""
a2ee5c6
    file_list = ['%s' % encode_filename(f.filename) for f in files]
a2ee5c6
    
a2ee5c6
    if REPLAYGAIN_COMMANDS.has_key(format) \
a2ee5c6
        and tagger.config.setting[REPLAYGAIN_COMMANDS[format][0]]:
a2ee5c6
        command = tagger.config.setting[REPLAYGAIN_COMMANDS[format][0]]
a2ee5c6
        options = tagger.config.setting[REPLAYGAIN_COMMANDS[format][1]].split(' ')
a2ee5c6
        tagger.log.debug('%s %s %s' % (command, ' '.join(options), decode_filename(' '.join(file_list))))
a2ee5c6
        check_call([command] + options + file_list)
a2ee5c6
    else:
a2ee5c6
        raise Exception, 'ReplayGain: Unsupported format %s' % (format)
a2ee5c6
a2ee5c6
class ReplayGain(BaseAction):
a2ee5c6
    NAME = N_("Calculate replay &gain...")
a2ee5c6
    
a2ee5c6
    def _add_file_to_queue(self, file):
a2ee5c6
        self.tagger.other_queue.put((
a2ee5c6
            partial(self._calculate_replaygain, file),
a2ee5c6
            partial(self._replaygain_callback, file),
a2ee5c6
            QtCore.Qt.NormalEventPriority))
a2ee5c6
a2ee5c6
    def callback(self, objs):
a2ee5c6
        for obj in objs:
a2ee5c6
            if isinstance(obj, Track):
a2ee5c6
                for files in obj.linked_files:
a2ee5c6
                    self._add_file_to_queue(file)
a2ee5c6
            elif isinstance(obj, File):
a2ee5c6
                self._add_file_to_queue(obj)
a2ee5c6
a2ee5c6
    def _calculate_replaygain(self, file):
a2ee5c6
        self.tagger.window.set_statusbar_message(N_('Calculating replay gain for "%s"...'), file.filename)
a2ee5c6
        calculate_replay_gain_for_files([file], file.NAME, self.tagger)
a2ee5c6
    
a2ee5c6
    def _replaygain_callback(self, file, result=None, error=None):
a2ee5c6
        if not error:
a2ee5c6
            self.tagger.window.set_statusbar_message(N_('Replay gain for "%s" successfully calculated.'), file.filename)
a2ee5c6
        else:
a2ee5c6
            self.tagger.window.set_statusbar_message(N_('Could not calculate replay gain for "%s".'), file.filename)
a2ee5c6
a2ee5c6
class AlbumGain(BaseAction):
a2ee5c6
    NAME = N_("Calculate album &gain...")
a2ee5c6
    
a2ee5c6
    def callback(self, objs):
a2ee5c6
        albums = [o for o in objs if isinstance(o, Album)]
a2ee5c6
        for album in albums:
a2ee5c6
            self.tagger.other_queue.put((
a2ee5c6
                partial(self._calculate_albumgain, album),
a2ee5c6
                partial(self._albumgain_callback, album),
a2ee5c6
                QtCore.Qt.NormalEventPriority))
a2ee5c6
 
a2ee5c6
    def split_files_by_type(self, files):
a2ee5c6
        """Split the given files by filetype into separate lists."""
a2ee5c6
        files_by_format = {}
a2ee5c6
        
a2ee5c6
        for file in files:
a2ee5c6
            if not files_by_format.has_key(file.NAME):
a2ee5c6
                files_by_format[file.NAME] = [file]
a2ee5c6
            else:
a2ee5c6
                files_by_format[file.NAME].append(file)
a2ee5c6
        
a2ee5c6
        return files_by_format
a2ee5c6
    
a2ee5c6
    def _calculate_albumgain(self, album):
a2ee5c6
        self.tagger.window.set_statusbar_message(N_('Calculating album gain for "%s"...'), album.metadata["album"])
a2ee5c6
        filelist = [t.linked_files[0] for t in album.tracks if t.is_linked()]
a2ee5c6
        
a2ee5c6
        for format, files in self.split_files_by_type(filelist).iteritems():
a2ee5c6
            calculate_replay_gain_for_files(files, format, self.tagger)
a2ee5c6
    
a2ee5c6
    def _albumgain_callback(self, album, result=None, error=None):
a2ee5c6
        if not error:
a2ee5c6
            self.tagger.window.set_statusbar_message(N_('Album gain for "%s" successfully calculated.'), album.metadata["album"])
a2ee5c6
        else:
a2ee5c6
            self.tagger.window.set_statusbar_message(N_('Could not calculate album gain for "%s".'), album.metadata["album"])
a2ee5c6
 
a2ee5c6
class ReplayGainOptionsPage(OptionsPage):
a2ee5c6
a2ee5c6
    NAME = "replaygain"
a2ee5c6
    TITLE = "ReplayGain"
a2ee5c6
    PARENT = "plugins"
a2ee5c6
a2ee5c6
    options = [
a2ee5c6
        TextOption("setting", "replaygain_vorbisgain_command", "vorbisgain"),
a2ee5c6
        TextOption("setting", "replaygain_vorbisgain_options", "-asf"),
a2ee5c6
        TextOption("setting", "replaygain_mp3gain_command", "mp3gain"),
a2ee5c6
        TextOption("setting", "replaygain_mp3gain_options", "-a"),
a2ee5c6
        TextOption("setting", "replaygain_metaflac_command", "metaflac"),
a2ee5c6
        TextOption("setting", "replaygain_metaflac_options", "--add-replay-gain"),
a2ee5c6
    ]
a2ee5c6
a2ee5c6
    def __init__(self, parent=None):
a2ee5c6
        super(ReplayGainOptionsPage, self).__init__(parent)
a2ee5c6
        self.ui = Ui_ReplayGainOptionsPage()
a2ee5c6
        self.ui.setupUi(self)
a2ee5c6
a2ee5c6
    def load(self):
a2ee5c6
        self.ui.vorbisgain_command.setText(self.config.setting["replaygain_vorbisgain_command"])
a2ee5c6
        self.ui.mp3gain_command.setText(self.config.setting["replaygain_mp3gain_command"])
a2ee5c6
        self.ui.metaflac_command.setText(self.config.setting["replaygain_metaflac_command"])
a2ee5c6
    
a2ee5c6
    def save(self):
a2ee5c6
        self.config.setting["replaygain_vorbisgain_command"] = unicode(self.ui.vorbisgain_command.text())
a2ee5c6
        self.config.setting["replaygain_mp3gain_command"] = unicode(self.ui.mp3gain_command.text())
a2ee5c6
        self.config.setting["replaygain_metaflac_command"] = unicode(self.ui.metaflac_command.text())
a2ee5c6
       
a2ee5c6
register_file_action(ReplayGain())
a2ee5c6
register_album_action(AlbumGain())
a2ee5c6
register_options_page(ReplayGainOptionsPage)