|
|
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)
|