Skip to content

Commit

Permalink
feat: support smart shuffle
Browse files Browse the repository at this point in the history
  • Loading branch information
devgianlu committed Nov 9, 2024
1 parent 85b1bb1 commit 73b2d59
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 5 deletions.
56 changes: 51 additions & 5 deletions cmd/daemon/player.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import (
"encoding/json"
"encoding/xml"
"fmt"
lenspb "github.com/devgianlu/go-librespot/proto/spotify/lens"
signalpb "github.com/devgianlu/go-librespot/proto/spotify/playlist/signal"
playlist4pb "github.com/devgianlu/go-librespot/proto/spotify/playlist4"
"io"
"net/url"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -303,12 +307,54 @@ func (p *AppPlayer) handlePlayerCommand(req dealer.RequestPayload) error {
return nil
}

p.state.player.ContextUrl = req.Command.Context.Url
p.state.player.ContextRestrictions = req.Command.Context.Restrictions
if p.state.player.ContextMetadata == nil {
p.state.player.ContextMetadata = map[string]string{}
}
for k, v := range req.Command.Context.Metadata {
p.state.player.ContextMetadata[k] = v
p.state.player.ContextMetadata = req.Command.Context.Metadata

if strings.HasPrefix(req.Command.Context.Url, "context://") {
parts := strings.Split(req.Command.Context.Url, "?")
if len(parts) > 1 {
query, err := url.ParseQuery(parts[1])
if err != nil {
return fmt.Errorf("failed to parse context URL query: %w", err)
}

applyLenses := query["spotify-apply-lenses"]
if len(applyLenses) > 0 {
if len(applyLenses) > 1 {
log.Warnf("ignoring multiple spotify-apply-lenses: %v", applyLenses)
}

lensBytes, err := proto.Marshal(&lenspb.Lens{Identifier: applyLenses[0]})
if err != nil {
return fmt.Errorf("failed to marshal lens: %w", err)
}

resp, err := p.sess.Spclient().PlaylistSignals(
librespot.SpotifyIdFromUri(p.state.player.ContextUri),
&playlist4pb.ListSignals{
BaseRevision: nil, // FIXME?
EmittedSignals: []*signalpb.Signal{{
Identifier: "reset",
Data: lensBytes,
}},
},
applyLenses,
)
if err != nil {
return fmt.Errorf("failed to list playlist signals: %w", err)
}

if p.state.player.ContextMetadata == nil {
p.state.player.ContextMetadata = make(map[string]string)
}
for _, attr := range resp.Attributes.FormatAttributes {
p.state.player.ContextMetadata[*attr.Key] = *attr.Value
}

p.state.tracks.ApplySelectedListContent(resp)
}
}
}

p.updateState()
Expand Down
22 changes: 22 additions & 0 deletions tracks/tracks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package tracks

import (
"fmt"
playlist4pb "github.com/devgianlu/go-librespot/proto/spotify/playlist4"
"slices"
"strconv"

librespot "github.com/devgianlu/go-librespot"
connectpb "github.com/devgianlu/go-librespot/proto/spotify/connectstate"
Expand Down Expand Up @@ -318,3 +320,23 @@ func (tl *List) ToggleShuffle(shuffle bool) error {
}
}
}

func (tl *List) ApplySelectedListContent(content *playlist4pb.SelectedListContent) {
for _, item := range content.Contents.Items {
spotId := librespot.SpotifyIdFromUri(item.GetUri())

Check failure on line 326 in tracks/tracks.go

View workflow job for this annotation

GitHub Actions / build

assignment mismatch: 1 variable but librespot.SpotifyIdFromUri returns 2 values
track := connectpb.ContextTrack{
Uri: spotId.Uri(),
Gid: spotId.Id(),
Metadata: map[string]string{
"added_at": strconv.FormatInt(item.Attributes.GetTimestamp(), 10),
"added_by_username": item.Attributes.GetAddedBy(),
},
}

for _, attr := range item.Attributes.FormatAttributes {
track.Metadata[*attr.Key] = *attr.Value
}

log.Infof("%v", &track) // TODO: no clue where these should be added
}
}

0 comments on commit 73b2d59

Please sign in to comment.