Updating to master to form a new PR.

This commit is contained in:
kerobaros 2014-10-24 21:33:09 -05:00
commit d06da6381f
12 changed files with 123 additions and 17 deletions

View file

@ -310,9 +310,9 @@ def match_album(artist, album, tracks=None, limit=SEARCH_LIMIT):
optionally, a number of tracks on the album.
"""
# Build search criteria.
criteria = {'release': album.lower()}
criteria = {'release': album.lower().strip()}
if artist is not None:
criteria['artist'] = artist.lower()
criteria['artist'] = artist.lower().strip()
else:
# Various Artists search.
criteria['arid'] = VARIOUS_ARTISTS_ID
@ -341,8 +341,8 @@ def match_track(artist, title, limit=SEARCH_LIMIT):
objects. May raise a MusicBrainzAPIError.
"""
criteria = {
'artist': artist.lower(),
'recording': title.lower(),
'artist': artist.lower().strip(),
'recording': title.lower().strip(),
}
if not any(criteria.itervalues()):

View file

@ -41,7 +41,7 @@ class EmbedCoverArtPlugin(BeetsPlugin):
'maxwidth': 0,
'auto': True,
'compare_threshold': 0,
'ifempty': False
'ifempty': False,
})
if self.config['maxwidth'].get(int) and not ArtResizer.shared.local:
@ -64,7 +64,7 @@ class EmbedCoverArtPlugin(BeetsPlugin):
)
maxwidth = config['embedart']['maxwidth'].get(int)
compare_threshold = config['embedart']['compare_threshold'].get(int)
ifempty = config['embedart']['ifempty'].get()
ifempty = config['embedart']['ifempty'].get(bool)
def embed_func(lib, opts, args):
if opts.file:
@ -165,6 +165,7 @@ def embed_album(album, maxwidth=None, quiet=False):
for item in album.items():
embed_item(item, imagepath, maxwidth, None,
config['embedart']['compare_threshold'].get(int),
<<<<<<< HEAD
config['embedart']['ifempty'], asalbum=True)
@ -175,6 +176,9 @@ def resize_image(imagepath, maxwidth):
.format(maxwidth))
imagepath = ArtResizer.shared.resize(maxwidth, syspath(imagepath))
return imagepath
=======
config['embedart']['ifempty'].get(bool))
>>>>>>> upstream/master
def check_art_similarity(item, imagepath, compare_threshold):

View file

@ -1,5 +1,5 @@
# This file is part of beets.
# Copyright 2013, Adrian Sampson.
# Copyright 2014, Adrian Sampson.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the

View file

@ -18,8 +18,11 @@ from beets.plugins import BeetsPlugin
from beets import ui
from beets.util import displayable_path
from beets import config
import logging
import re
log = logging.getLogger('beets')
def split_on_feat(artist):
"""Given an artist string, split the "main" artist from any artist
@ -69,7 +72,7 @@ def update_metadata(item, feat_part, drop_feat):
item.title = new_title
def ft_in_title(item, drop_feat):
def ft_in_title(item, drop_feat, write):
"""Look for featured artists in the item's artist fields and move
them to the title.
"""
@ -118,6 +121,7 @@ class FtInTitlePlugin(BeetsPlugin):
super(FtInTitlePlugin, self).__init__()
self.config.add({
'auto': True,
'drop': False
})
@ -130,6 +134,9 @@ class FtInTitlePlugin(BeetsPlugin):
action='store_true', default=False,
help='drop featuring from artists and ignore title update')
if self.config['auto']:
self.import_stages = [self.imported]
def commands(self):
def func(lib, opts, args):
@ -145,3 +152,11 @@ class FtInTitlePlugin(BeetsPlugin):
self._command.func = func
return [self._command]
def imported(self, session, task):
"""Import hook for moving featuring artist automatically.
"""
drop_feat = self.config['drop'].get(bool)
for item in task.imported_items():
ft_in_title(item, drop_feat)

View file

@ -329,10 +329,10 @@ def _scrape_strip_cruft(html, plain_text_out=False):
"""
html = unescape(html)
# Normalize EOL
html = html.replace('\r', '\n')
html = html.replace('\r', '\n') # Normalize EOL.
html = re.sub(r' +', ' ', html) # Whitespaces collapse.
html = BREAK_RE.sub('\n', html) # <br> eats up surrounding '\n'
html = BREAK_RE.sub('\n', html) # <br> eats up surrounding '\n'.
html = re.sub(r'<(script).*?</\1>(?s)', '', html) # Strip script tags.
if plain_text_out: # Strip remaining HTML tags
html = TAG_RE.sub('', html)

View file

@ -18,6 +18,10 @@ Features:
in its album-level incarnation, it could not represent heterogeneous
releases---for example, an album consisting of a CD and a DVD. Now, tracks
accurately indicate the media they appear on. Thanks to Heinz Wiesinger.
* :doc:`/plugins/embedart`: A new ``ifempty`` config option lets you only
embed album art when no album art is present. Thanks to kerobaros.
* :doc:`/plugins/ftintitle`: The plugin now runs automatically on import. To
disable this, unset the ``auto`` config flag.
Fixes:
@ -44,6 +48,8 @@ Fixes:
the Discogs servers. Thanks to Dustin Rodriguez.
* :doc:`/plugins/embedart`: Do not log "embedding album art into..." messages
during the import process.
* Fix a crash in the autotagger when files had only whitespace in their
metadata.
1.3.8 (September 17, 2014)

View file

