Skip to content

Refactor of metadata plugin and opt in all metadata plugins to new baseclass #5787

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 31 commits into
base: master
Choose a base branch
from

Conversation

semohr
Copy link
Contributor

@semohr semohr commented May 17, 2025

Description

At the moment the MetaDataSourcePlugin has multiple responsibilities:

  • fetch data via _search api
  • defines contract for interaction within the beets autotag lookup

I propose splitting these responsibilities, as it would enable us to use the MetaDataSourcePlugin baseclass with plugins that use external packages to fetch data.

This follows from discussion in #5761 and #5748 (comment).

Feedback is highly appreciated, as this is mainly architectural decision and I would prefer if the new behavior is a shared consensus.

To Do

  • Opt in plugins into the new MetaDataSourcePlugin
    • Spotify
    • Musicbrainz
    • Deezer
    • Beatport
    • Chroma
    • Disccogs
  • Remove old MetaDataSourcePlugin and related functions
  • Documentation on the ontology of plugins
  • Changelog

This PR was initially #5764 and was accidentally closed as the target branch was deleted. Wasn't able to recover the original PR.

@Copilot Copilot AI review requested due to automatic review settings May 17, 2025 08:54
Copy link

Thank you for the PR! The changelog has not been updated, so here is a friendly reminder to check if you need to add an entry.

Copilot

This comment was marked as outdated.

@semohr semohr requested a review from Copilot May 18, 2025 13:54
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the metadata lookup architecture by introducing a new beets.metadata_plugins module with a single base interface and migrating all existing source plugins to use it.

  • Added beets/metadata_plugins.py defining MetadataSourcePluginNext and SearchApiMetadataSourcePluginNext
  • Updated all core and third-party plugins (Spotify, MusicBrainz, Discogs, Deezer, Beatport, Chroma/Acoustid, mbsync, missing) to inherit from the new base classes
  • Adjusted tests and documentation to patch and reference beets.metadata_plugins instead of the old beets.plugins metadata methods

Reviewed Changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
test/test_ui.py Adjusted plugin-loading assertion to use new API
test/test_importer.py Patches updated to beets.metadata_plugins lookups
test/plugins/test_mbsync.py References to plugins.track_for_id/album_for_id replaced
docs/dev/plugins.rst Documentation revised for new metadata plugin interface
docs/dev/index.rst Added Confuse library link
beets/util/id_extractors.py Fixed key lookup to use lowercase source names
beets/plugins.py Removed old metadata plugin base and helper methods
beets/metadata_plugins.py New metadata plugin interface and loader functions
beetsplug/spotify.py Migrated Spotify plugin to SearchApiMetadataSourcePluginNext
beetsplug/musicbrainz.py Updated MusicBrainz plugin to MetadataSourcePluginNext
beetsplug/missing.py Switched plugins.* calls to metadata_plugins.*
beetsplug/mbsync.py Updated track/album lookup to new API
beetsplug/discogs.py Refactored Discogs plugin to new metadata interface
beetsplug/deezer.py Migrated Deezer plugin to SearchApiMetadataSourcePluginNext
beetsplug/chroma.py Adapted Acoustid plugin to new base and import patterns
beetsplug/beatport.py Refactored Beatport plugin to new base class and models
Comments suppressed due to low confidence (2)

test/test_ui.py:1062

  • [nitpick] The variable name plugs is ambiguous; consider renaming to found_plugins or loaded_plugins for clarity.
plugs = plugins.find_plugins()

beetsplug/chroma.py:29

  • TrackInfo is not exported by beets.metadata_plugins at runtime; import it from beets.autotag.hooks instead.
from beets.metadata_plugins import MetadataSourcePluginNext, TrackInfo

@snejus

This comment was marked as resolved.

@semohr semohr force-pushed the metadatasource-cleanup branch from c252adc to 2f0b610 Compare May 18, 2025 14:14
@semohr

This comment was marked as resolved.

@semohr semohr requested review from snejus and wisp3rwind May 18, 2025 14:25
Copy link
Member

@snejus snejus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good stuff! I added a couple of comments in the interfaces, however I'm unable to see how this affects the plugins because they have also been refactored.

Could you move optional changes to a separate PR to minimize any side effects this change may cause? I tried testing it and I found spotify stuck in some authentication exception loop, for example:

...done.
Success. Distance: 0.58
Sending event: albuminfo_received
Candidate: DRS - Mid Mic Crisis (6817507)
Computing track assignment...
...done.
Success. Distance: 0.58
spotify: Searching Spotify for 'album:Skeptical Answers artist:Vulkanski'
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.

@semohr
Copy link
Contributor Author

semohr commented May 27, 2025

Good stuff! I added a couple of comments in the interfaces, however I'm unable to see how this affects the plugins because they have also been refactored.

Could you move optional changes to a separate PR to minimize any side effects this change may cause? I tried testing it and I found spotify stuck in some authentication exception loop, for example:

...done.
Success. Distance: 0.58
Sending event: albuminfo_received
Candidate: DRS - Mid Mic Crisis (6817507)
Computing track assignment...
...done.
Success. Distance: 0.58
spotify: Searching Spotify for 'album:Skeptical Answers artist:Vulkanski'
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.
spotify: Spotify access token has expired. Reauthenticating.

That is a good point. How about we keep it for now (at least) until we are happy with the metadata_plugin.py file and after that I slowly start to create PRs to prepare the specific metadataplugins for adaption. Once all plugins are migrated we should than be able to merge this PR.

@snejus
Copy link
Member

snejus commented May 29, 2025

slowly start to create PRs to prepare the specific metadata plugins for adaption

If you undo MetadataSourcePlugin.get_artist -> artists_to_artist_str replacement and _get_id removal, you should be left with very little that needs adapting.

semohr added a commit that referenced this pull request Jun 11, 2025
## Description

Added some more typehints to the spotify plugin. Also added a method to
get the tokenfile and changed to logic for the handle_response to use
`requests.request`.

This is done mainly to prepare for
#5787, see also
#5814
@semohr semohr force-pushed the metadatasource-cleanup branch from 5c78619 to 5c972ae Compare June 11, 2025 13:47
semohr added a commit to semohr/beets that referenced this pull request Jun 18, 2025
semohr added 27 commits July 1, 2025 10:53
fixed minor suggestions from copilot review
- removed unused imports
- removed duplicate implementation for album_distance and track_distance
(same as in the metadata plugin)
no entry was found. Or raise an error if there is a problem with
the request.
@semohr semohr force-pushed the metadatasource-cleanup branch from 2eb1ee2 to 1869c10 Compare July 1, 2025 08:53
data_source=self.data_source, info=info, config=self.config
)

@cached_classproperty # type: ignore[type-defined]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The annotation needs to be on line 231.

@semohr do you run mypy locally?

@snejus
Copy link
Member

snejus commented Jul 5, 2025

Can we keep this PR focused on what the title suggests?

Refactor of metadata plugin and opt in all metadata plugins to new baseclass

Could you move commits with plugins and documentation improvements to separate branches, so that we can review them separately? I'm keen to merge the changes that have been reviewed and would like to have a final look without any noise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants