Skip to content

feat: Add MSX Bridge Player Provider#3123

Open
trudenboy wants to merge 62 commits intomusic-assistant:devfrom
trudenboy:feat/msx-bridge-player-provider
Open

feat: Add MSX Bridge Player Provider#3123
trudenboy wants to merge 62 commits intomusic-assistant:devfrom
trudenboy:feat/msx-bridge-player-provider

Conversation

@trudenboy
Copy link
Contributor

@trudenboy trudenboy commented Feb 9, 2026

Summary

New Player Provider that streams Music Assistant to Smart TVs via the Media Station X (MSX) app. Runs an embedded HTTP server (default port 8099) inside the MA process — no external containers or services required.

Smart TV (MSX App) ──HTTP/WS──▶ MSXBridgeProvider (port 8099) ──internal API──▶ MA core

Architecture

Provider Files

File Lines Purpose
__init__.py 142 setup(), get_config_entries(), feature flags
provider.py 703 MSXBridgeProvider(PlayerProvider): lifecycle, player registry, idle timeout, WS push, SharedGroupStream
player.py 381 MSXPlayer(Player): play/pause/stop/resume, position reporting, queue sync, echo prevention
http_server.py 2168 MSXHTTPServer: aiohttp server, 50 routes, WebSocket handler, audio streaming, CORS
mappers.py 224 MA media items → MSX JSON content objects, playlist builder
models.py 58 Pydantic models for MSX content pages (MSXContentPage, MSXContentItem)
constants.py 38 Config keys, defaults, regex patterns
manifest.json 12 Provider metadata
static/plugin.html 463 MSX interaction plugin (device ID detection, WebSocket client, menu)
static/input.html/js 1401 MSX Input Plugin (search keyboard)
static/web/ 2277 Browser-based web player (no MSX app needed)

HTTP Endpoints

MSX Bootstrap & Navigation:

Method Route Purpose
GET / Status dashboard
GET /msx/start.json MSX app entry point
GET /msx/plugin.html MSX interaction plugin
GET /msx/menu.json Main menu (Albums, Artists, Playlists, Tracks, Search)
GET /msx/albums.json Albums listing
GET /msx/artists.json Artists listing
GET /msx/playlists.json Playlists listing
GET /msx/tracks.json Tracks listing
GET /msx/recently-played.json Recently played
GET /msx/search-page.json Search UI with input plugin
GET /msx/search-input.json Search results from keyboard input
GET /msx/search.json Search results (API-style)

MSX Detail Pages:

Method Route Purpose
GET /msx/albums/{id}/tracks.json Album tracks
GET /msx/artists/{id}/albums.json Artist albums
GET /msx/playlists/{id}/tracks.json Playlist tracks

MSX Native Playlists (per-track streaming):

Method Route Purpose
GET /msx/queue-playlist/{player_id}.json MA queue → MSX playlist
GET /msx/playlist/album/{id}.json Album as playlist
GET /msx/playlist/playlist/{id}.json Playlist as playlist
GET /msx/playlist/tracks.json All tracks as playlist
GET /msx/playlist/recently-played.json Recently played as playlist
GET /msx/playlist/search.json Search results as playlist

Audio Streaming:

Method Route Purpose
GET /msx/audio/{player_id} Audio stream for MSX playback (PCM → ffmpeg → MP3/AAC/FLAC)
GET /stream/{player_id} Direct stream proxy (programmatic playback)

Library REST API:

Method Route Purpose
GET /api/albums, /api/artists, /api/playlists, /api/tracks Library browsing
GET /api/albums/{id}/tracks, /api/artists/{id}/albums, /api/playlists/{id}/tracks Detail endpoints
GET /api/search, /api/recently-played Search and history
GET /api/lyrics/{id} Track lyrics for current player
GET /api/queue/{id} Current playback queue
POST /api/play Start playback
ANY /api/pause/{id}, /api/stop/{id}, /api/next/{id}, /api/previous/{id} Playback control
ANY /api/quick-stop/{id} Immediate stop (abort stream + WS notify)

Other:

Method Route Purpose
GET /health Health check
GET /ws WebSocket endpoint
GET /web Browser-based web player

WebSocket Protocol

Server → Client (MA → MSX):

