Added Discogs search plugin.

This commit is contained in:
imenem 2013-03-07 17:59:52 +02:00
parent 69f2659410
commit 12476ca22e
3 changed files with 155 additions and 0 deletions

View file

@ -0,0 +1,54 @@
"""Abstract plugin for new search
service support for the autotagger.
"""
global str
import logging
import abc
from os.path import dirname, basename
from beets.plugins import BeetsPlugin
from beets.autotag import match
log = logging.getLogger('beets')
# Plugin structure and autotagging logic.
class AbstractSearchPlugin(BeetsPlugin):
name = ''
def __init__(self):
self.name = self.__class__.__name__
super(AbstractSearchPlugin, self).__init__()
def candidates(self, items):
try:
artist, album = self._metadata_from_items(items)
albums = self._search(artist, album)
return map(self._album_info, albums)
except BaseException as e:
log.error(self.name + ' search error: ' + str(e))
return []
@abc.abstractmethod
def _search(self, artist, album):
log.debug(self.name + ' search for: ' + artist + ' - ' + album)
return []
@abc.abstractmethod
def _album_info(self, album):
pass
def _metadata_from_items(self, items):
artist, album, artist_consensus = match.current_metadata(items)
va_likely = ((not artist_consensus) or
(artist.lower() in match.VA_ARTISTS) or
any(item.comp for item in items))
if va_likely:
return u'', album
else:
return artist, album

100
beetsplug/discogs.py Normal file
View file

@ -0,0 +1,100 @@
"""Adds Discogs album search support to the
autotagger. Requires the discogs-client library.
"""
import string
from time import strptime
from beetsplug.abstract_search import AbstractSearchPlugin
from beets.autotag import hooks
import discogs_client
from discogs_client import Artist, Release, Search, DiscogsAPIError
discogs_client.user_agent = 'curl/7.28.0'
# Plugin structure and autotagging logic.
class DiscogsPlugin(AbstractSearchPlugin):
def __init__(self):
super(DiscogsPlugin, self).__init__()
def _search(self, artist, album):
super(DiscogsPlugin, self)._search(artist, album)
try:
albums = Search(artist + ' ' + album).results()[0:5]
return filter(lambda album: isinstance(album, Release), albums)
except DiscogsAPIError as e:
if str(e).startswith('404'):
return []
else:
raise e
def _album_info(self, album):
return hooks.AlbumInfo(
album.title,
None,
self._artists_names(album.artists),
None,
map(self._track_info, album.tracklist)
)
def _track_info(self, track):
disk_number, position = self._position(track['position'])
return hooks.TrackInfo(
track['title'],
None,
self._artists_names(track['artists']),
None,
self._duration(track['duration']),
position,
disk_number
)
def _artists_names(self, artists):
filtered = filter(lambda artist: isinstance(artist, Artist), artists)
names = map(lambda artist: artist.name, filtered)
return ' and '.join(names)
def _position(self, position):
try:
original = position
"""Convert track position from u'1', u'2' or u'A', u'B' to 1, 2 etc"""
position = position.encode('ascii').lower() # Convert from unicode to lovercase ascii
if not len(position):
return 0, 0
first = position[0]
if string.ascii_lowercase.find(first) != -1:
number = ord(first) - 96
if len(position) == 1:
replace = '%i' % number # Letter is track number
else:
replace = '%i-' % number # Letter is vinyl side
position = position.replace(first, replace)
if position.find('-') == -1:
position = '1-' + position # If no disk number, set to 1
result = map(int, position.split('-'))
if len(result) == 2:
return result
else:
return 0, 0
except ValueError:
return 0, 0
def _duration(self, duration):
try:
duration = strptime(duration.encode('ascii'), '%M:%S')
except ValueError:
return 0
else:
return duration.tm_min * 60 + duration.tm_sec

View file

@ -75,6 +75,7 @@ setup(name='beets',
'munkres',
'unidecode',
'musicbrainzngs>=0.2',
'discogs_client>=1.1',
'pyyaml',
]
+ (['colorama'] if (sys.platform == 'win32') else [])