diff --git a/beetsplug/echonest.py b/beetsplug/echonest.py index e183eb8ef..e31a8c425 100644 --- a/beetsplug/echonest.py +++ b/beetsplug/echonest.py @@ -23,6 +23,7 @@ from string import Template import subprocess from beets import util, config, plugins, ui +from beets.dbcore import types import pyechonest import pyechonest.song import pyechonest.track @@ -38,7 +39,9 @@ DEVNULL = open(os.devnull, 'wb') ALLOWED_FORMATS = ('MP3', 'OGG', 'AAC') UPLOAD_MAX_SIZE = 50 * 1024 * 1024 -# The attributes we can import and where to store them in beets fields. +# Maps attribute names from echonest to their field names in beets. +# The attributes are retrieved from a songs `audio_summary`. See: +# http://echonest.github.io/pyechonest/song.html#pyechonest.song.profile ATTRIBUTES = { 'energy': 'energy', 'liveness': 'liveness', @@ -49,6 +52,16 @@ ATTRIBUTES = { 'tempo': 'bpm', } +# Types for the flexible fields added by `ATTRIBUTES` +FIELD_TYPES = { + 'energy': types.FLOAT, + 'liveness': types.FLOAT, + 'speechiness': types.FLOAT, + 'acousticness': types.FLOAT, + 'danceability': types.FLOAT, + 'valence': types.FLOAT, +} + MUSICAL_SCALE = ['C', 'C#', 'D', 'D#', 'E' 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] @@ -104,6 +117,9 @@ def similar(lib, src_item, threshold=0.15, fmt='${difference}: ${path}'): class EchonestMetadataPlugin(plugins.BeetsPlugin): + + item_types = FIELD_TYPES + def __init__(self): super(EchonestMetadataPlugin, self).__init__() self.config.add({ diff --git a/test/helper.py b/test/helper.py index 0688708fd..f706871a7 100644 --- a/test/helper.py +++ b/test/helper.py @@ -43,7 +43,7 @@ from enum import Enum import beets from beets import config import beets.plugins -from beets.library import Library, Item +from beets.library import Library, Item, Album from beets import importer from beets.autotag.hooks import AlbumInfo, TrackInfo from beets.mediafile import MediaFile @@ -168,18 +168,24 @@ class TestHelper(object): Similar setting a list of plugins in the configuration. Make sure you call ``unload_plugins()`` afterwards. """ + # FIXME this should eventually be handled by a plugin manager beets.config['plugins'] = plugins beets.plugins.load_plugins(plugins) beets.plugins.find_plugins() + Item._types = beets.plugins.types(Item) + Album._types = beets.plugins.types(Album) def unload_plugins(self): """Unload all plugins and remove the from the configuration. """ + # FIXME this should eventually be handled by a plugin manager beets.config['plugins'] = [] for plugin in beets.plugins._classes: plugin.listeners = None beets.plugins._classes = set() beets.plugins._instances = {} + Item._types = {} + Album._types = {} def create_importer(self, item_count=1, album_count=1): """Create files to import and return corresponding session. diff --git a/test/test_echonest.py b/test/test_echonest.py index da05adde3..43f006dfc 100644 --- a/test/test_echonest.py +++ b/test/test_echonest.py @@ -70,9 +70,17 @@ class EchonestCliTest(unittest.TestCase, TestHelper): self.run_command('echonest') item.load() - self.assertEqual(item['danceability'], '0.5') + self.assertEqual(item['danceability'], 0.5) + self.assertEqual(item['liveness'], 0.5) + self.assertEqual(item['bpm'], 120) self.assertEqual(item['initial_key'], 'C#m') + def test_custom_field_range_query(self): + item = Item(liveness=2.2) + item.add(self.lib) + item = self.lib.items('liveness:2.2..3').get() + self.assertEqual(item['liveness'], 2.2) + def suite(): return unittest.TestLoader().loadTestsFromName(__name__)