Type Payload Purpose
play path, title, artist, image_url, duration Playback started
playlist url Push MSX native playlist
goto_index index Jump to track in playlist
pause Pause playback
resume Resume playback
stop showNotification Stop playback

Client → Server (MSX → MA):

Type Payload Purpose
position position (seconds) Position reporting (3s interval)
pause position User paused on TV
resume User resumed on TV
debug_info data (device capabilities) Device diagnostics

Features

Dynamic Player Registration

  • TVs register as MA players on first HTTP request (device ID or IP-based)
  • Multi-TV support: each TV gets a unique player (msx_<device_id>)
  • Idle timeout cleanup (configurable, default 30 min)
  • Race condition handling via _pending_unregisters events
  • on_player_disabled override: keeps player registered (no re-discovery needed on enable)

Native Playlist Playback

  • MA builds playlist from queue, pushes to TV via WebSocket playlist message
  • Each track streams individually through /msx/audio/ — no flow mode
  • MSX controls prev/next navigation natively
  • Playlist rotation: clicked track always at index 0
  • goto_index for MA-initiated track changes
  • Skip re-enqueue when MA drives queue (prevents loops)

Bidirectional Sync

  • MSX → MA: Position reporting via WS (3s timer), pause/resume detection
  • MA → MSX: broadcast_pause/broadcast_resume notifications
  • Echo prevention: _skip_ws_notify flag prevents notification loops (wrapped in try/finally)
  • update_position() prefers WebSocket data over wall-clock polling

Audio Streaming Pipeline

MA Queue → mass.streams.get_stream() → raw PCM → get_ffmpeg_stream() → MP3/AAC/FLAC → HTTP → TV
  • Pre-buffer 64KB before sending HTTP headers (prevents MSX stutter)
  • Chunk queue maxsize=32 for network jitter tolerance
  • Content-Length from duration × bytes_per_sec for MSX progress bar
  • Three stream modes for groups: Independent, Shared Buffer, Redirect (scaffold)

Player Grouping (Experimental)

  • enable_player_grouping config toggle (default: true)
  • SharedGroupStream: one ffmpeg process, multiple TV readers via shared buffer
  • Late joiners receive buffered catch-up data under lock (race condition safe), then live stream
  • Recursion guard for group propagation
  • Parallel member notification

Browser Web Player

  • /web — full-featured player without MSX app
  • Library browsing, playback controls, keyboard shortcuts
  • Kiosk mode (/web?kiosk=1): immersive full-screen album art with auto-hiding controls
  • Karaoke lyrics overlay in kiosk mode
  • Queue panel with two-column layout (art+controls | queue)
  • Three-column kiosk layout (art | lyrics/equalizer | queue)
  • CSS equalizer animation
  • Sendspin audio mode integration (scaffold, disabled by default)

Configuration

Key Type Default Description
http_port int 8099 HTTP server port
output_format enum mp3 Audio format (MP3, AAC, FLAC)
player_idle_timeout int 30 Minutes before unregistering idle players
show_stop_notification bool false Show notification on MSX when stopping
abort_stream_first bool false Cancel stream before WS stop (vs. after)
enable_player_grouping bool true Enable SYNC_PLAYERS / SET_MEMBERS features
group_stream_mode enum independent Group streaming: Independent / Shared Buffer

Testing

121 unit tests (120 passed + 1 skipped):

Test File Tests Coverage
test_http_server.py 53 Routes, content pages, audio streaming, WS, queue API, CORS
test_player.py 42 Play/pause/stop/resume, position, queue sync, groups
test_provider.py 9 Registration, idle timeout, lifecycle, group streams
test_init.py 6 Config entries, setup, feature flags
test_playlist.py 5 Playlist mapping, rotation, serialization
test_models.py 4 Pydantic models, aliases, serialization
test_mappers.py 2 Track formatting, content mapping

Pre-commit: ruff ✅, mypy ✅


Screenshots

MSX Album View MSX Player View

Links


🤖 Generated with Claude Code

@github-actions
Copy link
Contributor

github-actions bot commented Feb 9, 2026

🔒 Dependency Security Report

✅ No dependency changes detected in this PR.

