Git Product home page Git Product logo

spotify's Introduction

Spotify

GoDoc

This is a Go wrapper for working with Spotify's Web API.

It aims to support every task listed in the Web API Endpoint Reference, located here.

By using this library you agree to Spotify's Developer Terms of Use.

Installation

To install the library, simply

go get github.com/zmb3/spotify/v2

Authentication

Spotify uses OAuth2 for authentication and authorization.
As of May 29, 2017 all Web API endpoints require an access token.

You can authenticate using a client credentials flow, but this does not provide any authorization to access a user's private data. For most use cases, you'll want to use the authorization code flow. This package includes an Authenticator type to handle the details for you.

Start by registering your application at the following page:

https://developer.spotify.com/my-applications/.

You'll get a client ID and secret key for your application. An easy way to provide this data to your application is to set the SPOTIFY_ID and SPOTIFY_SECRET environment variables. If you choose not to use environment variables, you can provide this data manually.

// the redirect URL must be an exact match of a URL you've registered for your application
// scopes determine which permissions the user is prompted to authorize
auth := spotifyauth.New(spotifyauth.WithRedirectURL(redirectURL), spotifyauth.WithScopes(spotifyauth.ScopeUserReadPrivate))

// get the user to this URL - how you do that is up to you
// you should specify a unique state string to identify the session
url := auth.AuthURL(state)

// the user will eventually be redirected back to your redirect URL
// typically you'll have a handler set up like the following:
func redirectHandler(w http.ResponseWriter, r *http.Request) {
      // use the same state string here that you used to generate the URL
      token, err := auth.Token(r.Context(), state, r)
      if err != nil {
            http.Error(w, "Couldn't get token", http.StatusNotFound)
            return
      }
      // create a client using the specified token
      client := spotify.New(auth.Client(r.Context(), token))

      // the client can now be used to make authenticated requests
}

You may find the following resources useful:

  1. Spotify's Web API Authorization Guide: https://developer.spotify.com/web-api/authorization-guide/

  2. Go's OAuth2 package: https://godoc.org/golang.org/x/oauth2/google

Helpful Hints

Automatic Retries

The API will throttle your requests if you are sending them too rapidly. The client can be configured to wait and re-attempt the request. To enable this, set the AutoRetry field on the Client struct to true.

For more information, see Spotify rate-limits.

API Examples

Examples of the API can be found in the examples directory.

You may find tools such as Spotify's Web API Console or Rapid API valuable for experimenting with the API.

Missing data in responses

It's extremely common that when there is no market information available in your request, that the Spotify API will simply return null for details about a track or episode.

This typically occurs when you are just using an application's auth token, and aren't impersonating a user via oauth. As when you are using a token associated with a user, the user's market seems to be extracted from their profile and used when producing the response.

spotify's People

Contributors

adamo57 avatar aditya-k2 avatar ahmed-deftoner avatar alexrs avatar brandur avatar brodin avatar bturrubiates avatar conradludgate avatar coolbaluk avatar dependabot[bot] avatar donfros avatar dsoprea avatar elivlo avatar elliotwms avatar junnishimura avatar lukehobbs avatar obrhoff avatar pebbe avatar pixelrazor avatar ptrkrlsrd avatar r0qs3t avatar roelrymenants avatar rtrox avatar samuelrey avatar strideynet avatar tobiola avatar wernerdweight avatar wndhydrnt avatar xonstone avatar zmb3 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spotify's Issues

Ascii codes in authentication Uri

Hey,

I've tried using this wrapper for authentication. It seems to work fine except for the redirectURL and state which contain a hexadecimal representation of their special characters:

auth = spotify.NewAuthenticator("http://localhost:4200/create", spotify.ScopeUserModifyPlaybackState)
config := config.ReadConfig()
auth.SetAuthInfo(config.ClientId, config.ClientSecret)
code := rest_objects.AuthorizationCode{Uri: auth.AuthURL(time.Now().Format(time.RFC3339))}
println(code.Uri)

