Skip to content

MusicBrainz provider refactor and birthday/memoriam/anniversary recommendations#3833

Draft
dmoo500 wants to merge 1 commit into
music-assistant:devfrom
dmoo500:feature/musicbrainz-recommendations
Draft

MusicBrainz provider refactor and birthday/memoriam/anniversary recommendations#3833
dmoo500 wants to merge 1 commit into
music-assistant:devfrom
dmoo500:feature/musicbrainz-recommendations

Conversation

@dmoo500
Copy link
Copy Markdown
Contributor

@dmoo500 dmoo500 commented May 3, 2026

Summary

Refactors the MusicBrainz provider into a multi-file package and adds ProviderFeature.RECOMMENDATIONS support.

⚠️ Depends on PR #3811

Refactor: monolithic __init__.py → multi-file package

The existing musicbrainz/__init__.py (~600 lines) is split into focused modules:

File Purpose
api_client.py HTTP client with 5 req/s throttling and 30-day caching
models.py MusicBrainz dataclasses (MusicBrainzArtist, MusicBrainzReleaseGroup, etc.)
constants.py Shared config key constants (breaks circular-import)
provider.py MusicbrainzProvider(MetadataProvider) — all existing functionality preserved
recommendations.py MusicBrainzRecommendationManager — new recommendation logic

Feature: three recommendation types

All three scan the user's library and cross-reference MusicBrainz data:

  1. Birthday artists — library artists whose MB life_span.begin date matches today's month/day, grouped by genre into RecommendationFolders with mdi-cake-variant icon.
  2. In memoriam artists — library artists whose MB life_span.end date matches today's (only when life_span.ended == True), icon mdi-candle.
  3. Album release anniversaries — library albums with a MB release-group ID whose first_release_date matches today, filtered to 5-year milestones only (5, 10, 15, … years ago), icon mdi-calendar-star. Each matching album becomes a playable RecommendationFolder.

Implementation notes

  • No wildcard API queriesbegin:????-MM-DD returns 0 results on the MA mirror, so the library-scan strategy is used: iterate library items, fetch individual MB records (30-day cached).
  • MusicBrainzAPIClient.domain — added domain = "musicbrainz" class attribute required by the @use_cache decorator.
  • MusicBrainzReleaseGroup.first_release_date — new optional field populated from the existing release-group/{id} endpoint response.

Tests

17 unit tests covering:

  • Birthday date matching (exact, partial/year-only, no life_span, API error)
  • In-memoriam date matching + ended=False guard
  • _build_artist_folders genre grouping, overrides, and "Other" bucket
  • Album anniversary: 5-year match, non-milestone skip, wrong date skip, no MBID skip
  • get_recommendations integration for all three types

Note: Depends on #3811. The diff against dev includes commits from that PR; once #3811 merges this PR can be re-based directly onto dev.

Copilot AI review requested due to automatic review settings May 3, 2026 13:28
Copy link
Copy Markdown
Contributor

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 extends Music Assistant’s cross-type ProviderFeature dispatch so that metadata providers and plugin providers can participate in features like browse/recommendations/similar-* (building on the #3811 groundwork), and updates controllers/base classes plus tests accordingly.

Changes:

  • Add MusicAssistant.get_providers_supporting_feature(...) to retrieve providers supporting a feature across provider-type tiers with priority ordering.
  • Enable cross-type feature usage in controllers (e.g., browse root and recommendations aggregation; similar-artists endpoint; similar-tracks fallback to metadata/plugins).
  • Add/extend base provider APIs (PluginProvider/MetadataProvider) for similar-* and recommendations, update demo plugin provider, and add unit tests.

Reviewed changes

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

Show a summary per file
File Description
music_assistant/mass.py Adds get_providers_supporting_feature helper and removes plugin-specific feature query helper.
music_assistant/controllers/music.py Uses cross-type provider lookup for browse root and recommendations; widens provider recommendation typing.
music_assistant/controllers/media/tracks.py Adds metadata/plugin fallback for similar_tracks.
music_assistant/controllers/media/artists.py Adds similar_artists API endpoint with music-first, metadata/plugin fallback dispatch.
music_assistant/controllers/media/base.py Allows get_provider_item to call get_item on plugin providers (in addition to music providers).
music_assistant/models/metadata_provider.py Adds cross-type feature stubs: similar tracks/artists + recommendations.
music_assistant/models/plugin.py Adds cross-type feature stubs + playlist/get_item plumbing for plugins.
music_assistant/models/music_provider.py Adds get_similar_artists abstract method to the MusicProvider base.
music_assistant/providers/_demo_plugin_provider/__init__.py Documents/illustrates cross-type feature stubs and playlist plumbing for plugins.
music_assistant/providers/qobuz/__init__.py Removes an unused/empty get_similar_artists stub.
tests/test_cross_type_features.py Adds unit tests for provider ordering and cross-type dispatch behavior.

Comment thread music_assistant/controllers/media/tracks.py
Comment thread music_assistant/mass.py Outdated
Comment thread tests/test_cross_type_features.py Outdated
@dmoo500 dmoo500 marked this pull request as draft May 3, 2026 13:43
@OzGav
Copy link
Copy Markdown
Contributor

OzGav commented May 3, 2026

I think the refactor should be one PR and the extra recommendations another. Also there are outstanding PRs which touch the metadata controller and this will mean problems later. I was planning on looking into doing this (so thanks for taking it on) but I was going to wait until all the other changes were merged.

Edit: sorry I misread and this is the musicbrainz controller. Regardless, the PR should only be just that. And my last.fm provider also touches this.

@OzGav
Copy link
Copy Markdown
Contributor

OzGav commented May 3, 2026

And why is the Qobuz provider getting touched here?

edit: ok I see this is the result of #3811 . Once that is merged and this is rebased then split this into two.

@dmoo500
Copy link
Copy Markdown
Contributor Author

dmoo500 commented May 4, 2026

Keeping this as Draft until two upstream PRs are merged:

Once both are merged, I'll rebase and split this into two PRs:

  • MusicBrainz provider refactor (monolithic init.py → multi-file package)
  • Birthday/memoriam/anniversary recommendations

Refactor the monolithic musicbrainz __init__.py into separate modules:
- api_client.py: HTTP client with throttling and caching
- models.py: MusicBrainz dataclasses (adds first_release_date to ReleaseGroup)
- constants.py: shared config key constants
- provider.py: MusicbrainzProvider metadata provider

Implement ProviderFeature.RECOMMENDATIONS via a new RecommendationManager:
- Birthday artists: library artists born on today's date
- In memoriam artists: library artists who passed away on today's date
- Album anniversaries: 5-year milestone release anniversaries for today's date

Add 17 unit tests covering all recommendation types.
@dmoo500 dmoo500 force-pushed the feature/musicbrainz-recommendations branch from 581a028 to b4fa005 Compare May 4, 2026 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants