No description
  • Dart 92.5%
  • CMake 4.4%
  • C++ 2.7%
  • C 0.3%
Find a file
schattenan 07622d53ac Fix repeat progress bar and unify track-list "now playing" cue
- Progress bar no longer sticks at full when a track repeats: a sustained
  overshoot on a still-playing track (HA leaves media_position pinned at
  the end) wraps and counts again, while a brief overshoot still shows
  full so normal track transitions look right.
- Album/playlist/artist track rows now listen to the playback controller
  and highlight the currently playing track. Matching is by ratingKey from
  media_content_id with a title fallback, shared with the queue panel so
  duplicate titles don't both light up.
- Unify the "now playing" indicator into a single PlayingArtThumb widget
  (art + animated equalizer overlay) used by every art-based row, so the
  playlist/search/queue views all match the album view instead of three
  divergent implementations.

analyze clean, 13 tests passing, Linux build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 13:11:50 +02:00
android Initial scaffold: Linux+Android Flutter app with HA/Plex/Sonos backend layer 2026-06-13 12:41:14 +02:00
lib Fix repeat progress bar and unify track-list "now playing" cue 2026-06-14 13:11:50 +02:00
linux Build rich Plex→Sonos player UI on the backend layer 2026-06-13 14:10:55 +02:00
test Fix repeat progress bar and unify track-list "now playing" cue 2026-06-14 13:11:50 +02:00
.gitignore Initial scaffold: Linux+Android Flutter app with HA/Plex/Sonos backend layer 2026-06-13 12:41:14 +02:00
.metadata Initial scaffold: Linux+Android Flutter app with HA/Plex/Sonos backend layer 2026-06-13 12:41:14 +02:00
analysis_options.yaml Initial scaffold: Linux+Android Flutter app with HA/Plex/Sonos backend layer 2026-06-13 12:41:14 +02:00
CHANGELOG.md Fix repeat progress bar and unify track-list "now playing" cue 2026-06-14 13:11:50 +02:00
CLAUDE.md Build rich Plex→Sonos player UI on the backend layer 2026-06-13 14:10:55 +02:00
pubspec.lock Build rich Plex→Sonos player UI on the backend layer 2026-06-13 14:10:55 +02:00
pubspec.yaml Build rich Plex→Sonos player UI on the backend layer 2026-06-13 14:10:55 +02:00
README.md Support non-Sonos (Cast/DLNA) media players 2026-06-14 11:58:20 +02:00

Plex Sonos Caster

A rich, animated Flutter music app (Linux + Android) that browses your Plex library and casts it to your Sonos speakers — a single speaker or every speaker at once — orchestrated through Home Assistant.

The app talks to your Plex Media Server directly for metadata and artwork, and uses Home Assistant to group Sonos speakers and drive playback.

Features

  • Browse your music library: albums, artists, and playlists, with a recently-added shelf and disk-cached album art.
  • Search across artists, albums, and songs with live results.
  • Cast any album, artist, playlist, or track to a chosen speaker or to all Sonos at once (whole-home grouping). Non-Sonos HA media players (Google Cast / DLNA, e.g. an LG soundbar) are supported too.
  • Full transport controls: play/pause, stop, next/previous, shuffle, repeat, seek, and group volume — each enabled per the target's reported capabilities.
  • Now playing: album-art-driven gradient ambience, a live-ticking progress bar, and a persistent mini-player docked above the navigation.
  • Up Next queue: see the tracks you cast and which one is playing.
  • Ratings: Plex star ratings (5 stars, half-star precision) shown across the library and the player.
  • Device picker that detects already-grouped Sonos zones.

How it works

┌──────────────┐   browse / search    ┌──────────────────┐
│              │ ───────────────────▶ │  Plex Media      │
│  Flutter app │                      │  Server (direct) │
│  (this repo) │                      └──────────────────┘
│              │   discover · group · play_media
│              │ ───────────────────▶ ┌──────────────────┐
└──────────────┘                      │  Home Assistant  │ ──▶ Sonos speakers
                                       └──────────────────┘
  1. Discover Sonos speakers — media_player entities exposing a group_members attribute.
  2. Group them into one coordinator via media_player.join (when casting everywhere).
  3. Cast a Plex item to the coordinator with media_player.play_media, using a plex://<machineId>/<ratingKey> content id that HA's Plex integration resolves and streams to the whole group.
  4. Poll the coordinator's media_player state for the now-playing view.

Project layout

lib/
  main.dart                         App entry; wires controllers + theme
  src/
    backends.dart                   Barrel export for the backend layer
    config/                         url + token value objects
    services/
      home_assistant_backend.dart   REST client (states + service calls)
      plex_backend.dart             REST client (search / browse / artwork)
      sonos_service.dart            Discovery, grouping, casting, playback state
      settings_service.dart         On-device persistence (shared_preferences)
    ui/
      app_scope.dart                Inherited access to the controllers
      theme.dart                    Dark theme, colors, gradients, motion
      navigation.dart               Fade/slide page transitions
      controllers/                  connection · library · playback (ChangeNotifier)
      screens/                      root · settings · library · search · detail ·
                                    now-playing · main shell
      widgets/                      album art · cards · track tile · mini player ·
                                    device picker · queue sheet · stars · …

The backend layer (src/config, src/services) has no Flutter dependency — plain Dart, independently unit-testable. The UI layer talks to it only through the service classes.

Configuration

Two connections are entered in-app on first run and persisted locally:

Setting Example Notes
Home Assistant URL http://homeassistant.local:8123
Home Assistant token long-lived access token Profile → Security
Plex URL http://192.168.1.10:32400
Plex token X-Plex-Token
Music library chosen from a dropdown pick Music vs. Audiobooks etc.

Tap Test connections to verify reachability and load the list of music libraries. Local HTTP (cleartext) traffic is enabled in the Android manifest so on-network servers work without TLS.

Casting format: HA's Sonos integration resolves Plex media addressed as plex://<plexMachineIdentifier>/<ratingKey> (see SonosService.buildPlexContentId). A bare JSON payload returns HTTP 200 but plays nothing; a plex://{json} payload returns HTTP 500. The machine id is read from the Plex server via PlexBackend.machineId().

Speakers

  • Sonos is first-class: whole-home grouping ("Everywhere") and full transport.
  • Google Cast / DLNA players (e.g. an LG soundbar) work as single targets. HA's Cast integration resolves the same plex:// id, so casting works — but many Cast endpoints only expose play/pause/stop/seek/volume, not skip/shuffle/repeat. The app reads each player's supported_features and greys out controls a device can't do. Such players also often publish no artwork; the app recovers it from Plex.

Limitations

  • Sonos/HA does not expose its live queue over REST, so "Up Next" reflects the collection you cast. For a shuffled cast the songs are correct but the displayed order is the original collection order, not HA's random order.
  • Skip/shuffle/repeat on non-Sonos devices depend on what the player exposes to HA; many Cast devices don't expose them, and there is no client-side queue engine to work around that.
  • Now-playing ratings are looked up from Plex by title, so they appear a moment after the track changes.

Development

flutter pub get
flutter run -d linux            # desktop
flutter run -d <android-device> # Android
flutter analyze
flutter test

Acknowledgements

The Home Assistant and Plex REST clients are adapted from angela-dart.