Which prints: https://accounts.spotify...redirect_uri=http%3A%2F%2Flocalhost%3A4200%2Fcreate&...&state=2018-04-30T14%3A04%3A39%2B02%3A00

I am currently replacing these substrings but is there a way to bypass this in the first place?

Spotify refresh token after 1 hour expiry

Realize this is not so much an issue with this Spotify lib, but curious if anyone has got Spotify's OAUTH token to "refresh" using the Go Ouath2 library?

I have implemented a pair of helper functions to writeToken and readToken that caches a json blob containing the auth token to disk. I am doing this so that the user isn't forced to continually authorize access to Spotify. As it stands Spotify has a very short token expiry (1 hour) but they do provide a refresh token when authenticated. Documented here -> https://developer.spotify.com/web-api/authorization-guide/#authorization_code_flow

@zmb3 if you have attempted such an oauth flow using the refresh token, would you mind adding this to the example?

Token error

Hi,

I'm playing with the player example but I have some issue sending request just after I logging or sending 2 commands
The error I got is sometimes

2017/10/07 12:18:21 oauth2: cannot fetch token: 400 Bad Request
Response: {"error":"invalid_grant","error_description":"Authorization code expired"}

or

2017/10/07 12:21:51 oauth2: cannot fetch token: 400 Bad Request Response: {"error":"invalid_grant","error_description":"Invalid authorization code"}

Is there an issue with oauth2 lib ?

Error when authenticating user

Running the auth example of the library returns the following error:

2017/01/13 23:32:10 Post https://accounts.spotify.com/api/token: stream error: stream ID 1; REFUSED_STREAM

UPDATE:
If it is executed with GODEBUG=http2client=0 it seems to work. This is the output I get when I set GODEBUG=http2debug=2:

2017/01/13 23:33:57 http2: Transport failed to get client conn for accounts.spotify.com:443: http2: no cached connection was available
2017/01/13 23:33:57 http2: Transport creating client conn 0xc4200901a0 to 194.132.196.226:443
2017/01/13 23:33:57 http2: Framer 0xc4202f5800: wrote SETTINGS len=18, settings: ENABLE_PUSH=0, INITIAL_WINDOW_SIZE=4194304, MAX_HEADER_LIST_SIZE=10485760
2017/01/13 23:33:57 http2: Framer 0xc4202f5800: wrote WINDOW_UPDATE len=4 (conn) incr=1073741824
2017/01/13 23:33:57 http2: Transport encoding header ":authority" = "accounts.spotify.com"
2017/01/13 23:33:57 http2: Transport encoding header ":method" = "POST"
2017/01/13 23:33:57 http2: Transport encoding header ":path" = "/api/token"
2017/01/13 23:33:57 http2: Transport encoding header ":scheme" = "https"
2017/01/13 23:33:57 http2: Transport encoding header "content-type" = "application/x-www-form-urlencoded"
2017/01/13 23:33:57 http2: Transport encoding header "authorization" = "Basic NDEzZGY5N2FlOTZmNDliODgxN2Q1MzIyNzg4YjI0YzI6NDM5Mzk3Zjg1OGM4NGE3ZWJmOTUzMzFmZjA2NjA3ZDY="
2017/01/13 23:33:57 http2: Transport encoding header "content-length" = "379"
2017/01/13 23:33:57 http2: Transport encoding header "accept-encoding" = "gzip"
2017/01/13 23:33:57 http2: Transport encoding header "user-agent" = "Go-http-client/2.0"
2017/01/13 23:33:57 http2: Framer 0xc4202f5800: wrote HEADERS flags=END_HEADERS stream=1 len=158
2017/01/13 23:33:57 http2: Framer 0xc4202f5800: wrote DATA stream=1 len=379 data="client_id=MY_ID&code=AQAGHoYEEByHqEm9ecc22P9zqQTegw5D89DIswksVSmMcNFTcje5neDqJlNRpmxRyBihut-LCd7pLZr9vw5ltoXM2hRtTcE8yPnOFHEpzfhHxrsNOV-NnW77HMQku1X291AXOI8434rpv8t-B-5RxB62jJ9BTtumniPvSqondV1nG-UfaEoYsYNsN3VS3P17K96TknTlE9NSgmzX" (123 bytes omitted)
2017/01/13 23:33:57 http2: Framer 0xc4202f5800: wrote DATA flags=END_STREAM stream=1 len=0 data=""
2017/01/13 23:33:57 http2: Framer 0xc4202f5800: read SETTINGS len=18, settings: MAX_CONCURRENT_STREAMS=128, INITIAL_WINDOW_SIZE=0, MAX_FRAME_SIZE=16777215
2017/01/13 23:33:57 http2: Transport received SETTINGS len=18, settings: MAX_CONCURRENT_STREAMS=128, INITIAL_WINDOW_SIZE=0, MAX_FRAME_SIZE=16777215
2017/01/13 23:33:57 http2: Framer 0xc4202f5800: wrote SETTINGS flags=ACK len=0
2017/01/13 23:33:57 http2: Framer 0xc4202f5800: read WINDOW_UPDATE len=4 (conn) incr=2147418112
2017/01/13 23:33:57 http2: Transport received WINDOW_UPDATE len=4 (conn) incr=2147418112
2017/01/13 23:33:57 http2: Framer 0xc4202f5800: read SETTINGS flags=ACK len=0
2017/01/13 23:33:57 http2: Transport received SETTINGS flags=ACK len=0
2017/01/13 23:33:57 http2: Framer 0xc4202f5800: read RST_STREAM stream=1 len=4 ErrCode=REFUSED_STREAM
2017/01/13 23:33:57 http2: Transport received RST_STREAM stream=1 len=4 ErrCode=REFUSED_STREAM
2017/01/13 23:33:57 RoundTrip failure: stream error: stream ID 1; REFUSED_STREAM
2017/01/13 23:33:57 Post https://accounts.spotify.com/api/token: stream error: stream ID 1; REFUSED_STREAM
exit status 1

