mirror of
https://github.com/beetbox/beets.git
synced 2025-12-15 21:14:19 +01:00
Merge branch 'master' of https://github.com/sampsyo/beets into bs1770gainsupport
This commit is contained in:
commit
efb7370e04
9 changed files with 56 additions and 40 deletions
|
|
@ -624,7 +624,7 @@ def cpu_count():
|
|||
num = 0
|
||||
elif sys.platform == b'darwin':
|
||||
try:
|
||||
num = int(command_output(['sysctl', '-n', 'hw.ncpu']))
|
||||
num = int(command_output([b'sysctl', b'-n', b'hw.ncpu']))
|
||||
except ValueError:
|
||||
num = 0
|
||||
else:
|
||||
|
|
@ -641,8 +641,8 @@ def cpu_count():
|
|||
def command_output(cmd, shell=False):
|
||||
"""Runs the command and returns its output after it has exited.
|
||||
|
||||
``cmd`` is a list of arguments starting with the command names. If
|
||||
``shell`` is true, ``cmd`` is assumed to be a string and passed to a
|
||||
``cmd`` is a list of byte string arguments starting with the command names.
|
||||
If ``shell`` is true, ``cmd`` is assumed to be a string and passed to a
|
||||
shell to execute.
|
||||
|
||||
If the process exits with a non-zero return code
|
||||
|
|
|
|||
|
|
@ -91,8 +91,8 @@ def im_resize(maxwidth, path_in, path_out=None):
|
|||
# compatibility.
|
||||
try:
|
||||
util.command_output([
|
||||
'convert', util.syspath(path_in),
|
||||
'-resize', '{0}x^>'.format(maxwidth), path_out
|
||||
b'convert', util.syspath(path_in),
|
||||
b'-resize', b'{0}x^>'.format(maxwidth), path_out
|
||||
])
|
||||
except subprocess.CalledProcessError:
|
||||
log.warn(u'artresizer: IM convert failed for {0}',
|
||||
|
|
@ -187,7 +187,7 @@ class ArtResizer(object):
|
|||
|
||||
# Try invoking ImageMagick's "convert".
|
||||
try:
|
||||
out = util.command_output(['identify', '--version'])
|
||||
out = util.command_output([b'identify', b'--version'])
|
||||
|
||||
if 'imagemagick' in out.lower():
|
||||
pattern = r".+ (\d+)\.(\d+)\.(\d+).*"
|
||||
|
|
|
|||
|
|
@ -192,7 +192,13 @@ class DiscogsPlugin(BeetsPlugin):
|
|||
# Strip medium information from query, Things like "CD1" and "disk 1"
|
||||
# can also negate an otherwise positive result.
|
||||
query = re.sub(r'(?i)\b(CD|disc)\s*\d+', '', query)
|
||||
releases = self.discogs_client.search(query, type='release').page(1)
|
||||
try:
|
||||
releases = self.discogs_client.search(query,
|
||||
type='release').page(1)
|
||||
except CONNECTION_ERRORS as exc:
|
||||
self._log.debug("Communication error while searching for {0!r}: "
|
||||
"{1}".format(query, exc))
|
||||
return []
|
||||
return [self.get_album_info(release) for release in releases[:5]]
|
||||
|
||||
def get_album_info(self, result):
|
||||
|
|
|
|||
|
|
@ -196,13 +196,13 @@ class EmbedCoverArtPlugin(BeetsPlugin):
|
|||
# Converting images to grayscale tends to minimize the weight
|
||||
# of colors in the diff score.
|
||||
convert_proc = subprocess.Popen(
|
||||
['convert', syspath(imagepath), syspath(art),
|
||||
'-colorspace', 'gray', 'MIFF:-'],
|
||||
[b'convert', syspath(imagepath), syspath(art),
|
||||
b'-colorspace', b'gray', b'MIFF:-'],
|
||||
stdout=subprocess.PIPE,
|
||||
close_fds=not is_windows,
|
||||
)
|
||||
compare_proc = subprocess.Popen(
|
||||
['compare', '-metric', 'PHASH', '-', 'null:'],
|
||||
[b'compare', b'-metric', b'PHASH', b'-', b'null:'],
|
||||
stdin=convert_proc.stdout,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
|
|
|
|||
|
|
@ -33,14 +33,12 @@ from beets import config
|
|||
# Utilities.
|
||||
|
||||
class ReplayGainError(Exception):
|
||||
|
||||
"""Raised when a local (to a track or an album) error occurs in one
|
||||
of the backends.
|
||||
"""
|
||||
|
||||
|
||||
class FatalReplayGainError(Exception):
|
||||
|
||||
"""Raised when a fatal error occurs in one of the backends.
|
||||
"""
|
||||
|
||||
|
|
@ -69,7 +67,6 @@ AlbumGain = collections.namedtuple("AlbumGain", "album_gain track_gains")
|
|||
|
||||
|
||||
class Backend(object):
|
||||
|
||||
"""An abstract class representing engine for calculating RG values.
|
||||
"""
|
||||
|
||||
|
|
@ -90,20 +87,18 @@ class Backend(object):
|
|||
|
||||
# bsg1770gain backend
|
||||
class Bs1770gainBackend(Backend):
|
||||
|
||||
"""bs1770gain is a loudness scanner compliant with ITU-R BS.1770 and its
|
||||
flavors EBU R128,ATSC A/85 and Replaygain 2.0. It uses a special
|
||||
designed algorithm to normalize audio to the same level.
|
||||
"""bs1770gain is a loudness scanner compliant with ITU-R BS.1770 and
|
||||
its flavors EBU R128, ATSC A/85 and Replaygain 2.0.
|
||||
"""
|
||||
|
||||
def __init__(self, config, log):
|
||||
super(Bs1770gainBackend, self).__init__(config, log)
|
||||
cmd = 'bs1770gain'
|
||||
cmd = b'bs1770gain'
|
||||
|
||||
try:
|
||||
self.method = '--' + config['method'].get(unicode)
|
||||
self.method = b'--' + config['method'].get(str)
|
||||
except:
|
||||
self.method = '--replaygain'
|
||||
self.method = b'--replaygain'
|
||||
|
||||
try:
|
||||
call([cmd, self.method])
|
||||
|
|
@ -215,9 +210,9 @@ class CommandBackend(Backend):
|
|||
)
|
||||
else:
|
||||
# Check whether the program is in $PATH.
|
||||
for cmd in ('mp3gain', 'aacgain'):
|
||||
for cmd in (b'mp3gain', b'aacgain'):
|
||||
try:
|
||||
call([cmd, '-v'])
|
||||
call([cmd, b'-v'])
|
||||
self.command = cmd
|
||||
except OSError:
|
||||
pass
|
||||
|
|
@ -281,14 +276,14 @@ class CommandBackend(Backend):
|
|||
# tag-writing; this turns the mp3gain/aacgain tool into a gain
|
||||
# calculator rather than a tag manipulator because we take care
|
||||
# of changing tags ourselves.
|
||||
cmd = [self.command, '-o', '-s', 's']
|
||||
cmd = [self.command, b'-o', b'-s', b's']
|
||||
if self.noclip:
|
||||
# Adjust to avoid clipping.
|
||||
cmd = cmd + ['-k']
|
||||
cmd = cmd + [b'-k']
|
||||
else:
|
||||
# Disable clipping warning.
|
||||
cmd = cmd + ['-c']
|
||||
cmd = cmd + ['-d', bytes(self.gain_offset)]
|
||||
cmd = cmd + [b'-c']
|
||||
cmd = cmd + [b'-d', bytes(self.gain_offset)]
|
||||
cmd = cmd + [syspath(i.path) for i in items]
|
||||
|
||||
self._log.debug(u'analyzing {0} files', len(items))
|
||||
|
|
@ -576,7 +571,6 @@ class GStreamerBackend(Backend):
|
|||
|
||||
|
||||
class AudioToolsBackend(Backend):
|
||||
|
||||
"""ReplayGain backend that uses `Python Audio Tools
|
||||
<http://audiotools.sourceforge.net/>`_ and its capabilities to read more
|
||||
file formats and compute ReplayGain values using it replaygain module.
|
||||
|
|
@ -706,7 +700,6 @@ class AudioToolsBackend(Backend):
|
|||
# Main plugin logic.
|
||||
|
||||
class ReplayGainPlugin(BeetsPlugin):
|
||||
|
||||
"""Provides ReplayGain analysis.
|
||||
"""
|
||||
|
||||
|
|
@ -802,7 +795,6 @@ class ReplayGainPlugin(BeetsPlugin):
|
|||
)
|
||||
|
||||
self.store_album_gain(album, album_gain.album_gain)
|
||||
|
||||
for item, track_gain in itertools.izip(album.items(),
|
||||
album_gain.track_gains):
|
||||
self.store_track_gain(item, track_gain)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ Changelog
|
|||
|
||||
Features:
|
||||
|
||||
* :doc:`/plugins/replaygain`: There is a new backend for the `bs1770gain`_
|
||||
tool. Thanks to :user:`jmwatte`. :bug:`1343`
|
||||
* There are now multiple levels of verbosity. On the command line, you can
|
||||
make beets somewhat verbose with ``-v`` or very verbose with ``-vv``.
|
||||
:bug:`1244`
|
||||
|
|
@ -129,6 +131,8 @@ For developers:
|
|||
immediately after they are initialized. It's also possible to replace the
|
||||
originally created tasks by returning new ones using this event.
|
||||
|
||||
.. _bs1770gain: http://bs1770gain.sourceforge.net
|
||||
|
||||
|
||||
1.3.10 (January 5, 2015)
|
||||
------------------------
|
||||
|
|
|
|||
|
|
@ -90,7 +90,8 @@ backend in your configuration file::
|
|||
|
||||
replaygain:
|
||||
backend: bs1770gain
|
||||
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
|
|
@ -118,11 +119,10 @@ These options only work with the "command" backend:
|
|||
|
||||
This option only works with the "bs1770gain" backend:
|
||||
|
||||
- **method**: The loudness scanning standard: either 'replaygain' for ReplayGain 2.0,
|
||||
'ebu' for EBU R128 or 'atsc' for ATSC A/85.
|
||||
This dictates the reference level: -18, -23, or -24 LUFS respectively. Default: replaygain
|
||||
|
||||
|
||||
- **method**: The loudness scanning standard: either `replaygain` for
|
||||
ReplayGain 2.0, `ebu` for EBU R128, or `atsc` for ATSC A/85. This dictates
|
||||
the reference level: -18, -23, or -24 LUFS respectively. Default:
|
||||
`replaygain`
|
||||
|
||||
|
||||
Manual Analysis
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ from beets.library import Library, Item, Album
|
|||
from beets import importer
|
||||
from beets.autotag.hooks import AlbumInfo, TrackInfo
|
||||
from beets.mediafile import MediaFile, Image
|
||||
from beets.ui import _encoding
|
||||
|
||||
# TODO Move AutotagMock here
|
||||
from test import _common
|
||||
|
|
@ -117,9 +118,13 @@ def capture_stdout():
|
|||
def has_program(cmd, args=['--version']):
|
||||
"""Returns `True` if `cmd` can be executed.
|
||||
"""
|
||||
full_cmd = [cmd] + args
|
||||
for i, elem in enumerate(full_cmd):
|
||||
if isinstance(elem, unicode):
|
||||
full_cmd[i] = elem.encode(_encoding())
|
||||
try:
|
||||
with open(os.devnull, 'wb') as devnull:
|
||||
subprocess.check_call([cmd] + args, stderr=devnull,
|
||||
subprocess.check_call(full_cmd, stderr=devnull,
|
||||
stdout=devnull, stdin=devnull)
|
||||
except OSError:
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ try:
|
|||
import gi
|
||||
gi.require_version('Gst', '1.0')
|
||||
GST_AVAILABLE = True
|
||||
except ImportError, ValueError:
|
||||
except (ImportError, ValueError):
|
||||
GST_AVAILABLE = False
|
||||
|
||||
if any(has_program(cmd, ['-v']) for cmd in ['mp3gain', 'aacgain']):
|
||||
|
|
@ -47,9 +47,18 @@ class ReplayGainCliTestBase(TestHelper):
|
|||
try:
|
||||
self.load_plugins('replaygain')
|
||||
except:
|
||||
self.teardown_beets()
|
||||
self.unload_plugins()
|
||||
raise
|
||||
import sys
|
||||
# store exception info so an error in teardown does not swallow it
|
||||
exc_info = sys.exc_info()
|
||||
try:
|
||||
self.teardown_beets()
|
||||
self.unload_plugins()
|
||||
except:
|
||||
# if load_plugins() failed then setup is incomplete and
|
||||
# teardown operations may fail. In particular # {Item,Album}
|
||||
# may not have the _original_types attribute in unload_plugins
|
||||
pass
|
||||
raise exc_info[1], None, exc_info[2]
|
||||
|
||||
self.config['replaygain']['backend'] = self.backend
|
||||
album = self.add_album_fixture(2)
|
||||
|
|
|
|||
Loading…
Reference in a new issue