Skip to content

Add Recently Played plugin provider#3831

Draft
dmoo500 wants to merge 2 commits into
music-assistant:devfrom
dmoo500:feature/recently-played-plugin
Draft

Add Recently Played plugin provider#3831
dmoo500 wants to merge 2 commits into
music-assistant:devfrom
dmoo500:feature/recently-played-plugin

Conversation

@dmoo500
Copy link
Copy Markdown
Contributor

@dmoo500 dmoo500 commented May 2, 2026

Recently Played Plugin Provider

A new PluginProvider that tracks songs heard on radio streams via ICY metadata and surfaces them as a "Recently Played on Radio Station" recommendation folder on the home screen.

⚠️ Depends on PR #3811


How ICY tracking works

  1. Subscribes to QUEUE_UPDATED events
  2. Detects when a radio stream's ICY metadata changes (new song)
  3. Stores the raw ICY title/artist in a local JSON history (max 50 entries, deduplicated)
  4. Resolves each entry in the background via mass.music.search() (library-first, then all providers) to a real track URI
  5. Only resolved entries are shown in the "Recently Played on Radio Station" folder
  6. After every 5 resolved tracks the recommendations cache is invalidated so the next request returns fresh data

Provider settings

Setting Default Description
Show Recently Played on Radio Station ✅ on Enable/disable the folder

Screenshot

Recently Played on Radio Station

recently played on radio

Note: The library-based recommendation folders (Forgotten Tracks/Albums/Artists, Most Played, etc.) have been split into a separate PR: #3890

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 2, 2026

🔒 Dependency Security Report

✅ No dependency changes detected in this PR.

@dmoo500 dmoo500 force-pushed the feature/recently-played-plugin branch 2 times, most recently from 7b7d21e to a66cb02 Compare May 2, 2026 07:20
@dmoo500 dmoo500 force-pushed the feature/recently-played-plugin branch from a66cb02 to 6703e2d Compare May 2, 2026 07:22
@OzGav
Copy link
Copy Markdown
Contributor

OzGav commented May 2, 2026

Some nice ideas in here, thanks for putting it together. I think at the outset the radio station recently played row part of this PR should be on its own and the other rows can be discussed separately.

Then, the radio station part could be done by identifying when an ICY title changes during a radio item, resolve it and write a playlog entry for the resolved track — then the existing recently_played() API would surface it for free. This would reduce the code significantly.

Note that the resolution of the "Artist - Track" info is being done similarly but in a more robust way in my last.fm recommendations provider. You could look at that for some inspiration and there is an argument to be made that this functionality could be split out in future into helpers so many providers could use it. #3020

@dmoo500
Copy link
Copy Markdown
Contributor Author

dmoo500 commented May 2, 2026

Thanks for the feedback and the pointer to #3020 — very helpful context.

After thinking it through, I agree the ICY resolution doesn't belong in a plugin. Here's what I'm proposing instead:

Core change in player_queues.py: Hook into the existing stream_title change detection in _update_queue_from_player. When "stream_title" in changed_keys and the current item is a radio station, fire a background task that calls get_track_by_name()mark_item_played(user_initiated=False). No JSON file, no plugin — just a standard playlog entry, excluded from "Don't Stop the Music" recommendations via the existing user_initiated_only filter.

For the resolution logic I'm planning to extract it as a standalone helper function resolve_stream_title_to_track(mass, stream_title) -> Track | None in helpers/. That makes it a clean extension point — if someone later wants to plug in a more robust MBID/ISRC pipeline (like the one in your #3020), only that one function needs to change, without touching player_queues.py.

Two questions before I open the separate PR:

  1. Does the shared helper approach make sense to you, or would you prefer a different extension point?
  2. Is get_track_by_name() an acceptable baseline for now, with a note in the code that it can be replaced by a more robust resolver in a follow-up?

The recently_played plugin in this PR would then only contain the library recommendation folders (Forgotten Tracks, Most Played, etc.) — which simply query the playlog that Core already populates.

@OzGav
Copy link
Copy Markdown
Contributor

OzGav commented May 2, 2026

Before you continue on I would see what @MarvinSchenkel thinks. The concept of logging the radio tracks as played is similar to this discussion. https://github.com/orgs/music-assistant/discussions/4616

The key issue is that it will have side-effects on play_count / last_played stats for in-library tracks. It means radio will silently influence "Forgotten Tracks", "Most Played", and the personalized rows in my last.fm recommendation provider (for example) — songs the user never chose to listen to will start ranking.

@dmoo500
Copy link
Copy Markdown
Contributor Author

dmoo500 commented May 14, 2026

The scope of this PR has been narrowed to ICY radio tracking only.

The library-based recommendation folders that were originally part of this PR (Forgotten Tracks/Albums/Artists, Most Played, Never/Rarely Played, Recently Favorited, Recently Added, Random Artists/Albums) have been split out into a dedicated plugin: #3890 – Add Recommendations plugin provider.

This keeps each PR focused and makes reviewing easier.

@OzGav
Copy link
Copy Markdown
Contributor

OzGav commented May 14, 2026

@dmoo500 Coming back to this — when we discussed two weeks ago you mentioned extracting resolve_stream_title_to_track() as a helper so the resolver could be swapped for a more robust one later. This revision still calls mass.music.search() directly though. Did you change your mind on the helper approach, or is it still planned for a follow-up? Asking because the mass.music.search() path fans out to every provider and waits for all of them on every track change, which is potentially a lot of unnecessary API calls.

Separately, I've got some reservations about the separate JSON file for history — it sits outside MA's existing storage (cache / playlog). Feels like a symptom of the unresolved question about how radio plays should integrate with the playlog without poisoning the stats?

Also I think there will be problems with the radio providers that cycle the artist name with the upcoming tracks like Radio Paradise and Mother Earth. As far as I can tell lookups would be triggered for this?

Thoughts?

@dmoo500
Copy link
Copy Markdown
Contributor Author

dmoo500 commented May 14, 2026

Thanks for the detailed feedback. The current changes in this PR are limited to the split: the library-based recommendation folders have been moved to a dedicated plugin (#3890), so this PR stays focused on ICY radio tracking only. The main reason it's still a draft — I haven't yet fully resolved the open questions you raised.

That said, I want to address the JSON history point: keeping the ICY history outside the playlog is actually intentional. This plugin is an alternative for users who don't run a scrobbling provider (Last.fm etc.) — similar to what you described in #4616. Once a scrobbling plugin covers radio streams properly, this plugin becomes redundant for those users. Integrating into the playlog would cause exactly the stats poisoning you mentioned (play_count / last_played influenced by radio), which is why the separate file made sense to me.

For the resolver and the Radio Paradise concern: fair points, and I'll revisit those once #3020 is merged.

@OzGav OzGav added this to the 2.10.0 milestone May 18, 2026
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.

2 participants