HTTP 204 for Client.PlayerStateOpt()

From previous issues, it looks like the HTTP 204 error has already been dealt with when it comes to playback functions, but I'm currently getting it for Client.PlayerStateOpt().

Simply substituting Client.get() with Client.execute() in Client.PlayerStateOpt() won't work, as it will result in an EOF error when reading the Body on a 204.

Since HTTP 204 seems to be a standard part of Spotify's API, could we add a check to Client.get() before decoding the Body that returns nil if the status code is 204?

Alternatively, if you don't want to mess with the Client.get() code, returning an exported error type that includes the HTTP status code would be nice.

TransferPlayback doesn't work as intended.

Hi. I have recently tried to use TransferPlayback method without success. Whenever I run the code below, the change of track in the PlayOpt method works, but it does not switch playback device. I start Spotify on my phone and begin playing music. I run the code, and song switches, but it doesn't change playback device to my computer. Do I have to do something else or am I just missing something?

devices, err := client.PlayerDevices()
	if err != nil {
		log.Print("L72: ", err)
	}

	for _, device := range devices {
		if device.Name == "VictorsMacbookAir" {
			fmt.Println(device.ID)
			err = client.TransferPlayback(device.ID, true)
			if err != nil {
				log.Print("L79: ", err)
			}
			w.Header().Set("Content-Type", "text/plain")
			fmt.Fprintf(w, "Login completed, transfering playback to: %s", device.Name)
			break
		}
	}

	songs := []spotify.URI{"spotify:track:7BfiAzV3ASEK9egnfxN1v1"}
	options := &spotify.PlayOptions{
		URIs: songs,
	}
	err = client.PlayOpt(options)
	if err != nil {
		log.Print("L93: ", err)
	}

Best regards,
Victor

Client returns 'spotify: HTTP 204: No Content ' as an error

Up until recently it worked fine, but now some calls return the following error:

spotify: HTTP 204: No Content (body empty)

Which is not really an error if you ask me ;)

Ive identified at least these functions to do this:

