mirror of
https://github.com/skidoodle/spotify-ws.git
synced 2025-02-15 06:09:14 +01:00
Initial commit
This commit is contained in:
commit
d3d2cbd9d0
11 changed files with 1031 additions and 0 deletions
146
main.go
Normal file
146
main.go
Normal file
|
@ -0,0 +1,146 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/zmb3/spotify"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var (
|
||||
clients = make(map[*websocket.Conn]bool) // Map to keep track of connected clients
|
||||
broadcast = make(chan *spotify.CurrentlyPlaying) // Channel for broadcasting currently playing track
|
||||
upgrader = websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool { return true },
|
||||
}
|
||||
spotifyClient spotify.Client // Spotify API client
|
||||
tokenSource oauth2.TokenSource // OAuth2 token source
|
||||
config *oauth2.Config // OAuth2 configuration
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Load environment variables from .env file if not already set
|
||||
if os.Getenv("CLIENT_ID") == "" || os.Getenv("CLIENT_SECRET") == "" || os.Getenv("REFRESH_TOKEN") == "" {
|
||||
if err := godotenv.Load(); err != nil {
|
||||
log.Fatalf("Error loading .env file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
clientID := os.Getenv("CLIENT_ID")
|
||||
clientSecret := os.Getenv("CLIENT_SECRET")
|
||||
refreshToken := os.Getenv("REFRESH_TOKEN")
|
||||
|
||||
// Setup OAuth2 configuration for Spotify API
|
||||
config = &oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: clientSecret,
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "https://accounts.spotify.com/authorize",
|
||||
TokenURL: "https://accounts.spotify.com/api/token",
|
||||
},
|
||||
}
|
||||
|
||||
token := &oauth2.Token{RefreshToken: refreshToken}
|
||||
tokenSource = config.TokenSource(context.Background(), token)
|
||||
|
||||
// Create an OAuth2 HTTP client
|
||||
httpClient := oauth2.NewClient(context.Background(), tokenSource)
|
||||
spotifyClient = spotify.NewClient(httpClient)
|
||||
|
||||
// Handle WebSocket connections at the root endpoint
|
||||
http.HandleFunc("/", ConnectionHandler)
|
||||
|
||||
// Log server start-up and initialize background tasks
|
||||
log.Println("Server started on :3000")
|
||||
go TrackFetcher() // Periodically fetch currently playing track from Spotify
|
||||
go MessageHandler() // Broadcast messages to connected clients
|
||||
|
||||
// Start the HTTP server
|
||||
if err := http.ListenAndServe(":3000", nil); err != nil {
|
||||
log.Fatalf("Error starting server: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ConnectionHandler upgrades HTTP connections to WebSocket and handles communication with clients
|
||||
func ConnectionHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ws, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer ws.Close()
|
||||
clients[ws] = true
|
||||
|
||||
// Immediately send the current track to the newly connected client
|
||||
currentTrack, err := spotifyClient.PlayerCurrentlyPlaying()
|
||||
if err != nil {
|
||||
log.Printf("Error getting currently playing track: %v", err)
|
||||
ws.Close()
|
||||
delete(clients, ws)
|
||||
return
|
||||
}
|
||||
|
||||
// Send the current track information to the client
|
||||
err = ws.WriteJSON(currentTrack)
|
||||
if err != nil {
|
||||
ws.Close()
|
||||
delete(clients, ws)
|
||||
return
|
||||
}
|
||||
|
||||
// Keep the connection open to listen for incoming messages (heartbeat)
|
||||
for {
|
||||
_, _, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
delete(clients, ws)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MessageHandler continuously listens for messages on the broadcast channel and sends them to all connected clients
|
||||
func MessageHandler() {
|
||||
for {
|
||||
msg := <-broadcast
|
||||
for client := range clients {
|
||||
err := client.WriteJSON(msg)
|
||||
if err != nil {
|
||||
client.Close()
|
||||
delete(clients, client)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TrackFetcher periodically fetches the currently playing track from the Spotify API and broadcasts it to clients
|
||||
func TrackFetcher() {
|
||||
for {
|
||||
// Fetch the currently playing track
|
||||
current, err := spotifyClient.PlayerCurrentlyPlaying()
|
||||
if err != nil {
|
||||
log.Printf("Error getting currently playing track: %v", err)
|
||||
// Refresh the access token if it has expired
|
||||
if err.Error() == "token expired" {
|
||||
log.Println("Token expired, refreshing token...")
|
||||
newToken, err := tokenSource.Token()
|
||||
if err != nil {
|
||||
log.Fatalf("Couldn't refresh token: %v", err)
|
||||
}
|
||||
httpClient := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(newToken))
|
||||
spotifyClient = spotify.NewClient(httpClient)
|
||||
}
|
||||
// Wait before retrying to avoid overwhelming the API
|
||||
time.Sleep(30 * time.Minute)
|
||||
continue
|
||||
}
|
||||
// Broadcast the current track information to all clients
|
||||
broadcast <- current
|
||||
// Fetch track information every 5 seconds
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue