WIP playlist sync

This commit is contained in:
2026-05-02 23:56:33 +02:00
parent 54d2195b76
commit 44a486aa46
5 changed files with 416 additions and 0 deletions

View File

@@ -0,0 +1,113 @@
package playlist
import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
)
const spotifyAPIBase = "https://api.spotify.com/v1"
type SpotifyTokenResponse struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
}
type SpotifyPlaylist struct {
Name string `json:"name"`
Description string `json:"description"`
Public bool `json:"public"`
Images []struct {
Url string `json:"url"`
Height int `json:"height"`
Width int `json:"width"`
} `json:"images"`
Items struct {
Limit int `json:"limit"`
Offset int `json:"offset"`
Total int `json:"total"`
Items []struct {
Item SpotifyTrack `json:"item"`
} `json:"items"`
} `json:"items"`
ExternalURLs struct {
Spotify string `json:"spotify"`
} `json:"external_urls"`
}
type SpotifyTrack struct {
ID string `json:"id"`
Name string `json:"name"`
Artists []struct {
Name string `json:"name"`
} `json:"artists"`
ExternalIDs struct {
Isrc string `json:"isrc"`
} `json:"external_ids"`
}
func GetSpotifyAccessToken(clientID, clientSecret string) (string, error) {
creds := base64.StdEncoding.EncodeToString([]byte(clientID + ":" + clientSecret))
data := url.Values{}
data.Set("grant_type", "client_credentials")
req, err := http.NewRequest("POST", "https://accounts.spotify.com/api/token",
strings.NewReader(data.Encode()))
if err != nil {
return "", err
}
req.Header.Set("Authorization", "Basic "+creds)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("auth failed (%d): %s", resp.StatusCode, body)
}
var token SpotifyTokenResponse
if err := json.Unmarshal(body, &token); err != nil {
return "", err
}
return token.AccessToken, nil
}
func GetPlaylist(token, playlistID string) (*SpotifyPlaylist, error) {
// TODO: handle rate limit
// TODO: handle request limits
reqURL := fmt.Sprintf("%s/playlists/%s", spotifyAPIBase, playlistID)
req, err := http.NewRequest("GET", reqURL, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API error (%d): %s", resp.StatusCode, body)
}
var playlist SpotifyPlaylist
if err := json.Unmarshal(body, &playlist); err != nil {
return nil, err
}
return &playlist, nil
}