p.client.PlayOpt(&spotify.PlayOptions{
	URIs: []spotify.URI{URI},
})
p.client.Repeat("off")
p.client.Shuffle(false)

But there might be more?

x509: certificate signed by unknown authority

Hello,

While trying to receive a token, to make subsequent requests, I get the following error :

2019/01/13 18:33:09 couldn't get token: Post https://accounts.spotify.com/api/token: x509: certificate signed by unknown authority

This of course implies that the certificate, that is offered, is signed by a (to my computer) unknown authority.
However, this did work sometime in November last year. So spotify changed something up I'm guessing.

Would you have an Idea how to get this working, besides authorizing the certificate?

The following code was used:

config := &clientcredentials.Config{
	ClientID:     os.Getenv("SPOTIFY_ID"),
	ClientSecret: os.Getenv("SPOTIFY_SECRET"),
	TokenURL:     spotify.TokenURL,
}
token, err := config.Token(context.Background())
if err != nil {
	log.Fatalf("couldn't get token: %v", err)
}

client := spotify.Authenticator{}.NewClient(token)

NewReleasesOpt: Unnecessary complexity?

It seems like we're cutting ContentLength-bytes out of the body, loading it into a Buffer, and then passing that to the JSON-parser when we could just do what we did everywhere else and parse resp.Body directly. No?

Current:

func (c *Client) NewReleasesOpt(opt *Options) (albums *SimpleAlbumPage, err error) {
    spotifyURL := baseAddress + "browse/new-releases"
    if opt != nil {
        v := url.Values{}
        if opt.Country != nil {
            v.Set("country", *opt.Country)
        }
        if opt.Limit != nil {
            v.Set("limit", strconv.Itoa(*opt.Limit))
        }
        if opt.Offset != nil {
            v.Set("offset", strconv.Itoa(*opt.Offset))
        }
        if params := v.Encode(); params != "" {
            spotifyURL += "?" + params
        }
    }
    resp, err := c.http.Get(spotifyURL)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    if resp.StatusCode != http.StatusOK {
        return nil, decodeError(c, resp)
    }
    buf := bytes.NewBuffer(make([]byte, 0, resp.ContentLength+1))
    _, err = buf.ReadFrom(resp.Body)
    if err != nil {
        return nil, err
    }
    body := buf.Bytes()
    var objmap map[string]*json.RawMessage
    err = json.Unmarshal(body, &objmap)
    if err != nil {
        return nil, err
    }
    var result SimpleAlbumPage
    err = json.Unmarshal(*objmap["albums"], &result)
    if err != nil {
        return nil, err
    }
    return &result, nil
}

Proposed:

func (c *Client) NewReleasesOpt(opt *Options) (albums *SimpleAlbumPage, err error) {
    spotifyURL := baseAddress + "browse/new-releases"
    if opt != nil {
        v := url.Values{}
        if opt.Country != nil {
            v.Set("country", *opt.Country)
        }
        if opt.Limit != nil {
            v.Set("limit", strconv.Itoa(*opt.Limit))
        }
        if opt.Offset != nil {
            v.Set("offset", strconv.Itoa(*opt.Offset))
        }
        if params := v.Encode(); params != "" {
            spotifyURL += "?" + params
        }
    }
    resp, err := c.http.Get(spotifyURL)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    if resp.StatusCode != http.StatusOK {
        return nil, decodeError(c, resp)
    }
    var objmap map[string]*json.RawMessage
    err = json.NewDecoder(resp.Body).Decode(&objmap)
    if err != nil {
        return nil, err
    }

    var result SimpleAlbumPage
    err = json.Unmarshal(*objmap["albums"], &result)
    if err != nil {
        return nil, err
    }
    return &result, nil
}

invalid username for PlayerCurrentlyPlaying()

Hello!

Simply trying to retrieve the current playing song, feel like I am running into a wall with understanding the auth mechanisms here. Maybe it is that to get the currently playing song of my own account it is considered private data? and thus client credentials don't allow it?

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/zmb3/spotify"
	"golang.org/x/oauth2/clientcredentials"
)

