Smoother daemon: no-op reloads, batched placement, muted audio, graceful teardown

- Skip stream restarts when a reload resolves to the identical wall
  (same URLs, tile geometry, player settings) — saving an unrelated
  config edit no longer blanks the screen.
- Replace per-tile placement polling (a swaymsg fork per tile per second,
  plus 150ms get_tree polling per tile at startup) with one shared loop:
  a single get_tree snapshot per tick, re-placing only drifted windows,
  relaxing to a 2s tick once settled.
- Mute streams by default (--no-audio) to skip an audio decoder per
  stream; opt back in with player.audio: true.
- Graceful, parallel mpv teardown via cmd.Cancel/WaitDelay, fixing the
  double-Wait race between Stop and Supervise and cutting worst-case
  layout switches from ~2s x N streams to ~2s total.
- status: probe mpv IPC outside the daemon mutex so a slow probe can't
  block layout switches.
- View sync: reuse the Protect session across ticks (re-login only on
  expiry) and pick up view_refresh_seconds changes without a restart.
- Health strikes keyed by player, not slot, so they never carry across
  layout switches; prune stale entries.
- New `rtsp-streamer reload` CLI; fix dead sort in `layout ls`; back off
  on persistent control-socket accept errors; fsync config before the
  atomic rename (SD-card power-cut safety); bump IPC client deadline;
  gofmt stragglers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Levi Woodard
2026-07-02 07:38:01 -05:00
parent 291d37ec83
commit 98ddf1645c
11 changed files with 325 additions and 140 deletions

View File

@@ -328,7 +328,8 @@ func layoutCmd() *cobra.Command {
names = append(names, l.Name)
}
sort.Strings(names)
for _, l := range cfg.Layouts {
for _, name := range names {
l := cfg.LayoutByName(name)
marker := " "
if l.Name == cfg.ActiveLayout {
marker = "* "
@@ -374,6 +375,27 @@ func layoutCmd() *cobra.Command {
return c
}
// reloadCmd tells the running daemon to re-read its config and re-apply the
// active layout. The daemon leaves streams untouched when nothing material
// changed, so this is safe to run casually.
func reloadCmd() *cobra.Command {
return &cobra.Command{
Use: "reload",
Short: "Reload the running daemon's config and re-apply the layout",
RunE: func(cmd *cobra.Command, _ []string) error {
resp, err := ipc.Send(ipc.Request{Cmd: "reload"})
if err != nil {
return err
}
if !resp.OK {
return fmt.Errorf("%s", resp.Error)
}
fmt.Printf("Reloaded. Active layout: %s (%d streams)\n", resp.ActiveLayout, len(resp.Slots))
return nil
},
}
}
// statusCmd asks the running daemon for slot health.
func statusCmd() *cobra.Command {
return &cobra.Command{