215 lines
5.7 KiB
Go
215 lines
5.7 KiB
Go
package spotify
|
|
|
|
import (
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
|
|
"git.gay/besties/yellowcab/db"
|
|
"git.gay/besties/yellowcab/scrape"
|
|
"github.com/joho/godotenv"
|
|
_ "github.com/mattn/go-sqlite3"
|
|
)
|
|
|
|
type spotify struct {
|
|
Name string
|
|
}
|
|
|
|
var (
|
|
SPOTIFY_ID string
|
|
SPOTIFY_SECRET string
|
|
bytes []byte
|
|
refresh_token string
|
|
err error
|
|
timestamp time.Time
|
|
)
|
|
|
|
func init() {
|
|
var err error = godotenv.Load()
|
|
if err != nil {
|
|
log.Fatal("Error loading .env file")
|
|
}
|
|
|
|
// spotify loading
|
|
SPOTIFY_ID = url.PathEscape(os.Getenv("SPOTIFY_ID"))
|
|
SPOTIFY_SECRET = url.PathEscape(os.Getenv("SPOTIFY_SECRET"))
|
|
}
|
|
|
|
func sendAuthedRequest(url string, bearer string) (resp *http.Response, err error) {
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
req.Header.Set("Authorization", "Bearer "+bearer)
|
|
resp, err = http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
log.Fatalf("couldn't fulfill request to %s: %v", url, err)
|
|
}
|
|
return
|
|
}
|
|
|
|
func Refresh(refresh_token string) (refreshResponse *SpotifyRefreshResponse) {
|
|
form := url.Values{}
|
|
form.Add("grant_type", "refresh_token")
|
|
form.Add("refresh_token", refresh_token)
|
|
form.Add("client_id", SPOTIFY_ID)
|
|
|
|
bearer := "Basic " + base64.StdEncoding.EncodeToString([]byte(SPOTIFY_ID+":"+SPOTIFY_SECRET))
|
|
|
|
req, err := http.NewRequest("POST", "https://accounts.spotify.com/api/token", strings.NewReader(form.Encode()))
|
|
req.Header.Set("Authorization", bearer)
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
log.Fatalf("couldn't get token: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
log.Fatalf("%s bad status code: %v", refresh_token, resp.StatusCode)
|
|
return
|
|
}
|
|
|
|
bytes, err = io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Fatalf("couldn't get io readall: %v", err)
|
|
}
|
|
|
|
refreshResponse = &SpotifyRefreshResponse{}
|
|
err = json.Unmarshal(bytes, refreshResponse)
|
|
if err != nil {
|
|
log.Fatalf("err decoding this shit: %v", err)
|
|
}
|
|
return refreshResponse
|
|
}
|
|
|
|
func makeErrorResp(main string, sub string) (response *scrape.ScrobblerResponse) {
|
|
response = &scrape.ScrobblerResponse{
|
|
FromScrobbler: scrape.FromScrobbler{
|
|
Name: Spotify.Name,
|
|
},
|
|
Cover: nil,
|
|
Album: nil,
|
|
Artist: &scrape.Artist{
|
|
Name: sub,
|
|
},
|
|
Track: scrape.Track{
|
|
Name: main,
|
|
URL: nil,
|
|
StreamURL: nil,
|
|
},
|
|
EstTimestamp: "live",
|
|
TimestampMs: 0,
|
|
}
|
|
return
|
|
}
|
|
|
|
func (s *spotify) itemToTrack(item SpotifyItem, ts int64) *scrape.ScrobblerResponse {
|
|
return &scrape.ScrobblerResponse{
|
|
FromScrobbler: scrape.FromScrobbler{
|
|
Name: s.Name,
|
|
},
|
|
Cover: &item.Album.Images[0].URL,
|
|
Album: &scrape.Album{
|
|
URL: &item.Album.ExternalUrls.Spotify,
|
|
Name: item.Album.Name,
|
|
},
|
|
Artist: &scrape.Artist{
|
|
URL: &item.Artists[0].ExternalUrls.Spotify,
|
|
Name: item.Artists[0].Name,
|
|
},
|
|
Track: scrape.Track{
|
|
Name: item.Name,
|
|
URL: &item.ExternalUrls.Spotify,
|
|
StreamURL: &item.ExternalUrls.Spotify,
|
|
},
|
|
EstTimestamp: "live",
|
|
TimestampMs: int64(ts),
|
|
}
|
|
}
|
|
|
|
func (s *spotify) GetNowPlaying(username string) (response *scrape.ScrobblerResponse, err error) {
|
|
var refresh_token string
|
|
|
|
if db.HasRefreshToken(username) {
|
|
refresh_token, err = db.GetRefreshToken(username)
|
|
refresh_token = url.PathEscape(refresh_token)
|
|
if err != nil {
|
|
response = makeErrorResp("Database error", "Error getting token from database")
|
|
return
|
|
}
|
|
} else {
|
|
response = makeErrorResp("Missing user authentication", "Log in @ "+os.Getenv("BASE_URL")+"/spotifyauth")
|
|
return
|
|
}
|
|
|
|
refreshResponse := Refresh(refresh_token)
|
|
|
|
err = db.SetRefreshToken(username, refreshResponse.RefreshToken)
|
|
if err != nil {
|
|
response = makeErrorResp("Database error", "Error setting refresh token in database")
|
|
return
|
|
}
|
|
|
|
currentlyPlayingHTTPResp, err := sendAuthedRequest("https://api.spotify.com/v1/me/player/currently-playing", refreshResponse.AccessToken)
|
|
if err != nil {
|
|
response = makeErrorResp("Error getting currently playing", "Error getting currently playing")
|
|
return
|
|
}
|
|
|
|
defer currentlyPlayingHTTPResp.Body.Close()
|
|
|
|
var currentlyPlayingResponse *SpotifyPlayingResponse
|
|
if currentlyPlayingHTTPResp.StatusCode == http.StatusOK {
|
|
bytes, err = io.ReadAll(currentlyPlayingHTTPResp.Body)
|
|
currentlyPlayingResponse := &SpotifyPlayingResponse{}
|
|
err = json.Unmarshal(bytes, currentlyPlayingResponse)
|
|
if err != nil {
|
|
log.Fatalf("err unmarshalling json: %v", err)
|
|
}
|
|
response = s.itemToTrack(*currentlyPlayingResponse.Item, int64(currentlyPlayingResponse.Timestamp))
|
|
return
|
|
}
|
|
|
|
if currentlyPlayingResponse == nil || !currentlyPlayingResponse.IsPlaying {
|
|
// not playing anything, check history
|
|
var lastPlayedHTTPResp *http.Response
|
|
lastPlayedHTTPResp, err = sendAuthedRequest("https://api.spotify.com/v1/me/player/recently-played?limit=1", refreshResponse.AccessToken)
|
|
if err != nil {
|
|
response = makeErrorResp("Error getting last played", "Error getting last played")
|
|
return
|
|
}
|
|
defer lastPlayedHTTPResp.Body.Close()
|
|
|
|
if lastPlayedHTTPResp.StatusCode != http.StatusOK {
|
|
response = makeErrorResp("Not currently listening to anything", "No listening history")
|
|
return
|
|
}
|
|
lastPlayedResponse := &SpotifyLastPlayed{}
|
|
var bytes []byte
|
|
bytes, err = io.ReadAll(lastPlayedHTTPResp.Body)
|
|
err = json.Unmarshal(bytes, lastPlayedResponse)
|
|
if err != nil {
|
|
log.Fatalf("err unmarshalling json: %v", err)
|
|
}
|
|
|
|
timestamp, err = time.Parse(time.RFC3339, lastPlayedResponse.Items[0].PlayedAt)
|
|
if err != nil {
|
|
log.Fatalf("err parsing time: %v", err)
|
|
}
|
|
response = s.itemToTrack(lastPlayedResponse.Items[0].Track, timestamp.Unix())
|
|
return
|
|
}
|
|
|
|
response = makeErrorResp("No hits - Not listening", "No listening history")
|
|
return
|
|
}
|
|
|
|
var Spotify = spotify{
|
|
Name: "Spotify",
|
|
}
|