From 3d842db8d8507828ec1e0f6f961ac3e710fea683 Mon Sep 17 00:00:00 2001 From: Samuel Nilsson Date: Wed, 6 Feb 2019 09:27:24 +0100 Subject: [PATCH] Added per disc album_gain support --- beetsplug/replaygain.py | 92 +++++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 31 deletions(-) diff --git a/beetsplug/replaygain.py b/beetsplug/replaygain.py index ac45aa4f8..868800d9b 100644 --- a/beetsplug/replaygain.py +++ b/beetsplug/replaygain.py @@ -83,7 +83,7 @@ class Backend(object): def compute_track_gain(self, items): raise NotImplementedError() - def compute_album_gain(self, album): + def compute_album_gain(self, items): # TODO: implement album gain in terms of track gain of the # individual tracks which can be used for any backend. raise NotImplementedError() @@ -125,15 +125,14 @@ class Bs1770gainBackend(Backend): output = self.compute_gain(items, False) return output - def compute_album_gain(self, album): + def compute_album_gain(self, items): """Computes the album gain of the given album, returns an AlbumGain object. """ # TODO: What should be done when not all tracks in the album are # supported? - supported_items = album.items() - output = self.compute_gain(supported_items, True) + output = self.compute_gain(items, True) if not output: raise ReplayGainError(u'no output from bs1770gain') @@ -316,15 +315,15 @@ class CommandBackend(Backend): output = self.compute_gain(supported_items, False) return output - def compute_album_gain(self, album): + def compute_album_gain(self, items): """Computes the album gain of the given album, returns an AlbumGain object. """ # TODO: What should be done when not all tracks in the album are # supported? - supported_items = list(filter(self.format_supported, album.items())) - if len(supported_items) != len(album.items()): + supported_items = list(filter(self.format_supported, items)) + if len(supported_items) != len(items): self._log.debug(u'tracks are of unsupported format') return AlbumGain(None, []) @@ -521,8 +520,8 @@ class GStreamerBackend(Backend): return ret - def compute_album_gain(self, album): - items = list(album.items()) + def compute_album_gain(self, items): + items = list(items) self.compute(items, True) if len(self._file_tags) != len(items): raise ReplayGainError(u"Some items in album did not receive tags") @@ -777,22 +776,20 @@ class AudioToolsBackend(Backend): item.artist, item.title, rg_track_gain, rg_track_peak) return Gain(gain=rg_track_gain, peak=rg_track_peak) - def compute_album_gain(self, album): + def compute_album_gain(self, items): """Compute ReplayGain values for the requested album and its items. :rtype: :class:`AlbumGain` """ - self._log.debug(u'Analysing album {0}', album) - # The first item is taken and opened to get the sample rate to # initialize the replaygain object. The object is used for all the # tracks in the album to get the album values. - item = list(album.items())[0] + item = list(items)[0] audiofile = self.open_audio_file(item) rg = self.init_replaygain(audiofile, item) track_gains = [] - for item in album.items(): + for item in items: audiofile = self.open_audio_file(item) rg_track_gain, rg_track_peak = self._title_gain(rg, audiofile) track_gains.append( @@ -836,9 +833,11 @@ class ReplayGainPlugin(BeetsPlugin): 'backend': u'command', 'targetlevel': 89, 'r128': ['Opus'], + 'per_disc': False }) self.overwrite = self.config['overwrite'].get(bool) + self.per_disc = self.config['per_disc'].get(bool) backend_name = self.config['backend'].as_str() if backend_name not in self.backends: raise ui.UserError( @@ -919,6 +918,21 @@ class ReplayGainPlugin(BeetsPlugin): self._log.debug(u'applied r128 album gain {0}', album.r128_album_gain) + def store_album_gain_item_level(self, items, album_gain): + for item in items: + item.rg_album_gain = album_gain.gain + item.rg_album_peak = album_gain.peak + item.store() + self._log.debug(u'applied album gain {0}, peak {1}', + item.rg_album_gain, item.rg_album_peak) + + def store_album_r128_gain_item_level(self, items, album_gain): + for item in items: + item.r128_album_gain = album_gain.gain + item.store() + self._log.debug(u'applied r128 album gain {0}', + item.r128_album_gain) + def handle_album(self, album, write, force=False): """Compute album and track replay gain store it in all of the album's items. @@ -946,29 +960,45 @@ class ReplayGainPlugin(BeetsPlugin): backend_instance = self.r128_backend_instance store_track_gain = self.store_track_r128_gain store_album_gain = self.store_album_r128_gain + store_album_gain_item_level = self.store_album_r128_gain_item_level else: backend_instance = self.backend_instance store_track_gain = self.store_track_gain store_album_gain = self.store_album_gain + store_album_gain_item_level = self.store_album_gain_item_level - try: - album_gain = backend_instance.compute_album_gain(album) - if len(album_gain.track_gains) != len(album.items()): - raise ReplayGainError( - u"ReplayGain backend failed " - u"for some tracks in album {0}".format(album) - ) + discs = dict() + if self.per_disc: + for item in album.items(): + if discs.get(item.disc) is None: + discs[item.disc] = [] + discs[item.disc].append(item) + else: + discs[1] = album.items() - store_album_gain(album, album_gain.album_gain) - for item, track_gain in zip(album.items(), album_gain.track_gains): - store_track_gain(item, track_gain) - if write: - item.try_write() - except ReplayGainError as e: - self._log.info(u"ReplayGain error: {0}", e) - except FatalReplayGainError as e: - raise ui.UserError( - u"Fatal replay gain error: {0}".format(e)) + for discnumber in discs: + try: + items = discs[discnumber] + album_gain = backend_instance.compute_album_gain(items) + if len(album_gain.track_gains) != len(items): + raise ReplayGainError( + u"ReplayGain backend failed " + u"for some tracks in album {0}".format(album) + ) + + if len(items) == len(album.items()): + store_album_gain(album, album_gain.album_gain) + else: + store_album_gain_item_level(items, album_gain.album_gain) + for item, track_gain in zip(items, album_gain.track_gains): + store_track_gain(item, track_gain) + if write: + item.try_write() + except ReplayGainError as e: + self._log.info(u"ReplayGain error: {0}", e) + except FatalReplayGainError as e: + raise ui.UserError( + u"Fatal replay gain error: {0}".format(e)) def handle_track(self, item, write, force=False): """Compute track replay gain and store it in the item.