Skip to content

Add mbpseudo plugin for pseudo-release proposals#5888

Merged
semohr merged 13 commits intobeetbox:masterfrom
asardaes:feature/mbpseudo-plugin
Nov 3, 2025
Merged

Add mbpseudo plugin for pseudo-release proposals#5888
semohr merged 13 commits intobeetbox:masterfrom
asardaes:feature/mbpseudo-plugin

Conversation

@asardaes
Copy link
Contributor

@asardaes asardaes commented Jul 20, 2025

Description

Hi there, I wanted to implement this for my foreign music and I would be glad to get some feedback from the maintainers. I will address all the TODOs, but first I'd like to know if this fits.

The auto-tagger can already resolve official releases from pseudo-releases, but this only happens if the user provides search IDs manually. Similarly, import.languages can be used to search for artist aliases (and that does happen automatically), but that doesn't apply to album and track names.

This plugin proactively searches for pseudo-releases during import and adds them as candidates. Since it also depends on MusicBrainz, there are some special considerations for the default logic (which is now a plugin as well). However, at the very least it expects a list of desired names of scripts in the configuration, for example:

mbpseudo:
    scripts:
    - Latn

It will use that to search for pseudo-releases that match some of the desired scripts, but will only do so if the input tracks match against an official release that is not in one of the desired scripts.

Standalone Usage

This would be the recommended approach, which involves disabling the musicbrainz plugin. The mbpseudo plugin will manually delegate the initial search to it. Since the data source of official releases will still match MusicBrainz, weights are still relevant:

mbpseudo:
    source_weight: 0.0
    scripts:
    - Latn

musicbrainz:
    source_weight: 0.1

A setup like that would ensure that the pseudo-releases have slightly more preference when choosing the final proposal.

Combined Usage

I initially thought it would be important to coexist with the musicbrainz plugin when it's enabled, and reuse as much of its data as possible to avoid redundant calls to the MusicBrainz API. I have the impression this is not really important in the end, and maybe things could be simplified if we decide that both plugins shouldn't coexist.

As it is right now, using both plugins at the same time would still work, but it'll only avoid redundancy if musicbrainz emits its candidates before mbpseudo, which is why I modified the plugin-loading logic slightly to guarantee ordering. I'm not sure if you think this could be an issue, but I think the musicbrainz plugin is also used by other plugins and I can imagine it's good to guarantee the order that is declared in the configuration?

If the above is fulfilled, the mbpseudo plugin will use listeners to intercept data emitted by the musicbrainz plugin and check if any of them have pseudo-releases that might be desirable.

To Do

  • Documentation.
  • Changelog.
  • Tests.

@github-actions
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.

@codecov
Copy link

codecov bot commented Jul 20, 2025

Codecov Report

❌ Patch coverage is 22.34637% with 139 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.40%. Comparing base (c2d1bc3) to head (41487b3).

Files with missing lines Patch % Lines
beetsplug/mbpseudo.py 21.46% 139 Missing ⚠️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@asardaes asardaes force-pushed the feature/mbpseudo-plugin branch 6 times, most recently from 6bae336 to 41487b3 Compare August 9, 2025 13:40
@asardaes asardaes force-pushed the feature/mbpseudo-plugin branch 2 times, most recently from fc81dc8 to d864a24 Compare August 12, 2025 10:25
@asardaes asardaes force-pushed the feature/mbpseudo-plugin branch 2 times, most recently from a7df94f to 1fb8acf Compare August 26, 2025 16:58
@asardaes asardaes force-pushed the feature/mbpseudo-plugin branch 2 times, most recently from 2629a71 to 157f187 Compare September 5, 2025 16:49
@asardaes asardaes force-pushed the feature/mbpseudo-plugin branch from 157f187 to 2681a5f Compare September 10, 2025 11:09
@asardaes asardaes requested a review from a team as a code owner September 10, 2025 11:09
@asardaes asardaes force-pushed the feature/mbpseudo-plugin branch from 2681a5f to e37c711 Compare September 13, 2025 23:26
@asardaes asardaes force-pushed the feature/mbpseudo-plugin branch from e37c711 to f79eb11 Compare September 20, 2025 21:58
@semohr
Copy link
Contributor

semohr commented Oct 1, 2025

I was wondering if it woudn't be easier to just extend the default Musicbrainz plugin instead of using the event system. Have you tried that?

@asardaes
Copy link
Contributor Author

asardaes commented Oct 1, 2025

@semohr I think that would be fine, I just didn't want to start by modifying the core plugin. I'm not sure how to go about the source weights though, would the distance calculation need to be modified to consider script somehow? Or how could the user nudge the proposals toward the pseudo releases?

@semohr
Copy link
Contributor

semohr commented Oct 1, 2025

I think that would be fine, I just didn't want to start by modifying the core plugin.

Instead, I was thinking along the lines of extending it, while keeping the logic isolated in its own plugin/file. Something like:

from beetsplug.musicbrainz import MusicbrainzPlugin

