morningalarm/internal/spotify.go

163 lines
3.7 KiB
Go

package morningalarm
import (
"context"
"encoding/json"
"fmt"
"math/rand"
"net/http"
"os"
"github.com/gin-gonic/gin"
"github.com/zmb3/spotify/v2"
spotifyauth "github.com/zmb3/spotify/v2/auth"
"golang.org/x/oauth2"
)
const redirectURI = "/api/spotifycb"
type headerTransport struct {
baseTransport http.RoundTripper
headers map[string]string
}
func (t *headerTransport) RoundTrip(req *http.Request) (*http.Response, error) {
for key, value := range t.headers {
req.Header.Set(key, value)
}
return t.baseTransport.RoundTrip(req)
}
func (ma *MorningAlarm) setupSpotifyWithProxy() {
client := &http.Client{
Transport: &headerTransport{
baseTransport: http.DefaultTransport,
headers: map[string]string{
"X-API-KEY": ma.config.OAuthProxyAPIKey,
},
},
}
ma.sp = spotify.New(client, spotify.WithBaseURL(ma.config.OAuthProxyURL))
}
func (ma *MorningAlarm) setupSpotify() error {
auth := spotifyauth.New(
spotifyauth.WithRedirectURL(ma.config.RedirectURL+redirectURI),
spotifyauth.WithClientID(ma.config.SpotifyClientID),
spotifyauth.WithClientSecret(ma.config.SpotifyClientSecret),
spotifyauth.WithScopes(
spotifyauth.ScopeUserReadPrivate,
spotifyauth.ScopeUserModifyPlaybackState,
spotifyauth.ScopeStreaming,
spotifyauth.ScopeUserReadPlaybackState,
),
)
loadedTokenJSON, err := os.ReadFile("spotifyToken.json")
if err == nil {
fmt.Println("Found access token in file")
var token oauth2.Token
err = json.Unmarshal(loadedTokenJSON, &token)
if err != nil {
return err
}
ma.sp = spotify.New(auth.Client(context.Background(), &token), spotify.WithRetry(true))
newToken, err := ma.sp.Token()
if err != nil {
return err
}
tokenJSON, err := json.Marshal(newToken)
if err != nil {
return err
}
os.WriteFile("spotifyToken.json", tokenJSON, os.ModePerm)
return nil
}
state := "abc123"
ma.ro.GET(redirectURI, func(c *gin.Context) {
token, err := auth.Token(c, state, c.Request)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
tokenJSON, err := json.Marshal(token)
os.WriteFile("spotifyToken.json", tokenJSON, os.ModePerm)
ma.sp = spotify.New(auth.Client(c, token))
})
url := auth.AuthURL(state)
fmt.Println("Please log in to Spotify by visiting the following page in your browser:", url)
return nil
}
// playWakeUpMusic plays the playlist specified in the config at the specified device.
// Returns true if the playback was started successfully.
// Returns error if any error occurred.
// Can still return an error even if the playback was started successfully.
func (ma *MorningAlarm) playWakeUpMusic() (bool, error) {
playContextURI := spotify.URI("spotify:playlist:" + ma.config.PlaylistID)
deviceID := spotify.ID(ma.config.DeviceID)
playlist, err := ma.sp.GetPlaylistItems(context.Background(), spotify.ID(ma.config.PlaylistID))
if err != nil {
return true, err
}
randomTrackIndex := rand.Intn(playlist.Total)
err = ma.sp.PlayOpt(context.Background(), &spotify.PlayOptions{
PlaybackContext: &playContextURI,
DeviceID: &deviceID,
PlaybackOffset: &spotify.PlaybackOffset{Position: randomTrackIndex},
})
if err != nil {
return true, err
}
err = ma.sp.ShuffleOpt(context.Background(), true, &spotify.PlayOptions{
DeviceID: &deviceID,
})
return true, err
}
func (ma *MorningAlarm) getAvailableDevices() ([]Device, error) {
devices, err := ma.sp.PlayerDevices(context.Background())
if err != nil {
return nil, err
}
var availableDevices []Device
for _, device := range devices {
if !device.Restricted {
availableDevices = append(availableDevices, Device{
ID: string(device.ID),
Name: device.Name,
})
}
}
return availableDevices, nil
}