@ -28,7 +28,7 @@ When importing a lot of files with the ``auto`` option, one may be reluctant to
overwrite existing embedded art for all of them.
You can tell beets to avoid embedding images that are too different from the
existing ones.
existing ones.
This works by computing the perceptual hashes (`PHASH`_) of the two images and
checking that the difference between the two does not exceed a
threshold. You can set the threshold with the ``compare_threshold`` option.
@ -81,6 +81,9 @@ regarding to embedded art to be written to the file (see
:ref:`image-similarity-check`). The default is 0 (no similarity check).
Requires `ImageMagick`_.
To avoid embedding album art for files that already have album art, set the
``ifempty`` config option to ``yes``.
.. _PIL: http://www.pythonware.com/products/pil/
.. _ImageMagick: http://www.imagemagick.org/
.. _PHASH: http://www.fmwconcepts.com/misc_tests/perceptual_hash_test_results_510/

View file

@ -111,7 +111,6 @@ your config file::
fetchart:
google_search: true
Embedding Album Art
-------------------

View file

@ -21,4 +21,6 @@ If you prefer to remove featured artists entirely instead of adding them to
the title field, either use the ``-d`` flag to the command or set the
``ftintitle.drop`` config option.
To disable this plugin on import, set the ``auto`` config option to false.
.. _MusicBrainz style: http://musicbrainz.org/doc/Style

View file

@ -65,7 +65,7 @@ class AutotagStub(object):
def restore(self):
autotag.mb.match_album = self.mb_match_album
autotag.mb.match_track = self.mb_match_album
autotag.mb.match_track = self.mb_match_track
autotag.mb.album_for_id = self.mb_album_for_id
autotag.mb.track_for_id = self.mb_track_for_id

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# This file is part of beets.
# Copyright 2014, Fabrice Laporte.
#
@ -131,7 +130,7 @@ class LyricsPluginTest(unittest.TestCase):
self.assertFalse(lyrics.is_lyrics(t))
def test_slugify(self):
text = u"http://site.com/çafe-au_lait(boisson)"
text = u"http://site.com/\xe7afe-au_lait(boisson)"
self.assertEqual(lyrics.slugify(text), 'http://site.com/cafe_au_lait')
def test_scrape_strip_cruft(self):
@ -144,6 +143,11 @@ class LyricsPluginTest(unittest.TestCase):
self.assertEqual(lyrics._scrape_strip_cruft(text, True),
"one\ntwo !\n\nfour")
def test_scrape_strip_scripts(self):
text = u"""foo<script>bar</script>baz"""
self.assertEqual(lyrics._scrape_strip_cruft(text, True),
"foobaz")
def test_scrape_merge_paragraphs(self):
text = u"one</p> <p class='myclass'>two</p><p>three"
self.assertEqual(lyrics._scrape_merge_paragraphs(text),
@ -263,7 +267,7 @@ class LyricsGooglePluginTest(unittest.TestCase):
except ImportError:
self.skipTest('Beautiful Soup 4 not available')
if sys.version_info[:3] < (2, 7, 3):
self.skipTest("Pythons built-in HTML parser is not good enough")
self.skipTest("Python's built-in HTML parser is not good enough")
lyrics.LyricsPlugin()
lyrics.fetch_url = MockFetchUrl()

View file

@ -18,6 +18,7 @@ import _common
from _common import unittest
from beets.autotag import mb
from beets import config
import mock
class MBAlbumInfoTest(_common.TestCase):
@ -407,6 +408,78 @@ class ArtistFlatteningTest(_common.TestCase):
self.assertEqual(flat, ('ALIASfr_P', 'ALIASSORTfr_P', 'CREDIT'))
class MBLibraryTest(unittest.TestCase):
def test_match_track(self):
with mock.patch('musicbrainzngs.search_recordings') as p:
p.return_value = {
'recording-list': [{
'title': 'foo',
'id': 'bar',
'length': 42,
}],
}
ti = list(mb.match_track('hello', 'there'))[0]
p.assert_called_with(artist='hello', recording='there', limit=5)
self.assertEqual(ti.title, 'foo')
self.assertEqual(ti.track_id, 'bar')
def test_match_album(self):
mbid = 'd2a6f856-b553-40a0-ac54-a321e8e2da99'
with mock.patch('musicbrainzngs.search_releases') as sp:
sp.return_value = {
'release-list': [{
'id': mbid,
}],
}
with mock.patch('musicbrainzngs.get_release_by_id') as gp:
gp.return_value = {
'release': {
'title': 'hi',
'id': mbid,
'medium-list': [{
'track-list': [{
'recording': {
'title': 'foo',
'id': 'bar',
'length': 42,
},
'position': 9,
}],
'position': 5,
}],
'artist-credit': [{
'artist': {
'name': 'some-artist',
'id': 'some-id',
},
}],
'release-group': {
'id': 'another-id',
}
}
}
ai = list(mb.match_album('hello', 'there'))[0]
sp.assert_called_with(artist='hello', release='there', limit=5)
gp.assert_calledwith(mbid)
self.assertEqual(ai.tracks[0].title, 'foo')
self.assertEqual(ai.album, 'hi')
def test_match_track_empty(self):
with mock.patch('musicbrainzngs.search_recordings') as p:
til = list(mb.match_track(' ', ' '))
self.assertFalse(p.called)
self.assertEqual(til, [])
def test_match_album_empty(self):
with mock.patch('musicbrainzngs.search_releases') as p:
ail = list(mb.match_album(' ', ' '))
self.assertFalse(p.called)
self.assertEqual(ail, [])
def suite():
return unittest.TestLoader().loadTestsFromName(__name__)