Skip to content

Commit 7a2a06a

Browse files
committed
feat: support smart shuffle
1 parent d977b75 commit 7a2a06a

File tree

2 files changed

+73
-5
lines changed

2 files changed

+73
-5
lines changed

cmd/daemon/player.go

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import (
55
"encoding/json"
66
"encoding/xml"
77
"fmt"
8+
lenspb "github.com/devgianlu/go-librespot/proto/spotify/lens"
9+
signalpb "github.com/devgianlu/go-librespot/proto/spotify/playlist/signal"
10+
playlist4pb "github.com/devgianlu/go-librespot/proto/spotify/playlist4"
811
"io"
12+
"net/url"
913
"strconv"
1014
"strings"
1115
"sync"
@@ -305,12 +309,54 @@ func (p *AppPlayer) handlePlayerCommand(req dealer.RequestPayload) error {
305309
return nil
306310
}
307311

312+
p.state.player.ContextUrl = req.Command.Context.Url
308313
p.state.player.ContextRestrictions = req.Command.Context.Restrictions
309-
if p.state.player.ContextMetadata == nil {
310-
p.state.player.ContextMetadata = map[string]string{}
311-
}
312-
for k, v := range req.Command.Context.Metadata {
313-
p.state.player.ContextMetadata[k] = v
314+
p.state.player.ContextMetadata = req.Command.Context.Metadata
315+
316+
if strings.HasPrefix(req.Command.Context.Url, "context://") {
317+
parts := strings.Split(req.Command.Context.Url, "?")
318+
if len(parts) > 1 {
319+
query, err := url.ParseQuery(parts[1])
320+
if err != nil {
321+
return fmt.Errorf("failed to parse context URL query: %w", err)
322+
}
323+
324+
applyLenses := query["spotify-apply-lenses"]
325+
if len(applyLenses) > 0 {
326+
if len(applyLenses) > 1 {
327+
log.Warnf("ignoring multiple spotify-apply-lenses: %v", applyLenses)
328+
}
329+
330+
lensBytes, err := proto.Marshal(&lenspb.Lens{Identifier: applyLenses[0]})
331+
if err != nil {
332+
return fmt.Errorf("failed to marshal lens: %w", err)
333+
}
334+
335+
resp, err := p.sess.Spclient().PlaylistSignals(
336+
librespot.SpotifyIdFromUri(p.state.player.ContextUri),
337+
&playlist4pb.ListSignals{
338+
BaseRevision: nil, // FIXME?
339+
EmittedSignals: []*signalpb.Signal{{
340+
Identifier: "reset",
341+
Data: lensBytes,
342+
}},
343+
},
344+
applyLenses,
345+
)
346+
if err != nil {
347+
return fmt.Errorf("failed to list playlist signals: %w", err)
348+
}
349+
350+
if p.state.player.ContextMetadata == nil {
351+
p.state.player.ContextMetadata = make(map[string]string)
352+
}
353+
for _, attr := range resp.Attributes.FormatAttributes {
354+
p.state.player.ContextMetadata[*attr.Key] = *attr.Value
355+
}
356+
357+
p.state.tracks.ApplySelectedListContent(resp)
358+
}
359+
}
314360
}
315361

316362
p.updateState()

tracks/tracks.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package tracks
22

33
import (
44
"fmt"
5+
playlist4pb "github.com/devgianlu/go-librespot/proto/spotify/playlist4"
56
"slices"
7+
"strconv"
68

79
librespot "github.com/devgianlu/go-librespot"
810
connectpb "github.com/devgianlu/go-librespot/proto/spotify/connectstate"
@@ -312,3 +314,23 @@ func (tl *List) ToggleShuffle(shuffle bool) error {
312314
}
313315
}
314316
}
317+
318+
func (tl *List) ApplySelectedListContent(content *playlist4pb.SelectedListContent) {
319+
for _, item := range content.Contents.Items {
320+
spotId := librespot.SpotifyIdFromUri(item.GetUri())
321+
track := connectpb.ContextTrack{
322+
Uri: spotId.Uri(),
323+
Gid: spotId.Id(),
324+
Metadata: map[string]string{
325+
"added_at": strconv.FormatInt(item.Attributes.GetTimestamp(), 10),
326+
"added_by_username": item.Attributes.GetAddedBy(),
327+
},
328+
}
329+
330+
for _, attr := range item.Attributes.FormatAttributes {
331+
track.Metadata[*attr.Key] = *attr.Value
332+
}
333+
334+
log.Infof("%v", &track) // TODO: no clue where these should be added
335+
}
336+
}

0 commit comments

Comments
 (0)