mirror of
https://github.com/skidoodle/spotify-ws
synced 2025-10-09 05:22:43 +02:00
105 lines
2.2 KiB
Go
105 lines
2.2 KiB
Go
package websocket
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"log/slog"
|
|
"sync"
|
|
"time"
|
|
|
|
"spotify-ws/internal/spotify"
|
|
)
|
|
|
|
// Poller is responsible for fetching data from the Spotify API periodically.
|
|
type Poller struct {
|
|
client *spotify.Client
|
|
hub *Hub
|
|
realtime bool
|
|
lastState *spotify.CurrentlyPlaying
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
// NewPoller creates a new Poller.
|
|
func NewPoller(client *spotify.Client, hub *Hub, realtime bool) *Poller {
|
|
return &Poller{
|
|
client: client,
|
|
hub: hub,
|
|
realtime: realtime,
|
|
}
|
|
}
|
|
|
|
// Run starts the polling loop.
|
|
func (p *Poller) Run(ctx context.Context) {
|
|
slog.Info("poller started")
|
|
defer slog.Info("poller stopped")
|
|
|
|
p.UpdateState(ctx)
|
|
|
|
ticker := time.NewTicker(3 * time.Second)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-ticker.C:
|
|
p.UpdateState(ctx)
|
|
}
|
|
}
|
|
}
|
|
|
|
// UpdateState fetches the latest state, compares it, and broadcasts if needed.
|
|
func (p *Poller) UpdateState(ctx context.Context) {
|
|
current, err := p.client.CurrentlyPlaying(ctx)
|
|
if err != nil {
|
|
slog.Error("failed to get currently playing track", "error", err)
|
|
return
|
|
}
|
|
|
|
p.mu.Lock()
|
|
hasChanged := p.hasStateChanged(current)
|
|
if hasChanged {
|
|
p.lastState = current
|
|
}
|
|
p.mu.Unlock()
|
|
|
|
if hasChanged {
|
|
if !p.realtime {
|
|
trackName := "Nothing"
|
|
if current.Item != nil {
|
|
trackName = current.Item.Name
|
|
}
|
|
slog.Info("state changed, broadcasting update", "isPlaying", current.IsPlaying, "track", trackName)
|
|
}
|
|
|
|
payload := newPlaybackState(current, p.realtime)
|
|
message, err := json.Marshal(payload)
|
|
if err != nil {
|
|
slog.Error("failed to marshal playback state", "error", err)
|
|
return
|
|
}
|
|
|
|
p.hub.broadcast <- message
|
|
}
|
|
}
|
|
|
|
// hasStateChanged performs a robust comparison between the new and old states.
|
|
func (p *Poller) hasStateChanged(current *spotify.CurrentlyPlaying) bool {
|
|
if p.lastState == nil {
|
|
return true
|
|
}
|
|
if p.realtime && current.IsPlaying && current.Item != nil {
|
|
return true
|
|
}
|
|
if p.lastState.IsPlaying != current.IsPlaying {
|
|
return true
|
|
}
|
|
if (p.lastState.Item == nil) != (current.Item == nil) {
|
|
return true
|
|
}
|
|
if p.lastState.Item != nil && current.Item != nil && p.lastState.Item.ID != current.Item.ID {
|
|
return true
|
|
}
|
|
return false
|
|
}
|