Skip to content

Commit

Permalink
Merge pull request #4854 from JOJ0/discogs_single_album_refactor
Browse files Browse the repository at this point in the history
Support providing album information on singleton imports via Discogs
  • Loading branch information
JOJ0 authored Jul 25, 2023
2 parents 87cd387 + a374977 commit c03bd26
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 4 deletions.
2 changes: 2 additions & 0 deletions beets/autotag/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ def __init__(
bpm: Optional[str] = None,
initial_key: Optional[str] = None,
genre: Optional[str] = None,
album: Optional[str] = None,
**kwargs,
):
self.title = title
Expand Down Expand Up @@ -241,6 +242,7 @@ def __init__(
self.bpm = bpm
self.initial_key = initial_key
self.genre = genre
self.album = album
self.update(kwargs)

# As above, work around a bug in python-musicbrainz-ngs.
Expand Down
1 change: 1 addition & 0 deletions beets/config_default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import:
bell: no
set_fields: {}
ignored_alias_types: []
singleton_album_disambig: yes

clutter: ["Thumbs.DB", ".DS_Store"]
ignore: [".*", "*~", "System Volume Information", "lost+found"]
Expand Down
21 changes: 20 additions & 1 deletion beets/ui/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,15 @@ def disambig_string(info):
if info.albumstatus == 'Pseudo-Release':
disambig.append(info.albumstatus)

if isinstance(info, hooks.TrackInfo):
if info.index:
disambig.append("Index {}".format(str(info.index)))
if info.track_alt:
disambig.append("Track {}".format(info.track_alt))
if (config['import']['singleton_album_disambig'].get()
and info.get('album')):
disambig.append("[{}]".format(info.album))

if disambig:
return ', '.join(disambig)

Expand Down Expand Up @@ -444,18 +453,28 @@ def show_item_change(item, match):
"""
cur_artist, new_artist = item.artist, match.info.artist
cur_title, new_title = item.title, match.info.title
cur_album = item.album if item.album else ""
new_album = match.info.album if match.info.album else ""

if cur_artist != new_artist or cur_title != new_title:
if (cur_artist != new_artist or cur_title != new_title
or cur_album != new_album):
cur_artist, new_artist = ui.colordiff(cur_artist, new_artist)
cur_title, new_title = ui.colordiff(cur_title, new_title)
cur_album, new_album = ui.colordiff(cur_album, new_album)

print_("Correcting track tags from:")
print_(f" {cur_artist} - {cur_title}")
if cur_album:
print_(f" Album: {cur_album}")
print_("To:")
print_(f" {new_artist} - {new_title}")
if new_album:
print_(f" Album: {new_album}")

else:
print_(f"Tagging track: {cur_artist} - {cur_title}")
if cur_album:
print_(f" Album: {new_album}")

# Data URL.
if match.info.data_url:
Expand Down
45 changes: 42 additions & 3 deletions beetsplug/discogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import beets.ui
from beets import config
from beets.util.id_extractors import extract_discogs_id_regex
from beets.autotag.hooks import AlbumInfo, TrackInfo
from beets.autotag.hooks import AlbumInfo, TrackInfo, string_dist
from beets.plugins import MetadataSourcePlugin, BeetsPlugin, get_distance
import confuse
from discogs_client import __version__ as dc_string
Expand Down Expand Up @@ -196,6 +196,40 @@ def candidates(self, items, artist, album, va_likely, extra_tags=None):
self._log.debug('Connection error in album search', exc_info=True)
return []

def get_track_from_album_by_title(self, album_info, title,
dist_threshold=0.3):
def compare_func(track_info):
track_title = getattr(track_info, "title", None)
dist = string_dist(track_title, title)
return (track_title and dist < dist_threshold)
return self.get_track_from_album(album_info, compare_func)

def get_track_from_album(self, album_info, compare_func):
"""Return the first track of the release where `compare_func` returns
true.
:return: TrackInfo object.
:rtype: beets.autotag.hooks.TrackInfo
"""
if not album_info:
return None

for track_info in album_info.tracks:
# check for matching position
if not compare_func(track_info):
continue

# attach artist info if not provided
if not track_info['artist']:
track_info['artist'] = album_info.artist
track_info['artist_id'] = album_info.artist_id
# attach album info
track_info['album'] = album_info.album

return track_info

return None

def item_candidates(self, item, artist, title):
"""Returns a list of TrackInfo objects for Search API results
matching ``title`` and ``artist``.
Expand All @@ -214,6 +248,7 @@ def item_candidates(self, item, artist, title):
if not artist and not title:
self._log.debug('Skipping Discogs query. File missing artist and '
'title tags.')
return

query = f'{artist} {title}'
try:
Expand All @@ -230,7 +265,11 @@ def item_candidates(self, item, artist, title):
candidates = []
for album_cur in albums:
self._log.debug(u'searching within album {0}', album_cur.album)
candidates += album_cur.tracks
track_result = self.get_track_from_album_by_title(
album_cur, item['title']
)
if track_result:
candidates.append(track_result)
# first 10 results, don't overwhelm with options
return candidates[:10]

Expand Down Expand Up @@ -276,7 +315,7 @@ def get_albums(self, query):
query = re.sub(r'(?u)\W+', ' ', query)
# Strip medium information from query, Things like "CD1" and "disk 1"
# can also negate an otherwise positive result.
query = re.sub(r'(?i)\b(CD|disc)\s*\d+', '', query)
query = re.sub(r'(?i)\b(CD|disc|vinyl)\s*\d+', '', query)

try:
releases = self.discogs_client.search(query,
Expand Down
3 changes: 3 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ New features:
:bug:`4373`
* Fetch the ``release_group_title`` field from MusicBrainz.
:bug: `4809`
* :doc:`plugins/discogs`: Add support for applying album information on
singleton imports.
:bug: `4716`

Bug fixes:

Expand Down
5 changes: 5 additions & 0 deletions docs/plugins/discogs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ Discogs Plugin
The ``discogs`` plugin extends the autotagger's search capabilities to
include matches from the `Discogs`_ database.

Files can be imported as albums or as singletons. Since `Discogs`_ matches are
always based on `Discogs`_ releases, the album tag is written even to
singletons. This enhances the importers results when reimporting as (full or
partial) albums later on.

.. _Discogs: https://discogs.com

Installation
Expand Down
13 changes: 13 additions & 0 deletions docs/reference/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,19 @@ Fields are persisted to the media files of each track.

Default: ``{}`` (empty).

.. _singleton_album_disambig:

singleton_album_disambig
~~~~~~~~~~~~~~~~~~~~~~~~

During singleton imports and if the metadata source provides it, album names
are appended to the disambiguation string of matching track candidates. For
example: ``The Artist - The Title (Discogs, Index 3, Track B1, [The Album]``.
This feature is currently supported by the :doc:`/plugins/discogs` and the
:doc:`/plugins/spotify`.

Default: ``yes``.

.. _musicbrainz-config:

MusicBrainz Options
Expand Down

0 comments on commit c03bd26

Please sign in to comment.