mirror of
https://github.com/beetbox/beets.git
synced 2025-12-06 08:39:17 +01:00
missing: support non-musicbrainz data sources
This commit is contained in:
parent
6a192d0bdb
commit
4c1f217ce0
6 changed files with 27 additions and 50 deletions
|
|
@ -71,7 +71,7 @@ class DeezerPlugin(MetadataSourcePlugin, BeetsPlugin):
|
|||
self._log.error("Error fetching data from {}\n Error: {}", url, e)
|
||||
return None
|
||||
if "error" in data:
|
||||
self._log.error("Deezer API error: {}", data["error"]["message"])
|
||||
self._log.debug("Deezer API error: {}", data["error"]["message"])
|
||||
return None
|
||||
return data
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ from string import ascii_lowercase
|
|||
|
||||
import confuse
|
||||
from discogs_client import Client, Master, Release
|
||||
from discogs_client import __version__ as dc_string
|
||||
from discogs_client.exceptions import DiscogsAPIError
|
||||
from requests.exceptions import ConnectionError
|
||||
from typing_extensions import TypedDict
|
||||
|
|
@ -64,7 +63,6 @@ class ReleaseFormat(TypedDict):
|
|||
class DiscogsPlugin(BeetsPlugin):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.check_discogs_client()
|
||||
self.config.add(
|
||||
{
|
||||
"apikey": API_KEY,
|
||||
|
|
@ -80,24 +78,7 @@ class DiscogsPlugin(BeetsPlugin):
|
|||
self.config["apikey"].redact = True
|
||||
self.config["apisecret"].redact = True
|
||||
self.config["user_token"].redact = True
|
||||
self.discogs_client = None
|
||||
self.register_listener("import_begin", self.setup)
|
||||
|
||||
def check_discogs_client(self):
|
||||
"""Ensure python3-discogs-client version >= 2.3.15"""
|
||||
dc_min_version = [2, 3, 15]
|
||||
dc_version = [int(elem) for elem in dc_string.split(".")]
|
||||
min_len = min(len(dc_version), len(dc_min_version))
|
||||
gt_min = [
|
||||
(elem > elem_min)
|
||||
for elem, elem_min in zip(
|
||||
dc_version[:min_len], dc_min_version[:min_len]
|
||||
)
|
||||
]
|
||||
if True not in gt_min:
|
||||
self._log.warning(
|
||||
"python3-discogs-client version should be >= 2.3.15"
|
||||
)
|
||||
self.setup()
|
||||
|
||||
def setup(self, session=None):
|
||||
"""Create the `discogs_client` field. Authenticate if necessary."""
|
||||
|
|
@ -179,13 +160,9 @@ class DiscogsPlugin(BeetsPlugin):
|
|||
"""Returns a list of AlbumInfo objects for discogs search results
|
||||
matching an album and artist (if not various).
|
||||
"""
|
||||
if not self.discogs_client:
|
||||
return
|
||||
|
||||
if not album and not artist:
|
||||
self._log.debug(
|
||||
"Skipping Discogs query. Files missing album and "
|
||||
"artist tags."
|
||||
"Skipping Discogs query. Files missing album and artist tags."
|
||||
)
|
||||
return []
|
||||
|
||||
|
|
@ -254,12 +231,9 @@ class DiscogsPlugin(BeetsPlugin):
|
|||
:return: Candidate TrackInfo objects.
|
||||
:rtype: list[beets.autotag.hooks.TrackInfo]
|
||||
"""
|
||||
if not self.discogs_client:
|
||||
return []
|
||||
|
||||
if not artist and not title:
|
||||
self._log.debug(
|
||||
"Skipping Discogs query. File missing artist and " "title tags."
|
||||
"Skipping Discogs query. File missing artist and title tags."
|
||||
)
|
||||
return []
|
||||
|
||||
|
|
@ -290,9 +264,6 @@ class DiscogsPlugin(BeetsPlugin):
|
|||
"""Fetches an album by its Discogs ID and returns an AlbumInfo object
|
||||
or None if the album is not found.
|
||||
"""
|
||||
if not self.discogs_client:
|
||||
return
|
||||
|
||||
self._log.debug("Searching for release {0}", album_id)
|
||||
|
||||
discogs_id = extract_discogs_id_regex(album_id)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
"""List missing tracks."""
|
||||
|
||||
from collections import defaultdict
|
||||
from collections.abc import Iterator
|
||||
|
||||
import musicbrainzngs
|
||||
from musicbrainzngs.musicbrainz import MusicBrainzError
|
||||
|
|
@ -23,7 +24,7 @@ from musicbrainzngs.musicbrainz import MusicBrainzError
|
|||
from beets import config
|
||||
from beets.autotag import hooks
|
||||
from beets.dbcore import types
|
||||
from beets.library import Item
|
||||
from beets.library import Album, Item
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets.ui import Subcommand, decargs, print_
|
||||
|
||||
|
|
@ -226,19 +227,20 @@ class MissingPlugin(BeetsPlugin):
|
|||
if total:
|
||||
print(total_missing)
|
||||
|
||||
def _missing(self, album):
|
||||
def _missing(self, album: Album) -> Iterator[Item]:
|
||||
"""Query MusicBrainz to determine items missing from `album`."""
|
||||
item_mbids = [x.mb_trackid for x in album.items()]
|
||||
if len(list(album.items())) < album.albumtotal:
|
||||
# fetch missing items
|
||||
# TODO: Implement caching that without breaking other stuff
|
||||
album_info = hooks.album_for_mbid(album.mb_albumid)
|
||||
for track_info in getattr(album_info, "tracks", []):
|
||||
if len(album.items()) == album.albumtotal:
|
||||
return
|
||||
|
||||
item_mbids = {x.mb_trackid for x in album.items()}
|
||||
# fetch missing items
|
||||
# TODO: Implement caching that without breaking other stuff
|
||||
if album_info := hooks.album_for_id(album.mb_albumid):
|
||||
for track_info in album_info.tracks:
|
||||
if track_info.track_id not in item_mbids:
|
||||
item = _item(track_info, album_info, album.id)
|
||||
self._log.debug(
|
||||
"track {0} in album {1}",
|
||||
track_info.track_id,
|
||||
album_info.album_id,
|
||||
)
|
||||
yield item
|
||||
yield _item(track_info, album_info, album.id)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ New features:
|
|||
when fetching lyrics.
|
||||
* :doc:`plugins/lyrics`: Rewrite lyrics translation functionality to use Azure
|
||||
AI Translator API and add relevant instructions to the documentation.
|
||||
* :doc:`plugins/missing`: Add support for all metadata sources.
|
||||
|
||||
Bug fixes:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
Missing Plugin
|
||||
==============
|
||||
|
||||
This plugin adds a new command, ``missing`` or ``miss``, which finds
|
||||
and lists, for every album in your collection, which or how many
|
||||
tracks are missing. Listing missing files requires one network call to
|
||||
MusicBrainz. Merely counting missing files avoids any network calls.
|
||||
This plugin adds a new command, ``missing`` or ``miss``, which finds and lists
|
||||
missing tracks for albums in your collection. Each album requires one network
|
||||
call to album data source.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Add the ``missing`` plugin to your configuration (see :ref:`using-plugins`).
|
||||
By default, the ``beet missing`` command lists the names of tracks that your
|
||||
library is missing from each album. It can also list the names of albums that
|
||||
Add the ``missing`` plugin to your configuration (see :ref:`using-plugins`). By
|
||||
default, the ``beet missing`` command fetches album information from the origin
|
||||
data source and lists names of the **tracks** that are missing from your
|
||||
library. It can also list the names of albums that
|
||||
your library is missing from each artist.
|
||||
You can customize the output format, count
|
||||
the number of missing tracks per album, or total up the number of missing
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
"""Tests for discogs plugin."""
|
||||
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from beets import config
|
||||
|
|
@ -23,6 +25,7 @@ from beets.util.id_extractors import extract_discogs_id_regex
|
|||
from beetsplug.discogs import DiscogsPlugin
|
||||
|
||||
|
||||
@patch("beetsplug.discogs.DiscogsPlugin.setup", Mock())
|
||||
class DGAlbumInfoTest(BeetsTestCase):
|
||||
def _make_release(self, tracks=None):
|
||||
"""Returns a Bag that mimics a discogs_client.Release. The list
|
||||
|
|
|
|||
Loading…
Reference in a new issue