Add mammamiradio music provider#3836
Conversation
mammamiradio is a self-hosted, AI-generated continuous Italian radio
station with banter, music, and ads. This provider lets MA users add
their mammamiradio HA addon as a music source alongside SomaFM,
RadioBrowser, etc.
- One configurable field: mammamiradio_url (default http://localhost:8100)
- Single Radio entry titled "mammamiradio"
- BROWSE + SEARCH supported
- Stream URL: ${mammamiradio_url}/stream (Icecast, MP3 128k)
- Reachability checked at provider init; clear error if addon offline
- ProviderUnavailableError raised at stream time if addon goes offline
Requires the mammamiradio HA addon installed and running:
https://github.com/florianhorner/mammamiradio
🔒 Dependency Security Report✅ No dependency changes detected in this PR. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dbc466176f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| async with self.mass.http_session.head( | ||
| stream_path, timeout=timeout, allow_redirects=True | ||
| ) as response: | ||
| if response.status >= 400: | ||
| msg = ( |
There was a problem hiding this comment.
Avoid rejecting streams that disallow HEAD requests
Playback is blocked behind a HEAD probe, and any >=400 response is converted into ProviderUnavailableError. This breaks valid GET-only stream endpoints (a common setup for Icecast-style mounts): a stream can be playable via GET /stream but still return 405 Method Not Allowed to HEAD, causing MA to always report the source as unavailable and never attempt playback. Treat 405 as non-fatal or use a lightweight GET probe instead.
Useful? React with 👍 / 👎.
|
Marking this as draft while I finalize a few things internally before review:
Will mark ready-for-review once those land. Sorry for the noise — should not have hit ready on the first push. Appreciate your patience. |
|
Instead of a new provider why not just add this one station manually? |
Embeds the rendered mammamiradio logo as a base64 PNG inside the SVG wrapper; the prior icon was a placeholder shipped with the v1 PR.
Three coupled fixes addressing PR music-assistant#3836 review: - DEFAULT_URL now points to http://localhost:8000 to match the mammamiradio HA addon's actual listen port (the prior 8100 default meant the primary one-click setup path was broken). - /healthz replaces /api/capabilities as the reachability probe target; /healthz is the addon's canonical liveness endpoint and returns a lighter, structured payload. - get_stream_details now uses GET (not HEAD) for the per-play reachability check. Icecast and FastAPI/uvicorn stream endpoints commonly 405 on HEAD, which previously false-positived as ProviderUnavailableError on valid streams (codex bot P1 finding). Also strips query/fragment from the configured URL so a paste with trailing junk no longer corrupts the stream path.
Why: locks in the PR music-assistant#3836 cleanup fixes so they cannot regress. - HEAD-mock assertions swapped to GET to match the production probe. - New test_get_stream_details_tolerates_head_405_regression: simulates an Icecast-style server that 405s on HEAD and 200s on GET, asserts no ProviderUnavailableError and that head() is never called. - URL strings updated from localhost:8100 to localhost:8000 to follow the new DEFAULT_URL; the constant is also imported and asserted via the config-entries default. - Reachability probe assertion updated to /healthz. - New URL hygiene tests cover trailing slash, query string, and fragment stripping in the configured URL. - New empty-search test asserts an empty query returns no results. - Opt-in live smoke test (skipped unless MAMMAMIRADIO_LIVE_URL is set) exercises handle_async_init, browse, and get_stream_details against a running addon for end-to-end verification.
Why: independent code review surfaced that the prior >=400 check still raised ProviderUnavailableError on a 405 GET response from Icecast mounts that require Icy headers, contradicting the cleanup commit's stated 405- tolerance goal. ffmpeg connects with the correct headers and plays the stream regardless of the bare-GET 405, so the probe should treat 405 as reachable. Updates the regression test to exercise the production code path (GET 405 returns reachable) instead of mocking head() that the code no longer calls. Drops the redundant head() fixture stub.
handle_async_init now raises ProviderUnavailableError on failure (was logging warning). get_stream_details no longer probes the stream URL — passes through to ffmpeg, matching NTS, RadioBrowser, and ORF Radiothek. Stream-URL probing is structurally unreliable for Icecast: GET pushes audio bytes the probe never consumes, HEAD returns 200 even when the source is offline (Xiph mailing list, March 2015). A dedicated /healthz endpoint at init is the only reliable liveness signal, matching MA's canonical pattern. Tests: 4 obsolete tests removed (offline-raises, http-error-raises, get-not-head, 405-tolerance). 1 new test asserts no probing happens at stream-time. 2 init-time tests now assert ProviderUnavailableError is raised on ClientError and HTTP error.
|
Again why this provider instead of adding the station manually? |
|
Thank you for the check-in, sorry for he lag - was finalizing the cleanup before reply. Manual URL works for basic playback, fair point. The reason for a provider is segment-aware metadata: |
|
OK sounds fair. Currently though this isn't doing much so I would keep going and propose your final version with all the metadata included. |
|
Helpful. Two ways I could go: (a) Close this and refile as one bundled PR with the metadata extension RFC + reference impl + provider together. Which feels cleaner from your side? |
|
Personally I think B is fine. |
Add mammamiradio music provider
mammamiradio is a self-hosted, AI-generated continuous Italian radio station with banter, music, and ads. This provider lets MA users add their mammamiradio HA addon as a music source alongside SomaFM, RadioBrowser, etc.
What it does
mammamiradio_url(defaulthttp://localhost:8100)Radioentry titled "mammamiradio" (one provider, one station)SUPPORTED_FEATURES = {BROWSE, SEARCH}(matches SomaFM)${mammamiradio_url}/stream(Icecast, MP3 128k)${url}/api/capabilities; a failure logs a warning but does NOT raise — the user can fix the URL in the UIget_stream_details()raisesProviderUnavailableErrorif the addon is unreachable at stream time, so MA shows "source unavailable" instead of crashing inside ffmpegSelf-hosted requirement
mammamiradio is fully self-hosted — there is no public stream. Users install the mammamiradio HA addon (or run the standalone Docker image) on their own network. This provider does not register or proxy any external service; it just points MA at the user's own running addon.
Tests
tests/providers/test_mammamiradio.pycovers the unit-test paths from the design doc (17 tests):handle_async_init: passes when reachable; logs but does not raise when unreachablebrowse(): returns the single Radio entrysearch(): exact match, substring (case-insensitive), no-match, wrong media typeget_radio(): valid id returns Radio; invalid id raisesMediaNotFoundErrorget_stream_details(): returnsContentType.MP3+ bitrate 128,${url}/streampath, raisesProviderUnavailableErroron offline/5xx, raisesMediaNotFoundErroron unknown idget_config_entries()returns one required STRING entry with the documented defaultE2E paths (discovery flow, configuration error, mid-stream addon stop) are covered by manual smoke testing during dev — MA's reconnect logic handles the mid-stream case at the player level, not the provider level.
Manual smoke checklist
Settings → Providers → Addmodalpre-commit run --all-filesgreenpytest tests/providers/test_mammamiradio.py)