func main() {
	config := &clientcredentials.Config{
		ClientID:     os.Getenv("SPOTIFY_ID"),
		ClientSecret: os.Getenv("SPOTIFY_SECRET"),
		TokenURL:     spotify.TokenURL,
		Scopes:       []string{spotify.ScopeUserReadCurrentlyPlaying},
	}

	token, err := config.Token(context.Background())
	if err != nil {
		log.Fatalf("unable to get token: %v", err)
	}

	client := spotify.Authenticator{}.NewClient(token)

	song, err := client.PlayerCurrentlyPlaying()
	if err != nil {
		log.Fatalf("unable to get currently playing song: %v", err)
	}

	fmt.Println(song)
}

The error here is Invalid username

Any help would be appreciated.

Cheers!

Paging

It seems that e.g. getPlaylist doesn't support the paging features of the Spotify API. As Spotify limits output per page to 100, this means that the wrapper will only retrieve the first 100 items of a playlist or album. The following pages are inaccessibly through the wrapper.

Question regarding authentication

Is there any way to do the authentication process completely inside go, without having to use an external browser for the login part? I could not find any working solution so far.

TrackAttributes as exported struct

Hi, thanks for a great library wrapper!
I wonder about the TrackAttributes struct, wouldn't it be better/simpler to map a simple struct against the spotify API? at least export the fields so it can easily decode from json.
Now it is quite awkward to construct.
In my particular usecase I fetch some json trackattributes that I just want to send to the recommendation seed, and I dont see the point in looping and testing for every property. In the end its just json objects both ways.

No way to play songid

You have alot of functions but i cant find a function to play a specific songID?
Is there something i have not seen or is this not possible?

PlayerCurrentlyPlaying returns err io.EOF when not playing

Sometimes, when there's no player registered (I suppose), the API returns an EOF. The token is correct and the error can be ignored.

	if ftl, err := client.PlayerCurrentlyPlaying(); err != nil {
		if err != io.EOF {
			log.Fatal(err)
		}
	} else {
		if !ftl.Playing {
			log.Println("Currently not playing...")
			return
		}
		// Do whatever
	}

Library doesn't allow other arguments to be in the redirect-URI

I'm assigning a redirect-URI like:

redirectUrl := "http://urlecho.appspot.com/echo?debugMode=1"
auth := spotify.NewAuthenticator(redirectUrl, spotify.ScopeUserReadPrivate)

..but the produced authorization URL looks like:

https://accounts.spotify.com/authorize?client_id=CLIENTID&redirect_uri=http%3A%2F%2Furlecho.appspot.com%2Fecho&response_type=code&scope=user-read-private&state=arbitrary-state-data

When I add the argument to the URL directly, Spotify accepts it (though I have to make sure to white-list the whole thing, including the query portion) and then redirects to it. So, it is definitely supported.

Play a specific music

Hello,

I would like to know how I could play a specific music with your library.
Thanks.

Follow type

This line is buggy
https://github.com/zmb3/spotify/blob/master/user.go#L210

ids parameter value is missing. and the type is mandatory.
https://developer.spotify.com/web-api/follow-artists-users/

this works
spotifyURL := baseAddress + "me/following?type=artist&ids=" + strings.Join(toStringSlice(ids), ",")

I can submit a PR for this, but want to know what do you consider being the best way of tackling this.

Both Follow and UNfollow use the same modifyFollowers and take ids as varaidic arguments.
Shoud them take type as an argument? I think so. Following multiple people of different types would probably fail. SHould it be documented?

Add example to get items from next page

Currently trying to list the tracks on my playlist but I can't seem to get more than 100 which is the maximum items per page returned by spotify, now how do I fetch the next page of tracks?

I think this could be added to the search example or add a new example that lists all tracks (I can make a pr for that if I can get it working).