@trudenboy trudenboy force-pushed the feat/msx-bridge-player-provider branch from 03aa67a to bdc1dac Compare February 9, 2026 15:55
@trudenboy trudenboy marked this pull request as ready for review February 9, 2026 17:31
@OzGav
Copy link
Contributor

OzGav commented Feb 9, 2026

Ensure you run pre-commit before pushing any commits.

@trudenboy trudenboy marked this pull request as draft February 9, 2026 23:54
@trudenboy trudenboy force-pushed the feat/msx-bridge-player-provider branch from 3ec7d87 to 5dbb940 Compare February 10, 2026 00:01
@trudenboy trudenboy marked this pull request as ready for review February 10, 2026 00:02
Михаил Невский and others added 3 commits February 10, 2026 03:46
Co-authored-by: Cursor <cursoragent@cursor.com>
…ayback

For flow_mode (multi-track queue), Content-Length was set to the first track's
size. MSX player closed after receiving that many bytes, so the next track
never started. Now we omit Content-Length for flow streams so the client
keeps the connection open across tracks.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@trudenboy trudenboy force-pushed the feat/msx-bridge-player-provider branch from 7b15d21 to 0a098e9 Compare February 10, 2026 00:46
@trudenboy trudenboy marked this pull request as draft February 10, 2026 09:54
Co-authored-by: Cursor <cursoragent@cursor.com>
trudenboy added a commit to trudenboy/ma-server that referenced this pull request Feb 10, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
trudenboy and others added 4 commits February 11, 2026 02:13
Add 6 new configuration options for Yandex Music provider:
- My Wave maximum tracks (default: 150) - Control total number of tracks fetched
- My Wave batch count (default: 3) - Number of API calls for initial load
- Track details batch size (default: 50) - Batch size for track detail requests
- Discovery initial tracks (default: 5) - Initial display limit for Discover
- Browse initial tracks (default: 15) - Initial display limit for Browse
- Enable Discover (default: true) - Toggle recommendations on/off

Implemented duplicate protection for My Wave tracks using set-based tracking.
Recommendations now refresh every 60 seconds instead of 3 hours for fresher discoveries.

All new settings have sensible defaults that maintain current behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ck streaming

Replace flow-mode streaming with MSX native playlists: tracks now use
playlist:auto: actions that load /msx/playlist/ endpoints, enabling MSX
to manage sequential playback natively while each track streams individually
via force_flow_mode=False. Add .mp3 extension routes for TV compatibility,
next/prev actions in WS push, and from_playlist flag to suppress duplicate
WS notifications. Remove flow-mode poll logic, extract Pydantic models and
mappers into dedicated modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@trudenboy trudenboy force-pushed the feat/msx-bridge-player-provider branch from 0120c04 to 6fbcf2d Compare February 11, 2026 08:35
@trudenboy trudenboy marked this pull request as ready for review February 11, 2026 08:45
* feat: Add MSX Bridge Player Provider

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(msx_bridge): omit Content-Length for flow streams to fix queue playback

For flow_mode (multi-track queue), Content-Length was set to the first track's
size. MSX player closed after receiving that many bytes, so the next track
never started. Now we omit Content-Length for flow streams so the client
keeps the connection open across tracks.

Co-authored-by: Cursor <cursoragent@cursor.com>

* style(msx_bridge): fix Ruff TC006 runtime-cast-value

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(msx_bridge): cover play_update websocket and flow track metadata

Co-authored-by: Cursor <cursoragent@cursor.com>

* ⬆️ Update music-assistant-frontend to 2.17.84 (music-assistant#3135)

Co-authored-by: marcelveldt <6389780+marcelveldt@users.noreply.github.com>

* feat(msx_bridge): switch to MSX native playlist playback with per-track streaming

Replace flow-mode streaming with MSX native playlists: tracks now use
playlist:auto: actions that load /msx/playlist/ endpoints, enabling MSX
to manage sequential playback natively while each track streams individually
via force_flow_mode=False. Add .mp3 extension routes for TV compatibility,
next/prev actions in WS push, and from_playlist flag to suppress duplicate
WS notifications. Remove flow-mode poll logic, extract Pydantic models and
mappers into dedicated modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tests): correct import path in test_models.py for CI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Михаил Невский <renso@MacBook-Pro-Mihail.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: music-assistant-machine <141749843+music-assistant-machine@users.noreply.github.com>
Co-authored-by: marcelveldt <6389780+marcelveldt@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
@trudenboy trudenboy marked this pull request as draft February 11, 2026 09:24
…cursion guard, and mypy fixes

