Skip to content

Instantly share code, notes, and snippets.

@bethropolis
Last active October 27, 2023 08:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bethropolis/ce5c20b8b3a753625b1ac1fa0562d199 to your computer and use it in GitHub Desktop.
Save bethropolis/ce5c20b8b3a753625b1ac1fa0562d199 to your computer and use it in GitHub Desktop.
a program for getting artist and songs from spotify playlist
package main
import (
"bufio"
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
)
type Track struct {
No int `json:"no"`
Song string `json:"song"`
Artist string `json:"artist"`
Cover string `json:"album_cover"`
}
func main() {
reader := bufio.NewReader(os.Stdin)
for {
authToken := GetAccessToken() // Use the function from spotify_auth.go to get the Spotify access token
fmt.Print("\nEnter the Spotify playlist ID: ")
playlistID, _ := reader.ReadString('\n')
playlistID = strings.TrimSpace(playlistID)
// Define the base URL for the Spotify API
baseURL := "https://api.spotify.com/v1/playlists/" + playlistID + "/tracks?offset=%d&limit=100"
// Create a slice to store the tracks
var trackData []Track
// Offset and limit for pagination
offset := 0
limit := 100
fmt.Println("\nFetching data...")
for {
// Make a GET request to the Spotify API
reqURL := fmt.Sprintf(baseURL, offset)
req, err := http.NewRequest("GET", reqURL, nil)
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Add("Authorization", "Bearer "+authToken) // Use the obtained access token
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
fmt.Println("Error: API request returned status", resp.Status)
return
}
// Parse the response and extract track data
var tracksResponse struct {
Items []struct {
AddedAt string `json:"added_at"`
AddedBy struct {
ID string `json:"id"`
} `json:"added_by"`
Track struct {
Name string `json:"name"`
Artists []struct {
Name string `json:"name"`
} `json:"artists"`
Album struct {
Images []struct {
URL string `json:"url"`
} `json:"images"`
} `json:"album"`
} `json:"track"`
} `json:"items"`
}
err = json.NewDecoder(resp.Body).Decode(&tracksResponse)
if err != nil {
fmt.Println("Error decoding response:", err)
return
}
// Extract and structure track data
for index, item := range tracksResponse.Items {
track := Track{
No: offset + index + 1,
Song: item.Track.Name,
Artist: item.Track.Artists[0].Name,
}
if len(item.Track.Album.Images) > 0 {
track.Cover = item.Track.Album.Images[0].URL
}
trackData = append(trackData, track)
}
// Check if there are more tracks to fetch
if len(tracksResponse.Items) < limit {
fmt.Printf("Fetched %d tracks. Done.\n", offset+len(tracksResponse.Items))
break
}
// Increment the offset for the next request
offset += limit
}
// Marshal the track data to JSON
jsonTrackData, err := json.MarshalIndent(trackData, "", " ")
if err != nil {
fmt.Println("Error marshalling track data:", err)
return
}
// Generate the JSON file name with the playlist ID
filename := "track_data_" + playlistID + ".json"
// Save the JSON data to a file
err = os.WriteFile(filename, jsonTrackData, 0644) // Use os.WriteFile instead of ioutil.WriteFile
if err != nil {
fmt.Println("Error saving track data to file:", err)
return
}
fmt.Printf("Track data saved to %s\n", filename)
// Ask the user if they want to fetch another playlist
fmt.Printf("\nDo you want to fetch another playlist? (yes/no): ")
answer, _ := reader.ReadString('\n')
answer = strings.TrimSpace(answer)
if answer != "yes" {
break
}
}
}
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
"sync"
"time"
)
var (
clientID = "SPOTIFY_CLIENT_TOKEN"
clientSecret = "SPOTIFY_CLIENT_SECRET"
authURL = "https://accounts.spotify.com/api/token"
authHeader string
token string
expiration time.Time
fetchMutex sync.Mutex
fetchOnce sync.Once
)
type SpotifyAuthResponse struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
}
func init() {
authHeader = base64.StdEncoding.EncodeToString([]byte(clientID + ":" + clientSecret))
}
func fetchAccessToken() error {
data := url.Values{}
data.Set("grant_type", "client_credentials")
body := strings.NewReader(data.Encode())
client := &http.Client{}
req, err := http.NewRequest("POST", authURL, body)
if err != nil {
return err
}
req.Header.Set("Authorization", "Basic "+authHeader)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("Failed to obtain Spotify access token. Status code: %d", resp.StatusCode)
}
var authResponse SpotifyAuthResponse
err = json.NewDecoder(resp.Body).Decode(&authResponse)
if err != nil {
return err
}
token = authResponse.AccessToken
expiration = time.Now().Add(time.Second * time.Duration(authResponse.ExpiresIn))
return nil
}
func GetAccessToken() string {
fetchOnce.Do(func() {
fmt.Println("Fetching new access token...")
err := fetchAccessToken()
if err != nil {
fmt.Println("Error fetching access token:", err)
}
})
fetchMutex.Lock()
defer fetchMutex.Unlock()
if time.Now().After(expiration) {
err := fetchAccessToken()
if err != nil {
fmt.Println("Error fetching access token:", err)
}
}
fmt.Printf("Obtained access token :) \n")
return token
}
@bethropolis
Copy link
Author

file structure

folder/
    main.go
    spotify_auth.go

in the above spotify_auth.go

replace the values for the variables below with your own

clientID     = "SPOTIFY_CLIENT_TOKEN"
clientSecret = "SPOTIFY_CLIENT_SECRET"

you can get your client token and client secret from developer.spotify.com

READ MORE HERE: https://developer.spotify.com/documentation/web-api/tutorials/code-flow


run the following in your folders directory:

initialize package:

go mod init spotifyPlaylist

run the program:

go run .

compile/build executable program:

go build .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment