Skip to content

Commit e361485

Browse files
b0bbywanclaude
andcommitted
fix(mpris): ignore duplicate TrackAdded signals
Some bridges emit TrackAdded twice for a single queue insert, which duplicated the track in the cache. Track IDs are unique within a tracklist per spec, so a TrackAdded for an already-cached ID is a duplicate and is now a no-op. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AKi5652rX9cdUDycdU1btS
1 parent 31bc63d commit e361485

2 files changed

Lines changed: 25 additions & 3 deletions

File tree

backend/mpris/tracklist.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,19 @@ func (m *MPRISBackend) ReplaceTracklist(busName string, tracks []Track) error {
9595
}
9696

9797
// AddTrackToCache inserts a track after afterTrack (TrackAdded signal).
98+
// Track IDs are unique within a tracklist per spec, so a TrackAdded for an
99+
// already-cached ID is a duplicate signal and is ignored.
98100
func (m *MPRISBackend) AddTrackToCache(busName string, track Track, afterTrack string) error {
99101
return m.mutateTracklist(busName, func(p *Player) bool {
100102
// A player emitting TrackList signals supports the interface even if
101103
// the initial GetAll failed (lazy init).
102104
p.TracklistSupported = true
105+
for i := range p.Tracklist {
106+
if p.Tracklist[i].TrackID == track.TrackID {
107+
logger.Debug("[mpris] ignoring duplicate TrackAdded %s for %s", track.TrackID, busName)
108+
return false
109+
}
110+
}
103111
p.Tracklist = insertTrack(p.Tracklist, track, afterTrack)
104112
return true
105113
})

backend/mpris/tracklist_test.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,16 +177,26 @@ func TestAddTrackToCache(t *testing.T) {
177177
tests := []struct {
178178
name string
179179
busName string
180+
trackID string // defaults to /track/new
180181
afterTrack string
181182
wantIDs []string
182183
wantErr bool
184+
noop bool // duplicate signal: nothing stored, supported flag untouched
183185
}{
184186
{
185187
name: "prepend via NoTrack sentinel",
186188
busName: testBus,
187189
afterTrack: MPRIS_NO_TRACK,
188190
wantIDs: []string{"/track/new", "/track/1", "/track/2"},
189191
},
192+
{
193+
name: "duplicate TrackAdded for a cached ID is a no-op",
194+
busName: testBus,
195+
trackID: "/track/1",
196+
afterTrack: "/track/2",
197+
wantIDs: []string{"/track/1", "/track/2"},
198+
noop: true,
199+
},
190200
{
191201
name: "insert after existing track",
192202
busName: testBus,
@@ -214,7 +224,11 @@ func TestAddTrackToCache(t *testing.T) {
214224
Tracklist: append([]Track{}, initial...),
215225
})
216226

217-
err := b.AddTrackToCache(tt.busName, newTrack, tt.afterTrack)
227+
track := newTrack
228+
if tt.trackID != "" {
229+
track.TrackID = tt.trackID
230+
}
231+
err := b.AddTrackToCache(tt.busName, track, tt.afterTrack)
218232
if (err != nil) != tt.wantErr {
219233
t.Fatalf("AddTrackToCache error = %v, wantErr %v", err, tt.wantErr)
220234
}
@@ -223,8 +237,8 @@ func TestAddTrackToCache(t *testing.T) {
223237
}
224238

225239
p, _ := b.GetPlayerFromCache(testBus)
226-
if !p.TracklistSupported {
227-
t.Error("AddTrackToCache should mark the player as tracklist-supported")
240+
if p.TracklistSupported == tt.noop {
241+
t.Errorf("TracklistSupported = %v: an insert should set it, a no-op should store nothing", p.TracklistSupported)
228242
}
229243
assertTrackIDs(t, p.Tracklist, tt.wantIDs)
230244
})

0 commit comments

Comments
 (0)