- Add enable_player_grouping config option (default: true) to disable
  grouping entirely — removes SET_MEMBERS/SYNC_PLAYERS features
- Add recursion guard (_propagating flag) in _propagate_to_group_members
- Simplify _get_group_member_ids: use self.group_members directly
- Skip propagation when grouping_enabled=False at provider level
- Resolve all mypy errors in test files (type: ignore annotations)
- Add 7 new tests for disable, recursion guard, and feature flags
@trudenboy trudenboy force-pushed the feat/msx-bridge-player-provider branch from 32e6af0 to af59778 Compare February 11, 2026 12:51
@trudenboy trudenboy marked this pull request as ready for review February 11, 2026 13:33
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new MSX Bridge player provider to Music Assistant, enabling Smart TV playback via the Media Station X (MSX) app (HTTP content endpoints + WebSocket control), along with a bundled web UI and a comprehensive test suite.

Changes:

  • Introduce the MSX Bridge provider/player implementation, including grouping/shared-stream scaffolding and MSX content models/mappers.
  • Add MSX/TV plugin + web client static assets for browsing/playback and device identification.
  • Add extensive unit tests covering provider lifecycle, player behavior (queue/playlist sync, grouping), mappers/models, and HTTP server routes.

Reviewed changes

Copilot reviewed 22 out of 26 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/providers/msx_bridge/init.py Marks the MSX Bridge test package.
tests/providers/msx_bridge/conftest.py Adds fixtures for provider/player/http client testing.
tests/providers/msx_bridge/test_http_server.py Tests HTTP routes, MSX content endpoints, and WS inbound handling.
tests/providers/msx_bridge/test_init.py Tests provider setup and config entry behavior/feature flags.
tests/providers/msx_bridge/test_mappers.py Tests mapping MA media items to MSX items/actions.
tests/providers/msx_bridge/test_models.py Tests Pydantic model alias serialization.
tests/providers/msx_bridge/test_player.py Tests MSXPlayer playback, queue playlist sync, grouping propagation, and position reporting.
tests/providers/msx_bridge/test_playlist.py Tests queue/track list → MSX playlist content mapping.
tests/providers/msx_bridge/test_provider.py Tests provider lifecycle methods and player enable/disable behavior.
music_assistant/providers/msx_bridge/init.py Provider entry point: setup + config entries (including grouping/stream mode).
music_assistant/providers/msx_bridge/constants.py Adds provider constants and config keys/defaults.
music_assistant/providers/msx_bridge/icon.svg Adds provider icon asset.
music_assistant/providers/msx_bridge/icon_monochrome.svg Adds monochrome provider icon asset.
music_assistant/providers/msx_bridge/manifest.json Registers the provider manifest metadata.
music_assistant/providers/msx_bridge/mappers.py Implements mapping helpers for MSX content pages/items and playlist audio actions.
music_assistant/providers/msx_bridge/models.py Adds Pydantic models for MSX JSON content structures.
music_assistant/providers/msx_bridge/player.py Implements MSXPlayer with WS sync, queue playlist behavior, and grouping propagation.
music_assistant/providers/msx_bridge/provider.py Implements provider lifecycle, player registration/timeout, stop/pause/resume notifications, and shared-group stream manager.
music_assistant/providers/msx_bridge/static/input.html Adds MSX input plugin HTML wrapper.
music_assistant/providers/msx_bridge/static/input.js Adds vendored MSX input interaction plugin JS.
music_assistant/providers/msx_bridge/static/plugin.html Adds MSX InteractionPlugin bridge (menu + WS control + device ID).
music_assistant/providers/msx_bridge/static/web/index.html Adds browser-based MSX UI shell (normal + kiosk modes).
music_assistant/providers/msx_bridge/static/web/web.js Adds browser-based MSX UI logic + WS sync + optional Sendspin kiosk integration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI and others added 2 commits February 16, 2026 21:31
…ation, remove dead code (#50)

* Initial plan

* Port changes from trudenboy/msx-music-assistant#2 to msx_bridge provider

Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>
…types

Resync provider.py and constants.py from local project to fix CI errors:
- Restore is_redirect_stream_mode() and get_ma_stream_url() methods
- Use cast("int", ...) instead of int(raw_value) for MA config convention
- Restore GROUP_STREAM_MODE_REDIRECT constant import
- Fix mypy errors: redundant cast, unused type-ignore, unreachable code
- Fix WebserverController attribute access (use base_url property)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 16, 2026 18:59
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 26 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…dio URLs

- Remove hardcoded .mp3 extension from audio URLs in mappers (use extensionless)
- Fix XSS in web player msxIcon() by escaping user-controlled icon names
- Add server-side handler for debug_info WebSocket messages (was logging unknown)
- Document redirect stream mode as scaffold for future MA Streamserver integration
- Remove unused type: ignore comments for get_player() (valid in PR context)
- Update test assertion to match extensionless audio URLs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
trudenboy added a commit to trudenboy/ma-server that referenced this pull request Feb 16, 2026
…am-prs

Incorporates PR music-assistant#3123 review fixes:
- Remove hardcoded .mp3 from audio URLs (mappers.py)
- Add debug_info WS message handler (http_server.py)
- XSS fix: escape icon names in msxIcon() (web.js)
- Restore redirect mode scaffold methods with clear docstrings (provider.py)
- Keep GROUP_STREAM_MODE_REDIRECT constant active for http_server.py references

Conflict resolution: integration branch removed redirect dead code, but
http_server.py still references is_redirect_stream_mode() and get_ma_stream_url().
Kept both methods with docstrings documenting them as future scaffolds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@trudenboy trudenboy marked this pull request as draft February 17, 2026 09:24
trudenboy and others added 6 commits February 17, 2026 20:22
…ler compat

PlayerController exposes get() not get_player(). Fixed in provider.py and
http_server.py (7 call sites). Also removed .mp3 extension from audio action
URLs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…iding controls

- Full-bleed background image with blur/scale/vignette overlay
- Always-visible track info overlay (top-left, fades in on play)
- Auto-hiding frosted-glass controls panel (slide-down after 3.5s idle)
- Cursor hidden in idle, shown on interaction
- Idle state with music note icon when nothing is playing
- resetKioskHideTimer() / cancelKioskHideTimer() / setKioskPlaying() helpers
- mousemove, touchstart, click listeners trigger controls reveal

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@trudenboy trudenboy marked this pull request as ready for review February 17, 2026 18:49
Copilot AI review requested due to automatic review settings February 17, 2026 18:49
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 26 changed files in this pull request and generated 9 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@trudenboy trudenboy marked this pull request as draft February 17, 2026 19:12
trudenboy and others added 3 commits February 17, 2026 23:49
… get_player compat

- feat(web): karaoke lyrics overlay in kiosk mode
- fix(msx_bridge): use get_player() for PlayerController compat
- fix(web): lyrics display, autoplay, and player protocol issues
- fix(web): bypass autoplay restriction via muted-then-unmute trick

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sync with msx-music-assistant main: queue API endpoint, two-column
full player (art+controls | queue), three-column kiosk layout
(art | lyrics/equalizer | queue), CSS equalizer animation, and
responsive breakpoints. Also fix linting: split _setup_routes,
remove unused type: ignore comments, add Track type guard for lyrics.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…anup

- Lock buffer snapshot in SharedGroupStream.subscribe() to prevent deque mutation race
- Wrap _skip_ws_notify in try/finally to ensure flag reset on error
- Apply esc() in msxIcon() to prevent XSS
- Remove hardcoded .mp3 extension in WS play handlers (web.js, plugin.html)
- Remove redundant list() wrapping in player.py
- Add license header to input.js
- Fix misleading comment in test_provider.py
- Add queue API tests (test_http_server.py)
- Split _setup_routes() into _setup_msx_routes() and _setup_api_routes()
- Remove unused type: ignore comments (mypy cleanup)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@trudenboy trudenboy marked this pull request as ready for review February 17, 2026 22:48
Copilot AI review requested due to automatic review settings February 17, 2026 22:48
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 26 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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