Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 45 additions & 2 deletions player.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
Expand Down Expand Up @@ -61,7 +62,46 @@ type CurrentlyPlaying struct {
// Playing If something is currently playing.
Playing bool `json:"is_playing"`
// The currently playing track. Can be null.
Item *FullTrack `json:"item"`
Item *CurrentlyPlayingTrack `json:"item"`
}

// CurrentlyPlayingTrack is a union type for both a track and and episode.
type CurrentlyPlayingTrack struct {
Track *FullTrack
Episode *EpisodePage
}

// UnmarshalJSON customises the unmarshalling based on the type flags set.
func (t *CurrentlyPlayingTrack) UnmarshalJSON(b []byte) error {
if bytes.Equal(b, []byte("null")) {
return nil
}

itemType := struct {
Type string `json:"type"`
}{}

err := json.Unmarshal(b, &itemType)
if err != nil {
return err
}

switch itemType.Type {
case "episode":
err := json.Unmarshal(b, &t.Episode)
if err != nil {
return err
}
case "track":
err := json.Unmarshal(b, &t.Track)
if err != nil {
return err
}
default:
return fmt.Errorf("unrecognized item type: %s", itemType.Type)
}

return nil
}

type RecentlyPlayedItem struct {
Expand Down Expand Up @@ -178,10 +218,13 @@ func (c *Client) PlayerState(ctx context.Context, opts ...RequestOption) (*Playe
// Requires the ScopeUserReadCurrentlyPlaying scope or the ScopeUserReadPlaybackState
// scope in order to read information
//
// Supported options: Market
// Supported options: Market, AdditionalTypes
func (c *Client) PlayerCurrentlyPlaying(ctx context.Context, opts ...RequestOption) (*CurrentlyPlaying, error) {
spotifyURL := c.baseURL + "me/player/currently-playing"

// Add default as the first option so it gets override by url.Values#Set
opts = append([]RequestOption{AdditionalTypes(EpisodeAdditionalType, TrackAdditionalType)}, opts...)

if params := processOptions(opts...).urlParams.Encode(); params != "" {
spotifyURL += "?" + params
}
Expand Down
53 changes: 53 additions & 0 deletions player_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,59 @@ func TestPlayerCurrentlyPlaying(t *testing.T) {
}
}

func TestPlayerCurrentlyPlayingEpisode(t *testing.T) {
client, server := testClientFile(http.StatusOK, "test_data/player_currently_playing_episode.json")
defer server.Close()

current, err := client.PlayerCurrentlyPlaying(context.Background())
if err != nil {
t.Error(err)
return
}

if current.Item == nil {
t.Error("Expected item to be a episode")
}

expectedName := "300 multiple choices"
actualName := current.Item.Episode.Name
if expectedName != actualName {
t.Errorf("Got '%s', expected '%s'\n", actualName, expectedName)
}

if current.Playing {
t.Error("Expected not to be playing")
}
}

func TestPlayerCurrentlyPlayingOverride(t *testing.T) {
var types string
client, server := testClientString(http.StatusForbidden, "", func(r *http.Request) {
types = r.URL.Query().Get("additional_types")
})
defer server.Close()

_, _ = client.PlayerCurrentlyPlaying(context.Background(), AdditionalTypes(EpisodeAdditionalType))

if types != "episode" {
t.Errorf("Expected additional type episode, got %s\n", types)
}
}

func TestPlayerCurrentlyPlayingDefault(t *testing.T) {
var types string
client, server := testClientString(http.StatusForbidden, "", func(r *http.Request) {
types = r.URL.Query().Get("additional_types")
})
defer server.Close()

_, _ = client.PlayerCurrentlyPlaying(context.Background())

if types != "episode,track" {
t.Errorf("Expected additional type episode,track, got %s\n", types)
}
}

func TestPlayerRecentlyPlayed(t *testing.T) {
client, server := testClientFile(http.StatusOK, "test_data/player_recently_played.txt")
defer server.Close()
Expand Down
237 changes: 237 additions & 0 deletions test_data/player_currently_playing_episode.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
{
"context": null,
"timestamp": 1706351396175,
"progress_ms": 79799,
"is_playing": false,
"item": {
"audio_preview_url": "https://podz-content.spotifycdn.com/audio/clips/0o5xSNKJenESDGRQYklFdz/clip_3271600_3331750.mp3",
"description": "Over the past 8 years, Go Time has published 300 episodes! In this episode, the panel discusses which ones they loved the most, some current stuff that’s in the works, what struggles the podcast has had & what we’re planning for the future.",
"html_description": "<p>Over the past 8 years, Go Time has published 300 episodes! In this episode, the panel discusses which ones they loved the most, some current stuff that’s in the works, what struggles the podcast has had &amp; what we’re planning for the future.</p><br/><p><a href=\"https://changelog.com/gotime/300/discuss\" rel=\"nofollow\">Leave us a comment</a></p><br/><p><a href=\"https://changelog.com/&#43;&#43;\" rel=\"nofollow\">Changelog&#43;&#43;</a> members save 2 minutes on this episode because they made the ads disappear. Join today!</p><br/><p>Sponsors:</p><br/><p></p><ul><li><a href=\"https://changelog.com/news\" rel=\"nofollow\">Changelog News</a> – A podcast&#43;newsletter combo that’s brief, entertaining &amp; always on-point. <a href=\"https://changelog.com/news\" rel=\"nofollow\">Subscribe today</a>.<br/></li><li><a href=\"https://fly.io/changelog\" rel=\"nofollow\">Fly.io</a> – The home of Changelog.com — Deploy your apps and databases close to your users. In minutes you can run your Ruby, Go, Node, Deno, Python, or Elixir app (and databases!) all over the world. No ops required. Learn more at <a href=\"https://fly.io/changelog\" rel=\"nofollow\">fly.io/changelog</a> and check out <a href=\"https://fly.io/docs/speedrun/\" rel=\"nofollow\">the speedrun in their docs</a>.<br/></li></ul><br/><p>Featuring:</p><br/><p></p><ul><li>Kris Brandow – <a href=\"https://twitter.com/skriptble\" rel=\"nofollow\">Twitter</a>, <a href=\"https://github.com/skriptble\" rel=\"nofollow\">GitHub</a></li><li>Jon Calhoun – <a href=\"https://twitter.com/joncalhoun\" rel=\"nofollow\">Twitter</a>, <a href=\"https://github.com/joncalhoun\" rel=\"nofollow\">GitHub</a>, <a href=\"https://www.calhoun.io\" rel=\"nofollow\">Website</a></li><li>Johnny Boursiquot – <a href=\"https://twitter.com/jboursiquot\" rel=\"nofollow\">Twitter</a>, <a href=\"https://github.com/jboursiquot\" rel=\"nofollow\">GitHub</a>, <a href=\"https://www.jboursiquot.com/\" rel=\"nofollow\">Website</a></li><li>Ian Lopshire – <a href=\"https://twitter.com/ianlopshire\" rel=\"nofollow\">Twitter</a>, <a href=\"https://github.com/ianlopshire\" rel=\"nofollow\">GitHub</a></li><li>Angelica Hill – <a href=\"https://twitter.com/Angelica_Hill\" rel=\"nofollow\">Twitter</a>, <a href=\"https://github.com/angelicahill\" rel=\"nofollow\">GitHub</a></li></ul><br/><p>Show Notes:</p><br/><p></p><ul><li>Jon’s favorite episodes<br/><ul><li><a href=\"https://changelog.com/gotime/291\" rel=\"nofollow\">Go &amp; Templ</a></li><li><a href=\"https://changelog.com/gotime/176\" rel=\"nofollow\">Deep dive on TCP/IP &amp; Networking</a></li></ul><br/></li><li>Johnny’s favorite episodes<br/><ul><li><a href=\"https://changelog.com/gotime/294\" rel=\"nofollow\">7 deadly sins</a></li><li>What’s New In Go episodes<br/><ul><li><a href=\"https://changelog.com/gotime/289\" rel=\"nofollow\">1.21</a></li><li><a href=\"https://changelog.com/gotime/267\" rel=\"nofollow\">1.20</a></li><li><a href=\"https://changelog.com/gotime/240\" rel=\"nofollow\">1.19</a></li></ul><br/></li></ul><br/></li><li>Ian’s favorite episodes<br/><ul><li>Episodes where we talk to experts that deeply care</li><li>Who owns our code?<br/><ul><li><a href=\"https://changelog.com/gotime/252\" rel=\"nofollow\">Who owns our code?</a></li><li><a href=\"https://changelog.com/gotime/263\" rel=\"nofollow\">Who owns our code? Part 2</a></li></ul><br/></li><li>Biology and Go<br/><ul><li><a href=\"https://changelog.com/gotime/254\" rel=\"nofollow\">Go in medicine &amp; biology</a></li></ul><br/></li><li>What’s new in Go cryptopgrahy<br/><ul><li><a href=\"https://changelog.com/gotime/295\" rel=\"nofollow\">What’s new in Go’s cryptography libraries: Part 1</a></li><li><a href=\"https://changelog.com/gotime/298\" rel=\"nofollow\">What’s new in Go’s cryptography libraries: Part 2</a></li></ul><br/></li></ul><br/></li><li>Kris’ favorite episodes<br/><ul><li><a href=\"https://changelog.com/topic/maintenance\" rel=\"nofollow\">Maintenance mini series</a></li><li>Episodes about software engineering in general and the Go community<br/><ul><li><a href=\"https://changelog.com/gotime/296\" rel=\"nofollow\">Principles of Simplicity</a></li><li><a href=\"https://changelog.com/gotime/293\" rel=\"nofollow\">Experiences from GopherCon 2023</a></li></ul><br/></li></ul><br/></li><li>Angelica’s favorite episodes<br/><ul><li><a href=\"https://changelog.com/topic/maintenance\" rel=\"nofollow\">Maintenance mini series episodes</a></li><li>Event driven architectures<br/><ul><li><a href=\"https://changelog.com/gotime/297\" rel=\"nofollow\">Event-driven systems &amp; Architectures</a></li></ul><br/></li><li>Go deep dive episodes<br/><ul><li><a href=\"https://changelog.com/gotime/288\" rel=\"nofollow\">A deep dive into Go’s stack</a></li></ul><br/></li><li>Episodes that are about Go and tech but are also apply outside of tech<br/><ul><li><a href=\"https://changelog.com/gotime/297\" rel=\"nofollow\">Event-driven systems &amp; architecture</a></li><li><a href=\"https://changelog.com/gotime/296\" rel=\"nofollow\">Principles of simplicity</a></li></ul><br/></li></ul><br/></li><li>Ian’s recommendations<br/><ul><li>Who owns our code? episodes<br/><ul><li><a href=\"https://changelog.com/gotime/252\" rel=\"nofollow\">Who owns our code?</a></li><li><a href=\"https://changelog.com/gotime/263\" rel=\"nofollow\">Who owns our code? Part 2</a></li></ul><br/></li></ul><br/></li><li>Jon’s recommendations<br/><ul><li>Depends on the person</li><li>Usually episodes around the new to Go topic</li></ul><br/></li><li>Johnny’s recommendations<br/><ul><li>Doesn’t relisten to episode he’s on</li><li>Start with the last one</li></ul><br/></li><li>Kris’ recommendations<br/><ul><li>Recent episode</li></ul><br/></li><li>Angelica’s recommendations<br/><ul><li>Episode around Go and simplicity<br/><ul><li><a href=\"https://changelog.com/gotime/296\" rel=\"nofollow\">Principles of Simplicity</a></li></ul><br/></li><li>Angelica’s first episode<br/><ul><li><a href=\"https://changelog.com/gotime/157\" rel=\"nofollow\">The Secret Life of Gophers</a></li></ul><br/></li></ul><br/></li></ul><br/><p>Something missing or broken? <a href=\"https://github.com/thechangelog/show-notes/blob/master/gotime/go-time-300.md\" rel=\"nofollow\">PRs welcome!</a></p>",
"duration_ms": 6672328,
"explicit": false,
"external_urls": {
"spotify": "https://open.spotify.com/episode/0N1dkr7thtEBhk059opmp9"
},
"href": "https://api.spotify.com/v1/episodes/0N1dkr7thtEBhk059opmp9",
"id": "0N1dkr7thtEBhk059opmp9",
"images": [
{
"url": "https://i.scdn.co/image/18d9cacf366a7173b10204eb983fe5d374b728dd",
"height": 640,
"width": 640
},
{
"url": "https://i.scdn.co/image/3f9b1dd55b3353a0d70dfcca6a44c01775cfbfcc",
"height": 300,
"width": 300
},
{
"url": "https://i.scdn.co/image/aa9e23de8a5a64bedaf526bb963123413984e53f",
"height": 64,
"width": 64
}
],
"is_externally_hosted": false,
"is_playable": true,
"language": "en-US",
"languages": [
"en-US"
],
"name": "300 multiple choices",
"release_date": "2024-01-23",
"release_date_precision": "day",
"type": "episode",
"uri": "spotify:episode:0N1dkr7thtEBhk059opmp9",
"show": {
"available_markets": [
"AD",
"AE",
"AG",
"AL",
"AM",
"AR",
"AT",
"AU",
"BA",
"BB",
"BE",
"BF",
"BG",
"BH",
"BJ",
"BO",
"BR",
"BS",
"BT",
"BW",
"BZ",
"CA",
"CH",
"CL",
"CO",
"CR",
"CV",
"CW",
"CY",
"CZ",
"DE",
"DK",
"DM",
"DO",
"DZ",
"EC",
"EE",
"EG",
"ES",
"FI",
"FJ",
"FM",
"FR",
"GB",
"GD",
"GE",
"GH",
"GM",
"GR",
"GT",
"GW",
"GY",
"HK",
"HN",
"HR",
"HT",
"HU",
"ID",
"IE",
"IL",
"IN",
"IS",
"IT",
"JM",
"JO",
"JP",
"KE",
"KI",
"KN",
"KR",
"KW",
"LB",
"LC",
"LI",
"LR",
"LS",
"LT",
"LU",
"LV",
"MA",
"MC",
"ME",
"MG",
"MH",
"MK",
"ML",
"MT",
"MU",
"MV",
"MW",
"MX",
"MY",
"MZ",
"NA",
"NE",
"NG",
"NI",
"NL",
"NO",
"NR",
"NZ",
"OM",
"PA",
"PE",
"PG",
"PH",
"PL",
"PS",
"PT",
"PW",
"PY",
"QA",
"RO",
"RS",
"SA",
"SB",
"SC",
"SE",
"SG",
"SI",
"SK",
"SL",
"SM",
"SN",
"SR",
"ST",
"SV",
"TH",
"TL",
"TN",
"TO",
"TR",
"TT",
"TV",
"TW",
"UA",
"US",
"UY",
"VC",
"VN",
"VU",
"WS",
"XK",
"ZA"
],
"copyrights": [],
"description": "Your source for diverse discussions from around the Go community. This show records LIVE every Tuesday at 3pm US Eastern. Join the Golang community and chat with us during the show in the #gotimefm channel of Gophers slack. Panelists include Mat Ryer, Jon Calhoun, Natalie Pistunovich, Johnny Boursiquot, Angelica Hill, Kris Brandow, and Ian Lopshire. We discuss cloud infrastructure, distributed systems, microservices, Kubernetes, Docker… oh and also Go! Some people search for GoTime or GoTimeFM and can’t find the show, so now the strings GoTime and GoTimeFM are in our description too.",
"html_description": "Your source for diverse discussions from around the Go community. This show records LIVE every Tuesday at 3pm US Eastern. Join the Golang community and chat with us during the show in the #gotimefm channel of Gophers slack. Panelists include Mat Ryer, Jon Calhoun, Natalie Pistunovich, Johnny Boursiquot, Angelica Hill, Kris Brandow, and Ian Lopshire. We discuss cloud infrastructure, distributed systems, microservices, Kubernetes, Docker… oh and also Go! Some people search for GoTime or GoTimeFM and can’t find the show, so now the strings GoTime and GoTimeFM are in our description too.",
"explicit": false,
"external_urls": {
"spotify": "https://open.spotify.com/show/2cKdcxETn7jDp7uJCwqmSE"
},
"href": "https://api.spotify.com/v1/shows/2cKdcxETn7jDp7uJCwqmSE",
"id": "2cKdcxETn7jDp7uJCwqmSE",
"images": [
{
"url": "https://i.scdn.co/image/18d9cacf366a7173b10204eb983fe5d374b728dd",
"height": 640,
"width": 640
},
{
"url": "https://i.scdn.co/image/3f9b1dd55b3353a0d70dfcca6a44c01775cfbfcc",
"height": 300,
"width": 300
},
{
"url": "https://i.scdn.co/image/aa9e23de8a5a64bedaf526bb963123413984e53f",
"height": 64,
"width": 64
}
],
"is_externally_hosted": false,
"languages": [
"en-US"
],
"media_type": "audio",
"name": "Go Time: Golang, Software Engineering",
"publisher": "Changelog Media",
"type": "show",
"uri": "spotify:show:2cKdcxETn7jDp7uJCwqmSE",
"total_episodes": 307
}
},
"currently_playing_type": "episode",
"actions": {
"disallows": {
"resuming": true,
"skipping_prev": true
}
}
}