/
/
# What does this implement/fix? Retires the built-in `local_audio` provider: local audio output now runs as a Sendspin add-on outside the server, so the implementation and its PortAudio/PipeWire dependencies go away. Addresses the review ask on this PR — remove the code and dependencies, prevent new installs, and give existing installs a message pointing at the add-on. ### A tombstone, not a delete A plain delete would be silent. `get_provider_configs` skips any config whose domain has no manifest (the "guard for deleted providers"), so deleting the package would make `local_audio` vanish and no existing user would ever see why. Instead the package is reduced to ~35 lines whose only job is to fail with a retirement notice. `setup()` raises `UnsupportedSystemError`. That is a `SetupFailedError` subclass mapping to `ProviderStatus.INCOMPATIBLE`, which `mass.py` treats as permanent and never retries, and which `EditProvider.vue` renders with a **Remove** button — the one action that actually resolves this for a user. No new error or status was added to `music-assistant/models`, and the models pin is untouched. Two manifest keys are load-bearing by their absence: - **`builtin` is gone** — it would otherwise re-create the config on every boot, and `remove_provider_config` outright refuses to remove builtin providers, so the Remove button we steer users toward would fail. - **`depends_on` is gone** — `_load_provider` returns early and silently when a dependency is not loaded, which would swallow the retirement error before `setup()` ever ran. ### Preventing new installs `setup_provider` aborts with a `provider_retired` notice when the manifest stage is `deprecated`, ahead of the existing already-configured and missing-dependency checks. The gate is keyed on the **stage**, not on the `local_audio` domain — a domain literal in `flows.py` would be worse, and this is the natural reading of `deprecated`. `SetupFlowDialog.vue` renders `step.reason` through `MarkdownText`, so the link renders as a link with no frontend change. ### Not shouting at people who never used it Because the provider was `builtin`, **every install that ever ran a version carrying it has a persisted `local_audio` config** — including everyone who never attached a soundcard. Raising unconditionally would paint a retirement warning on all of them. A migration drops the config where there are no `local_audio` player configs and leaves it in place otherwise, so only actual users see the notice. ### Dependencies removed `sounddevice` (via the regenerated `requirements_all.txt`), and every OS-level package that existed only for this provider: - **`Dockerfile.base`** — the `pipewire` package and the whole out-of-dpkg `pipewire-alsa` unpack workaround, plus the three PulseAudio routing snippets it removed to keep the ALSA default pointed at pipewire. That workaround's own comment said it "exists only for the local audio provider and can be dropped once that moves to its own dedicated container" — that condition is now met. `pulseaudio` and `pulseaudio-utils` **stay**: `helpers/pulse_capture.py` shells out to `pactl suspend-sink`, and the private capture server backs the Spotify soloist backends. The `pulseaudio-utils` comment, which read "(local audio)", is retagged to name `pulse_capture` instead. - **`libportaudio2`** — out of `Dockerfile.base`, the CI apt line, `DEVELOPMENT.md`, and the now-dead `check_native_lib sounddevice portaudio libportaudio2` probe in `scripts/setup.sh`, which would otherwise warn on every setup run. - **`tests/common.py`** loses `SUPPRESSED_BUILTIN_PROVIDERS` and its hermetic-boot wrapper, which existed only to keep this provider from bridging the host's sound devices into test runs. One thing worth a reviewer's eye: `tests/providers/sendspin/test_bridge_manager.py` imported `LocalAudioBridgeManager` and `get_device_uuid` from the deleted module. Its `TestLocalAudioBridgeManager` class is dropped with them; the rest of that file, which covers the shared bridge base and the Chromecast bridge, is untouched. ### Notes on the CI checks - **`provider-scope` advisory annotations** on `tests/integration/conftest.py`, `tests/controllers/config/test_setup_flows.py` and `tests/controllers/config/test_migrations.py` are expected and correct to ignore here. The check flags a provider PR touching shared code; in this case the setup-flow gate is deliberately generic (keyed on stage, not domain) and the config migration is a settings-schema concern, so both necessarily live outside the provider package — as do their tests. - **`Dependency Security Review`** needs a maintainer to add the `dependencies-reviewed` label. It fires on any `requirements_all.txt` change, and this one only **removes** a dependency. ### Out of scope, but needs doing before this merges `music-assistant.io` still documents `player-support/local-audio` as a live provider, and the manifest keeps pointing at it. That page should be retired and pointed at the add-on. Tracked separately. **Related issue (if applicable):** - companion frontend PR: https://github.com/music-assistant/frontend/pull/2629 ## Types of changes <!-- switched from `enhancement` on the first pass: this removes a working provider, so existing local_audio users lose functionality until they move to the add-on --> - [ ] Bugfix (non-breaking change which fixes an issue) — `bugfix` - [ ] New feature (non-breaking change which adds functionality) — `new-feature` - [ ] Enhancement to an existing feature — `enhancement` - [ ] New music/player/metadata/plugin provider — `new-provider` - [x] Breaking change (fix or feature that would cause existing functionality to not work as expected) — `breaking-change` - [ ] Refactor (no behaviour change) — `refactor` - [ ] Documentation only — `documentation` - [ ] Maintenance / chore — `maintenance` - [ ] CI / workflow change — `ci` - [ ] Dependencies bump — `dependencies` ## Checklist - [x] The code change is tested and works locally. - [x] `pre-commit run --all-files` passes. - [x] `pytest` passes, and tests have been added/updated under `tests/` where applicable. - [ ] For changes to shared models, the companion PR in `music-assistant/models` is linked. - [x] For changes affecting the UI, the companion PR in `music-assistant/frontend` is linked. - [x] I have read and complied with the project's [AI Policy](https://github.com/music-assistant/.github/blob/main/AI_POLICY.md) for any AI-assisted contributions. - [ ] I have [raised a PR against the documentation repository](https://github.com/music-assistant/music-assistant.io/blob/main/CONTRIBUTING.md) targeting the main or beta branch as appropriate. --------- Co-authored-by: Marvin Schenkel <[email protected]>