My current code

    // list songs from playlist
    songs, err := client.GetPlaylistTracks(user.ID, playlistID)
    if err != nil {
        log.Fatal(err)
    }

    for _, item := range songs.Tracks {
        fmt.Printf("%s: %s (%s)\n", item.Track.ID, item.Track.Name, item.Track.Artists[0].Name)
    }

    fmt.Printf("This playlist has: %d songs (listed %d)\n", songs.Total, len(songs.Tracks))
    // This one outputs: `This playlist has: 144 songs (listed 100)`

Transfer Playback Multiple IDs

I might just be missing something, but why should TransferPlayback take a slice of deviceIDs rather than just a single ID to transfer playback to?

func (c *Client) TransferPlayback(deviceIDs []ID, play bool) error {

I am still trying to figure out what is causing it, but the TransferPlayback method is very finicky for me and seems to only work when I run it in between a song change or if I start the playback through the app and not the API.

Edit: It seems like I can transfer the playback between devices but the playback gets stuck on my desktop PC running Windows 10 and then I'm unable to transfer playback from this device. This might be just an issue with my desktop client... If so I can close this if the slice of deviceIDs is purposeful.

Bad Gateway?

When I am executing my app I am receiving a Bad Gateway on a regular basis, but not on every run. The app is simply searching for tracks and manipulating playlists. Has anyone else seen this before? Retries are turned on within the API client.

Adding Client credentials as authorisation option

I am trying to use your extremely useful package for a report I am writing, However the report is run as a daily job and must connect using client credentials https://developer.spotify.com/web-api/authorization-guide/#client-credentials-flow. Currently you support Authorization flow. Just wondering if its possible to use Client Credentials with your package. Using the oauth2/clientcredentials package it seems straight forward to create an authorised client. But I cannot assign it to a spotify client. Any suggestions.

import (
	"context"
	"fmt"
	"github.com/zmb3/spotify"
	"golang.org/x/oauth2/clientcredentials"
)

func main() {
		config := &clientcredentials.Config{
			ClientID:     "CLIENTID",
			ClientSecret: "SECRET",
			TokenURL:     "https://accounts.spotify.com/api/token",
	}
	//Creates an authorized *http.client
	AuthorizedClient := config.Client(context.Background())
	// need to assign authorizedClient to spotify.client

Allow HTTP Client and Base URL to be overrided on instantiation

I have an application that consists of two components: a persistent server that takes care of authenticating with Spotify (it stores creds and refreshes them after authenticating once), and a CLI that gets existing creds from this auth server and uses that token to make Spotify API calls. This lets me control Spotify from the command line without re-authenticating frequently. Currently the CLI portion of the app is written in Ruby while the server is in Go. I'm translating the CLI to Go right now and was hoping to use this library. However, the implementation of the Client struct here won't let me bypass this library's auth mechanism. I believe I could use this library if the Client.http field was public โ€“ I would create my own client with an http.RoundTripper that simply adds the authorization field.

Any chance these fields could be exposed?

Segmentation violation when using PlayOpt

client.PlayOpt(&spotify.PlayOptions{PlaybackContext: &spotify.URI("SOME_URI")})
always results in a crash. Am I stupid or is this a bug?

[signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0x644025]

goroutine 19 [running]:
github.com/zmb3/spotify.(*Client).PlayOpt(0x0, 0xc420498030, 0x0, 0x0)
	/home/lumi/go/src/github.com/zmb3/spotify/player.go:286 +0x55
main.mgmt(0x0)
	/home/lumi/Projects/spotiman/mgmt.go:50 +0xdd
created by main.main
	/home/lumi/Projects/spotiman/main.go:39 +0x7a

playlists unauthorized

I don't quite understand why this is giving an "unauthorized" error.
What am I missing? The credentials work fine for some other client.

package main

import (
  "context"
  "fmt"
  "log"

  "github.com/zmb3/spotify"
  "golang.org/x/oauth2/clientcredentials"
)

func main() {
  config := &clientcredentials.Config{
    ClientID:     os.Getenv("SPOTIFY_ID"),
    ClientSecret: os.Getenv("SPOTIFY_SECRET"),
    TokenURL:     spotify.TokenURL,
  }
  token, err := config.Token(context.Background())
  if err != nil {
    log.Fatalf("couldn't get token: %v", err)
  }

  client := spotify.Authenticator{}.NewClient(token)

  page, err := client.CurrentUsersPlaylists()
  if err != nil {
    log.Fatalf("couldn't get playlists: %v", err)
  }

  for _, playlist := range page.Playlists {
    fmt.Println("  ", playlist.Name)
  }
}

PlayerCurrentlyPlaying() returns error EOF when nothing is playing

Hi there!

I noticed that the function client.PlayerCurrentlyPlaying() returns an error with the message EOF when no song is currently playing.

The error occurs when trying to decode an empty response body.

if result != nil {
    if err := json.NewDecoder(resp.Body).Decode(result); err != nil {
        return err
    }
}

Debugger Screenshot

I would offer to submit a PR to fix this, but this is actually the first project I'm working on using go, so I'm not sure how to approach this issue.

Thanks!

Using spotifyd

Hello there!
First of great work and thanks for making this package! ๐Ÿ‘ โค๏ธ

I am trying this package to control spotifyd, however, somehow spotify API always returns with HTTP 202: Accepted (body empty) instead of HTTP 204: No Content as soon as I make a call (selected spotifyd as connected device).
The commands get trough! And spotifyd does everything fine, but HTTP 202 is not expected in this package so it will aways return an error.

I know that there is an Autoretry mode, however spotify always returns 202 no matter what, so I end up in an endless loop...

My guess is that there is an issue on spotifyd's side. (Will create a ticket there as well)
However I think there should be an option to enable HTTP 202 is good enough mode

What do you think?
BTW: willing to implement that feature!

Oauth Credential Storage

Is there a way to save the oauth credentials to the filesystem, so they can be used on a subsequent run?

How to refresh access token?

Hi there and thanks for this great library, it saved us a lot of work :)

I do have one problem though - an access token expires after 1 hour, but can be refreshed using the refresh token:

Access tokens are deliberately set to expire after a short time, after which new tokens may be granted by supplying the refresh token originally obtained during the authorization code exchange.

(https://developer.spotify.com/documentation/general/guides/authorization-guide#4-requesting-a-refreshed-access-token-spotify-returns-a-new-access-token-to-your-app)

Is there a way to do this using this library?

Thanks in advance!

ReleaseDateTime method only defined for FullAlbum struct

#71, which adds the ReleaseDate and ReleaseDatePrecision fields to the SimpleAlbum struct, was merged ages ago, but SimpleAlbum doesn't have the ReleaseDateTime method which is defined for the FullAlbum struct.

Is it possible to move the ReleaseDateTime method to the SimpleAlbum instead?
I would gladly open a PR if interested.

Tangentially related: Is there a reason why - especially in case of the Album structs - multiple fields are defined both on the "simple" as well as on the "full" variant even though the simple one is embedded?

ReorderPlaylistTracks userID string

It seems to me that userID should be a string in ReorderPlaylistTracks. It is expecting an ID instead

I'm learning go, and also new to the spotify API, so I might be missing something else :).
I'll gladly create the PR if you want

Would you be interested in a method which returns all the playlists of a user?

I am working on something where I need all the playlists of a user. I am not sure if this should be part of this library or not. Anyways here is my code:

func (c *Client) CurrentUsersAllPlaylists() (*SimplePlaylistPage, error){
	var allPlaylists *SimplePlaylistPage
	var total int
	limit := 50
	offset := 0
	opt := Options{
		Limit:  &limit,
		Offset: &offset,
	}
	for {
		playlists, err := c.CurrentUsersPlaylistsOpt(&opt)
		if err != nil {
			return nil, err
		}
		total = playlists.Total
		if allPlaylists == nil {
			allPlaylists = playlists
		} else {
			allPlaylists.Playlists = append(allPlaylists.Playlists, playlists.Playlists...)
		}
		offset = offset + limit
		if total < offset {
			break
		}
	}
	return allPlaylists, nil
}

If you think this is a good idea to have this as part of spotify lib, then I would be happy to send a PR ๐Ÿ˜„

Result of New Releases is empty

Code:

results, err := client.NewReleases()
if err != nil {
	log.Fatal(err)
}
spew.Dump(results)

The result is an empty SimpleAlbumPage struct and there is no raised error:

(*spotify.SimpleAlbumPage)(0xc04222f320)({
 basePage: (spotify.basePage) {
  Endpoint: (string) "",
  Limit: (int) 0,
  Offset: (int) 0,
  Total: (int) 0,
  Next: (string) "",
  Previous: (string) ""
 },
 Albums: ([]spotify.SimpleAlbum) <nil>
})

Other functions that need authentication work as expected with my client.

GetArtistAlbums panics

GetArtistAlbums(id) will call return c.GetArtistAlbumsOpt(artistID, nil, nil)
The third arg should not be supplied.

func (c *Client) GetArtistAlbumsOpt(artistID ID, options *Options, ts ...*AlbumType) (*SimpleAlbumPage, error) {
	spotifyURL := fmt.Sprintf("%sartists/%s/albums", c.baseURL, artistID)
	// add optional query string if options were specified
	values := url.Values{}
	if ts != nil {
		types := make([]string, len(ts))
		for i := range ts {
			types[i] = ts[i].encode()
		}
		values.Set("include_groups", strings.Join(types, ","))
	}

that for loops panics, since ts will not be nill, but will instead be [nil]

show_dialog optional parameter for authentication

The Spotify Web API also supports the option parameter show_dialog for authentication. Right now the library does not support this parameter and it's something that I would like to use for a project. Is there any plans to add/support this?

Help on how to use scopes

Hello

I'm having trouble using your package to access Spotify web API, looking to retrieve recently played items:

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/zmb3/spotify"
	"golang.org/x/oauth2/clientcredentials"
)

func main() {
	config := &clientcredentials.Config{
		ClientID:     "...",
		ClientSecret: "...",
		TokenURL:     spotify.TokenURL,
		Scopes:       []string{spotify.ScopeUserReadRecentlyPlayed},
	}

	token, err := config.Token(context.Background())
	if err != nil {
		log.Fatalf("unable to get token: %v", err)
	}

	client := spotify.Authenticator{}.NewClient(token)

	recent, err := client.PlayerRecentlyPlayedOpt(&spotify.RecentlyPlayedOptions{Limit: 10})
	if err != nil {
		log.Fatalf("unable to get recently played items: %v", err)
	}

	for _, item := range recent {
		fmt.Println(item)
	}
}

When I execute this code the API call for recently played items fails: 2017/10/29 15:26:53 unable to get recently played items: Insufficient client scope

Did I miss something regarding the specification of scopes?

FullArtist.Followers does not have a JSON-tag

Hey,

I see that most properties in this package have a JSON-tag to make the property lowercase when marshalling the structs, but it seems that FullArtist.Followers does not have it. Is there any reason for this?

Deprecate default client

Spotify is requiring all calls to be authenticated later this month.

Deprecate the default client and associated wrappers, and update the readme and godoc comments that mention which calls require authorization.

App engine

app engine seems to need a custom context to work somewhy:

Post https://accounts.spotify.com/api/token: not an App Engine context

oauth2.NoContext is user for all Follow, NewClient and Unfollow methods.
Should those accept a golang.net/x/net/context as an argument ?

Stop using http.Request for token recovery

Hello,

I'm actually using gin, and i can't access to the http.Request object
Is it possible to add a function where i can retrieve the token without using http.request

regards,

Store token to later identify users

I'm a bit confused as to how I should store the Spotify access token in order to identify users through multiple page accesses (i.e. keep a long running "session" open, saving the user's authorization grant).

Is the token itself safe to store in a cookie? Should I create a session ID and store that in a cookie instead (and use a map to associate it with a token)? Is there a better approach that I'm not aware of?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.