Skip to content

Conversation

@OzGav
Copy link
Contributor

@OzGav OzGav commented Oct 11, 2025

This PR introduces a new Pandora Music Provider focused exclusively on Radio Station playback.

Key Features
Radio Integration: Allows users to access and play their saved Pandora radio stations within Music Assistant.

Authentication: Uses the web-based Pandora REST API (username/password login) to retrieve the user's station list.

Streaming: Streams are served by fetching multiple playlist fragments from Pandora, assembling a multi-part path for continuous playback. Station promos also play.

🛑 Scope Limitations (API Constraint)
This provider is limited to Radio Stations Only and does not support search or station creation.This constraint is due to Pandora's API structure:

The REST API used here only provides access to login, station lists, and playback fragments.

Features like Search and Station Creation are restricted to Pandora's separate GraphQL API, which requires a completely different OAuth authentication flow and developer credentials.

Thus users must continue to create stations on the official Pandora app/website, and those stations will then automatically appear in MA.

There is no metadata for tracks as I'm not sure how to get that back when there are multiple URLs being streamed?

⏱️ Streaming Time Limit
The provider build a dummy list of 1000 URLs which should ensure playback lasts for more than 2 days.

@OzGav OzGav changed the title Pandora Add Pandora provider Oct 11, 2025
@OzGav OzGav marked this pull request as draft October 11, 2025 14:22
@OzGav OzGav marked this pull request as ready for review October 12, 2025 01:20
@TermeHansen
Copy link

Interesting work.

I have used pianobar for many years and it works very well. Have you looked to that code for how to use the api?

https://github.com/PromyLOPh/pianobar

@OzGav
Copy link
Contributor Author

OzGav commented Oct 12, 2025

Thanks for the suggestion! I’ve reviewed pianobar’s implementation, but there’s a key concern: pianobar uses extracted partner keys from official Pandora clients and encryption meant for specific devices. Pandora has explicitly stated that pianobar “is an unauthorized third-party application, and using it is a violation of our Terms of Use.” While they’ve tolerated it for years, it’s technically unauthorized.
This provider uses Pandora’s modern REST API (the same endpoints their website uses) with standard username/password authentication—no extracted keys or reverse-engineered encryption. The tradeoff is we can’t implement search/station creation yet, so users need to manage stations via Pandora’s official apps for now.

@OzGav OzGav marked this pull request as draft October 13, 2025 11:41
Comment on lines +396 to +400
async def browse(self, path: str) -> list[Radio]:
"""Browse radio stations."""
# For now, just return all stations like get_library_radios
# Could be enhanced with categories/genres in the future
return [station async for station in self.get_library_radios()]
Copy link
Member

Choose a reason for hiding this comment

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

this can be left out if you dont support this yet. So the default implementation can be used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried removing that but then there is nothing seen when browsing?

Copy link
Member

Choose a reason for hiding this comment

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

yeah, it will only work after the first sync was completed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After the sync I see the stations in the radio view but there is nothing when I browse to Pandora? This is after I see this in the log
2025-10-28 11:40:25.015 INFO (MainThread) [music_assistant.music] Sync task for Pandora/radios completed

OzGav and others added 2 commits October 19, 2025 22:50
…rough an exception

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
@OzGav OzGav marked this pull request as ready for review October 26, 2025 15:11
Comment on lines +255 to +260
# Is this what is envisaged to pass the static variables?
# MultiPartPath(
# path=f"{self.mass.streams.base_url}/{self.instance_id}_stream?"
# f"station_id={item_id}&track_num={i}&queue_id={queue_id}"
# f"&queue_item_id={queue_item_id}",
# )
Copy link
Member

Choose a reason for hiding this comment

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

yeah, here you can pass whatever you want

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cool, so I understand where we are at is these two questions:
How to pass queue_id/queue_item_id properly?
How to sync metadata updates with actual playback timing?

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.

5 participants