diff --git a/beetsplug/acousticbrainz.py b/beetsplug/acousticbrainz.py new file mode 100644 index 0000000000..fa1d71b5fb --- /dev/null +++ b/beetsplug/acousticbrainz.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +# This file is part of beets. +# Copyright 2015-2016, Ohm Patel. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +""" Fetch various AcousticBrainz metadata using MBID +""" +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +import requests + +from beets import plugins, ui + +ACOUSTIC_URL = "http://acousticbrainz.org/" +LEVEL = "/high-level" + + +class AcousticPlugin(plugins.BeetsPlugin): + def __init__(self): + super(AcousticPlugin, self).__init__() + + def commands(self): + cmd = ui.Subcommand('acousticbrainz', + help="fetch metadata from AcousticBrainz") + + def func(lib, opts, args): + fetch_info(self, lib) + + cmd.func = func + return [cmd] + + +def fetch_info(self, lib): + """Currently outputs MBID and corresponding request status code + """ + for item in lib.items(): + if item.mb_trackid: + rs = requests.get(generate_url(item.mb_trackid)) + + try: + rs.json() + except ValueError: + self._log.debug('Invalid Response: {}', rs.text) + + item.danceable = get_value(self._log, rs.json(), ["highlevel", + "danceability", + "all", + "danceable"]) + item.mood_happy = get_value(self._log, rs.json(), ["highlevel", + "mood_happy", + "all", + "happy"]) + item.mood_party = get_value(self._log, rs.json(), ["highlevel", + "mood_party", + "all", + "party"]) + + item.write() + item.store() + + +def generate_url(mbid): + """Generates url of AcousticBrainz end point for given MBID + """ + return ACOUSTIC_URL + mbid + LEVEL + + +def get_value(log, data, map_path): + """Allows traversal of dictionary with cleaner formatting + """ + try: + return reduce(lambda d, k: d[k], map_path, data) + except KeyError: + log.debug('Invalid Path: {}', map_path) diff --git a/docs/plugins/acousticbrainz.rst b/docs/plugins/acousticbrainz.rst new file mode 100644 index 0000000000..7019671a2c --- /dev/null +++ b/docs/plugins/acousticbrainz.rst @@ -0,0 +1,17 @@ +Acousticbrainz Plugin +===================== + +The ``acoustricbrainz`` plugin provides a command that traverses through a library and tags tracks with valid MusicBrainz IDs with additional metadata such as + +* ``danceable`` + + Predicts how easy the track is danceable to +* ``mood_happy`` + + Predicts the probability this track is played to invoke happiness +* ``mood_party`` + + Predicts the probability this track is played in a party environment + +Enable the ``acousticbrainz`` plugin in your configuration (see :ref:`using-plugins`) and run with: + + $ beet acousticbrainz + +Additional command-line options coming soon. diff --git a/docs/plugins/index.rst b/docs/plugins/index.rst index 5da5ee0be1..0c1c4ce11a 100644 --- a/docs/plugins/index.rst +++ b/docs/plugins/index.rst @@ -31,6 +31,7 @@ Each plugin has its own set of options that can be defined in a section bearing .. toctree:: :hidden: + acousticbrainz badfiles bpd bpm @@ -94,6 +95,7 @@ Autotagger Extensions Metadata -------- +* :doc:`acousticbrainz`: Fetch various AcousticBrainz metadata * :doc:`bpm`: Measure tempo using keystrokes. * :doc:`echonest`: Automatically fetch `acoustic attributes`_ from `the Echo Nest`_ (tempo, energy, danceability, ...).