From 3c55e5a08d339ea67551d9bf9acbb3e6f9361026 Mon Sep 17 00:00:00 2001 From: Paul Lisker Date: Sun, 12 Jul 2026 22:26:28 -0500 Subject: [PATCH 1/3] fix: reset context metadata when loading a new context --- daemon/controls.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/daemon/controls.go b/daemon/controls.go index 302f92a8..b2cb68a8 100644 --- a/daemon/controls.go +++ b/daemon/controls.go @@ -263,9 +263,11 @@ func (p *AppPlayer) loadContext(ctx context.Context, spotCtx *connectpb.Context, } } - if p.state.player.ContextMetadata == nil { - p.state.player.ContextMetadata = map[string]string{} - } + // Replace (don't merge): this is a fresh context, so metadata from the + // previous one must not linger — otherwise keys the new context omits (e.g. + // context_description, which drives Spotify's "Next from:" label) keep their + // stale values. + p.state.player.ContextMetadata = map[string]string{} for k, v := range spotCtx.Metadata { p.state.player.ContextMetadata[k] = v } From 0d65609d8695eb8cfd7b9a58404db116ab0dfb78 Mon Sep 17 00:00:00 2001 From: Paul Lisker Date: Sun, 12 Jul 2026 22:49:47 -0500 Subject: [PATCH 2/3] fix: populate context metadata from the resolved context on load --- daemon/controls.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/daemon/controls.go b/daemon/controls.go index b2cb68a8..315d95bd 100644 --- a/daemon/controls.go +++ b/daemon/controls.go @@ -264,13 +264,18 @@ func (p *AppPlayer) loadContext(ctx context.Context, spotCtx *connectpb.Context, } // Replace (don't merge): this is a fresh context, so metadata from the - // previous one must not linger — otherwise keys the new context omits (e.g. - // context_description, which drives Spotify's "Next from:" label) keep their - // stale values. + // previous one must not linger. Copy the play command's metadata, then + // overlay the resolved context's metadata (ctxTracks.Metadata()), which + // carries context_description — the label Spotify renders as "Next from: …" + // (linked to ContextUri) — that the command alone usually omits. Mirrors the + // transfer path in player.go. p.state.player.ContextMetadata = map[string]string{} for k, v := range spotCtx.Metadata { p.state.player.ContextMetadata[k] = v } + for k, v := range ctxTracks.Metadata() { + p.state.player.ContextMetadata[k] = v + } p.state.player.Timestamp = time.Now().UnixMilli() p.state.player.PositionAsOfTimestamp = 0 From 22887400109faf865c0d4032d1226a6d99357e2d Mon Sep 17 00:00:00 2001 From: Paul Lisker Date: Sun, 12 Jul 2026 23:23:03 -0500 Subject: [PATCH 3/3] fix: report upcoming tracks from our own list on set_queue --- daemon/controls.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/daemon/controls.go b/daemon/controls.go index 315d95bd..bf163181 100644 --- a/daemon/controls.go +++ b/daemon/controls.go @@ -511,7 +511,15 @@ func (p *AppPlayer) setQueue(ctx context.Context, prev []*connectpb.ContextTrack p.state.tracks.SetQueue(prev, next) p.state.player.PrevTracks = p.state.tracks.PrevTracks() - p.state.player.NextTracks = p.state.tracks.NextTracks(ctx, next) + // Report upcoming tracks from our own list (queue + context iterator) rather + // than echoing the command's next_tracks hint. On a context switch the + // controller can send a set_queue whose next_tracks still lead with the + // previous context's leftovers; echoing them shows tracks under "Next from:" + // that will never play, since advanceNext follows this same list, not the + // hint. SetQueue above already captured the (is_queued) manual queue from the + // hint, so only the reorder-by-hint of context tracks is dropped — and that + // never affected playback anyway. + p.state.player.NextTracks = p.state.tracks.NextTracks(ctx, nil) p.updateState(ctx) // The upcoming track may have changed: a stream prefetched under the old