class MBPseudoPlugin(MusicbrainzPlugin):
    pass

That way, the original plugin remains untouched, and this extension can encapsulate the new behavior.
It might also be good to raise a warning or error if both plugins are enabled at the same time, just to avoid confusing overlaps.

I'm not sure how to go about the source weights though, would the distance calculation need to be modified to consider script somehow? Or how could the user nudge the proposals toward the pseudo releases?

I don’t think special handling is strictly necessary here, but I’m not 100% certain. You might want to take a look at the functions in autotag.distance.

@asardaes
Copy link
Contributor Author

asardaes commented Oct 2, 2025

Ah I see, I imagine that should be fine. I'm away from my pc for some time, but I'll take a look at that option.

@asardaes asardaes force-pushed the feature/mbpseudo-plugin branch 3 times, most recently from 74072ef to 94e8f19 Compare October 6, 2025 04:07
@asardaes
Copy link
Contributor Author

asardaes commented Oct 6, 2025

@semohr I changed the implementation as suggested and I think it looks good. The config I mentioned in the original post still applies. I've been validating with this release and I do get the pseudo-release as the main proposal.

If this is ok, I'll add some tests and docs.

Copy link
Contributor

@semohr semohr left a comment

Choose a reason for hiding this comment

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

This looks pretty amazing and way cleaner now! Nicely done.

Some more things before we merge this. I think you wanted to do this either way but the plugin needs some documentation. Also while not required tests would be highly appreciated.

Another minor thing: We are trying to not use Optional[...] instead please use ... | None (this might need a from __future__ import annotations ). Also if you want to continue maintaining this plugin we would highly appreciate if you could add you handle to the CODEOWNERS file. This way you get updates if any PRs try to apply changes to the plugin (totally optional).

@miblodelcarpio
Copy link

miblodelcarpio commented Oct 20, 2025

Lovely, thank you for looking into this, @asardaes, and @semohr for suggesting add_media_field. Do you want to gauge whether or not this custom tag idea is in scope for the PR? Naturally I'd welcome it, but equally I'd be happy to see the plugin be merged before going into the broader ideas of "applying metadata from multiple sources to the same file" and "tags for translations that any plugin could modify". There is also issue #2866 from 2018 which I've just discovered and reckon gets to the nub of it, with @amogus07 even currently working on a plugin with the same requirement!

@amogus07
Copy link
Contributor

Something like in my custom base class could be implemented in MetadataSourcePlugin, so plugins can like mine easily define fields in their namespace which is inferred from self.name.

@amogus07
Copy link
Contributor

Honestly, I don't care about having multiple artists, titles, etc. I just think it's useful for id fields. Everything else can be inferred from the ids by reimporting. It would also fix multiple plugins trying to override the same album or item when running beet mbsync, since they would all use different id fields.

@asardaes
Copy link
Contributor Author

asardaes commented Oct 20, 2025

@miblodelcarpio I added the option to only add custom tags. Since beets already supports custom path configurations, it seems this would accomplish what you wanted:

paths:
    default: %ifdef{album_artist_transl, $album_artist_transl, $albumartist}/%ifdef{album_transl, $album_transl, $album}%aunique{}/$track%ifdef{title_transl, $title_transl, $title}

@asardaes asardaes force-pushed the feature/mbpseudo-plugin branch from 59df605 to 0443313 Compare October 20, 2025 20:21
@miblodelcarpio
Copy link

@asardaes Oh wow, thank you so much! Really looking forward to giving this a spin. Coupled with the conditional logic happening in item_fields from the inline plugin, this'll be a dream. I'm thinking:

item_fields:
    artist: %ifdef{artist_transl, $artist_transl, $artist}
    album: %ifdef{album_transl, $album_transl, $album}
    title: %ifdef{title_transl, $title_transl, $title}

While $albumartist_sort already comes in the language I'm after, English, with $artist being used for comp releases.

@asardaes asardaes force-pushed the feature/mbpseudo-plugin branch from 0443313 to dbd29e3 Compare October 22, 2025 17:15
@asardaes
Copy link
Contributor Author

@semohr I don't have further changes if you agree with the latest additions.

@asardaes asardaes force-pushed the feature/mbpseudo-plugin branch from dbd29e3 to 92998cd Compare October 26, 2025 12:29
@semohr
Copy link
Contributor

semohr commented Oct 26, 2025

In my head this is already approved! I'm waiting for some free time to have one closer look and merge this.

I haven't forgotten you, please be patient for a bit longer 🙏

@semohr semohr self-assigned this Oct 26, 2025
@asardaes asardaes force-pushed the feature/mbpseudo-plugin branch from 92998cd to 0899374 Compare October 29, 2025 17:38
@asardaes asardaes force-pushed the feature/mbpseudo-plugin branch from 0899374 to c087851 Compare November 1, 2025 12:52
@semohr semohr merged commit beda6fc into beetbox:master Nov 3, 2025
19 checks passed
@asardaes asardaes deleted the feature/mbpseudo-plugin branch November 3, 2025 12:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants