diff --git a/beetsplug/lastgenre.py b/beetsplug/lastgenre.py new file mode 100644 index 000000000..b8ae806cc --- /dev/null +++ b/beetsplug/lastgenre.py @@ -0,0 +1,74 @@ +# This file is part of beets. +# Copyright 2011, Adrian Sampson. +# +# 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. + +"""Gets genres for imported music based on Last.fm tags. +""" + +import logging +import pylast + +from beets.plugins import BeetsPlugin + +log = logging.getLogger('beets') +LASTFM = pylast.LastFMNetwork(api_key='2dc3914abf35f0d9c92d97d8f8e42b43') +WEIGHT_THRESH = 50 + +def _tags_for(obj): + """Given a pylast entity (album or track), returns a list of + tag names for that entity. Returns an empty list if the entity is + not found or another error occurs. + """ + try: + res = obj.get_top_tags() + except pylast.WSError, exc: + log.debug(u'last.fm error: %s' % unicode(exc)) + return [] + + tags = [] + for el in res: + if isinstance(el, pylast.TopItem): + el = el.item + tags.append(el.get_name()) + log.debug(u'last.fm tags: %s' % unicode(tags)) + return tags + +def _tags_to_genre(tags): + """Given a tag list, returns a genre. Returns None if no tag is + suitable. This should be smarter, but at the moment it just takes + the top tag and puts it in Title Case. + """ + if not tags: + return None + return tags[0].title() + +class LastGenrePlugin(BeetsPlugin): + pass + +@LastGenrePlugin.listen('album_imported') +def album_imported(lib, album): + tags = _tags_for(LASTFM.get_album(album.albumartist, album.album)) + genre = _tags_to_genre(tags) + if genre: + log.debug(u'adding last.fm album genre: %s' % genre) + album.genre = genre + lib.save() + +@LastGenrePlugin.listen('item_imported') +def item_imported(lib, item): + tags = _tags_for(LASTFM.get_track(item.artist, item.title)) + genre = _tags_to_genre(tags) + if genre: + log.debug(u'adding last.fm item genre: %s' % genre) + item.genre = genre + lib.save() diff --git a/docs/changelog.rst b/docs/changelog.rst index 510486cd8..739d54184 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,12 @@ Changelog ========= +1.0b11 (In Development) +----------------------- + +* The new :doc:`/plugins/lastgenre` automatically assigns genres to imported + albums and items based on Last.fm tags. + 1.0b10 (September 22, 2011) --------------------------- diff --git a/docs/plugins/index.rst b/docs/plugins/index.rst index b46c5ccb2..0a0930330 100644 --- a/docs/plugins/index.rst +++ b/docs/plugins/index.rst @@ -41,6 +41,7 @@ disabled by default, but you can turn them on as described above: mpdupdate embedart web + lastgenre .. _other-plugins: diff --git a/docs/plugins/lastgenre.rst b/docs/plugins/lastgenre.rst new file mode 100644 index 000000000..b6dca4705 --- /dev/null +++ b/docs/plugins/lastgenre.rst @@ -0,0 +1,30 @@ +LastGenre Plugin +================ + +The MusicBrainz database `does not contain genre information`_. Therefore, when +importing and autotagging music, beets does not assign a genre. The +``lastgenre`` plugin fetches *tags* from `Last.fm`_ and assigns them as genres +to your albums and items. The plugin is included with beets as of version +1.0b11. + +.. _does not contain genre information: + http://musicbrainz.org/doc/General_FAQ#Why_does_MusicBrainz_not_support_genre_information.3F +.. _Last.fm: http://last.fm/ + +The plugin requires `pylast`_, which you can install using `pip`_ by typing:: + + pip install pylast + +After you have pylast installed, enable the plugin by putting ``lastgenre`` on +your ``plugins`` line in :doc:`/reference/config`, like so:: + + [beets] + plugins: lastgenre + +For the time being, the genre-selection algorithm is pretty dumb: the most +popular tag is treated as the genre. This could be enhanced by using a "white +list" of known genre names. (This would be a great project for someone looking +to contribute to the beets project!) + +.. _pip: http://www.pip-installer.org/ +.. _pylast: http://code.google.com/p/pylast/