mirror of
https://github.com/beetbox/beets.git
synced 2025-12-27 11:02:43 +01:00
Added Discogs search plugin.
This commit is contained in:
parent
69f2659410
commit
12476ca22e
3 changed files with 155 additions and 0 deletions
54
beetsplug/abstract_search.py
Normal file
54
beetsplug/abstract_search.py
Normal 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
100
beetsplug/discogs.py
Normal 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
|
||||
1
setup.py
1
setup.py
|
|
@ -75,6 +75,7 @@ setup(name='beets',
|
|||
'munkres',
|
||||
'unidecode',
|
||||
'musicbrainzngs>=0.2',
|
||||
'discogs_client>=1.1',
|
||||
'pyyaml',
|
||||
]
|
||||
+ (['colorama'] if (sys.platform == 'win32') else [])
|
||||
|
|
|
|||
Loading…
Reference in a new issue