From e2810bb9d2515def0cbe41fb4192c06a32eed12e Mon Sep 17 00:00:00 2001 From: Flavio Sperandeo <55345633+thegeek-sys@users.noreply.github.com> Date: Wed, 12 Feb 2025 12:19:40 +0100 Subject: [PATCH 1/6] Update SpotifyArduino.h --- src/SpotifyArduino.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/SpotifyArduino.h b/src/SpotifyArduino.h index 102ca46..b2ed145 100644 --- a/src/SpotifyArduino.h +++ b/src/SpotifyArduino.h @@ -78,6 +78,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define SPOTIFY_SEEK_ENDPOINT "/v1/me/player/seek" +#define SPOTIFY_PLAYLIST_ENDPOINT "/v1/playlists/" + #define SPOTIFY_TOKEN_ENDPOINT "/api/token" #define SPOTIFY_NUM_ALBUM_IMAGES 3 // Max spotify returns is 3, but the third one is probably too big for an ESP @@ -146,6 +148,18 @@ struct SearchResult int numImages; }; +struct PlaylistResult +{ + const char *albumName; + const char *albumUri; + const char *trackName; + const char *trackUri; + SpotifyArtist artists[SPOTIFY_MAX_NUM_ARTISTS]; + SpotifyImage albumImages[SPOTIFY_NUM_ALBUM_IMAGES]; + int numArtists; + int numImages; +}; + struct CurrentlyPlaying { SpotifyArtist artists[SPOTIFY_MAX_NUM_ARTISTS]; @@ -167,6 +181,8 @@ typedef void (*processCurrentlyPlaying)(CurrentlyPlaying currentlyPlaying); typedef void (*processPlayerDetails)(PlayerDetails playerDetails); typedef bool (*processDevices)(SpotifyDevice device, int index, int numDevices); typedef bool (*processSearch)(SearchResult result, int index, int numResults); +typedef bool (*processPlaylist)(PlaylistResult result, int index, int numResults); + class SpotifyArduino { @@ -207,6 +223,9 @@ class SpotifyArduino //Search int searchForSong(String query, int limit, processSearch searchCallback, SearchResult results[]); + //Playlist + int getPlaylist(String query, int limit, processPlaylist playlistCallback, PlaylistResult results[]); + // Image methods bool getImage(char *imageUrl, Stream *file); bool getImage(char *imageUrl, uint8_t **image, int *imageLength); @@ -216,6 +235,7 @@ class SpotifyArduino int playerDetailsBufferSize = 2000; int getDevicesBufferSize = 3000; int searchDetailsBufferSize = 3000; + int playlistDetailsBufferSize = 3000; bool autoTokenRefresh = true; Client *client; void lateInit(const char *clientId, const char *clientSecret, const char *refreshToken = ""); From 10ea8233ab24dbceefa85527cd02b5d817c3b522 Mon Sep 17 00:00:00 2001 From: Flavio Sperandeo <55345633+thegeek-sys@users.noreply.github.com> Date: Wed, 12 Feb 2025 12:26:52 +0100 Subject: [PATCH 2/6] adding getPlaylist feature --- src/SpotifyArduino.cpp | 106 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/src/SpotifyArduino.cpp b/src/SpotifyArduino.cpp index e3fe108..acc1e49 100644 --- a/src/SpotifyArduino.cpp +++ b/src/SpotifyArduino.cpp @@ -1025,6 +1025,112 @@ int SpotifyArduino::searchForSong(String query, int limit, processSearch searchC return statusCode; } +int SpotifyArduino::getPlaylist(String query, int limit, processPlaylist playlistCallback, PlaylistResult results[]) +{ + +#ifdef SPOTIFY_DEBUG + Serial.println(SPOTIFY_PLAYLIST_ENDPOINT); + printStack(); +#endif + + // Get from https://arduinojson.org/v6/assistant/ + const size_t bufferSize = searchDetailsBufferSize; + if (autoTokenRefresh) + { + checkAndRefreshAccessToken(); + } + + int statusCode = makeGetRequest((SPOTIFY_PLAYLIST_ENDPOINT + query).c_str(), _bearerToken); +#ifdef SPOTIFY_DEBUG + Serial.print("Status Code: "); + Serial.println(statusCode); +#endif + if (statusCode > 0) + { + skipHeaders(); + } + + if (statusCode == 200) + { + + // Allocate DynamicJsonDocument + DynamicJsonDocument doc(bufferSize); + + // Parse JSON object +#ifndef SPOTIFY_PRINT_JSON_PARSE + DeserializationError error = deserializeJson(doc, *client); +#else + ReadLoggingStream loggingStream(*client, Serial); + DeserializationError error = deserializeJson(doc, loggingStream); +#endif + if (!error) + { + + uint8_t totalResults = doc["tracks"]["items"].size(); + + Serial.print("Total Results: "); + Serial.println(totalResults); + + PlaylistResult playlistResult; + for (int i = 0; i < totalResults; i++) + { + //Polling track information + JsonObject result = doc["tracks"]["items"][i]["track"]; + playlistResult.trackUri = result["uri"].as(); + playlistResult.trackName = result["name"].as(); + Serial.println(playlistResult.trackName); + playlistResult.albumUri = result["album"]["uri"].as(); + playlistResult.albumName = result["album"]["name"].as(); + + //Pull artist Information for the result + uint8_t totalArtists = result["artists"].size(); + playlistResult.numArtists = totalArtists; + + SpotifyArtist artist; + for (int j = 0; j < totalArtists; j++) + { + JsonObject artistResult = result["artists"][j]; + artist.artistName = artistResult["name"].as(); + artist.artistUri = artistResult["uri"].as(); + playlistResult.artists[j] = artist; + } + + uint8_t totalImages = result["album"]["images"].size(); + playlistResult.numImages = totalImages; + + SpotifyImage image; + for (int j = 0; j < totalImages; j++) + { + JsonObject imageResult = result["album"]["images"][j]; + image.height = imageResult["height"].as(); + image.width = imageResult["width"].as(); + image.url = imageResult["url"].as(); + playlistResult.albumImages[j] = image; + } + + results[i] = playlistResult; + + if (i >= limit-1 || !playlistCallback(playlistResult, i, totalResults)) + { + //Break at the limit or when indicated + break; + } + } + } + else + { +#ifdef SPOTIFY_SERIAL_OUTPUT + Serial.print(F("deserializeJson() failed with code ")); + Serial.println(error.c_str()); +#endif + statusCode = -1; + } + } + + closeClient(); + return statusCode; +} + int SpotifyArduino::commonGetImage(char *imageUrl) { #ifdef SPOTIFY_DEBUG From 50d89a8e1dd6fa0ba6250973e7a8af87003fd757 Mon Sep 17 00:00:00 2001 From: Flavio Sperandeo <55345633+thegeek-sys@users.noreply.github.com> Date: Wed, 12 Feb 2025 12:59:09 +0100 Subject: [PATCH 3/6] fixing playlistDetailsBufferSize --- src/SpotifyArduino.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SpotifyArduino.cpp b/src/SpotifyArduino.cpp index acc1e49..be22b2b 100644 --- a/src/SpotifyArduino.cpp +++ b/src/SpotifyArduino.cpp @@ -1034,7 +1034,7 @@ int SpotifyArduino::getPlaylist(String query, int limit, processPlaylist playlis #endif // Get from https://arduinojson.org/v6/assistant/ - const size_t bufferSize = searchDetailsBufferSize; + const size_t bufferSize = playlistDetailsBufferSize; if (autoTokenRefresh) { checkAndRefreshAccessToken(); From a0cf50b65705762ef2fcc46969babf58822ee054 Mon Sep 17 00:00:00 2001 From: Flavio Sperandeo <55345633+thegeek-sys@users.noreply.github.com> Date: Thu, 13 Feb 2025 10:03:29 +0100 Subject: [PATCH 4/6] changed struct of PlaylistResult to make it fit up 100 songs --- src/SpotifyArduino.cpp | 267 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 267 insertions(+) diff --git a/src/SpotifyArduino.cpp b/src/SpotifyArduino.cpp index be22b2b..1b41cc9 100644 --- a/src/SpotifyArduino.cpp +++ b/src/SpotifyArduino.cpp @@ -1,6 +1,273 @@ /* SpotifyArduino - An Arduino library to wrap the Spotify API +Copyright (c) 2020 Brian Lough. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef SpotifyArduino_h +#define SpotifyArduino_h + +// I find setting these types of flags unreliable from the Arduino IDE +// so uncomment this if its not working for you. +// NOTE: Do not use this option on live-streams, it will reveal your +// private tokens! + +#define SPOTIFY_DEBUG 1 + +// Comment out if you want to disable any serial output from this library (also comment out DEBUG and PRINT_JSON_PARSE) +#define SPOTIFY_SERIAL_OUTPUT 1 + +// Prints the JSON received to serial (only use for debugging as it will be slow) +//#define SPOTIFY_PRINT_JSON_PARSE 1 + +#include +#include +#include + +#ifdef SPOTIFY_PRINT_JSON_PARSE +#include +#endif + +#define SPOTIFY_HOST "api.spotify.com" +#define SPOTIFY_ACCOUNTS_HOST "accounts.spotify.com" + +// Fingerprint for "*.spotify.com", correct as of March 14, 2024 +#define SPOTIFY_FINGERPRINT "69 2B 36 29 F0 B5 FC 1B A3 57 A6 76 E6 92 EF 30 14 22 34 6A" + +// Fingerprint for "*.scdn.co", correct as of March 14, 2024 +#define SPOTIFY_IMAGE_SERVER_FINGERPRINT "0A 0F 59 45 2C FF 37 3C FE 37 27 AD 32 64 59 A9 5A B6 2F 30" + +#define SPOTIFY_TIMEOUT 2000 + +#define SPOTIFY_NAME_CHAR_LENGTH 100 //Increase if artists/song/album names are being cut off +#define SPOTIFY_URI_CHAR_LENGTH 40 +#define SPOTIFY_URL_CHAR_LENGTH 70 + +#define SPOTIFY_DEVICE_ID_CHAR_LENGTH 45 +#define SPOTIFY_DEVICE_NAME_CHAR_LENGTH 80 +#define SPOTIFY_DEVICE_TYPE_CHAR_LENGTH 30 + +#define SPOTIFY_CURRENTLY_PLAYING_ENDPOINT "/v1/me/player/currently-playing?additional_types=episode" + +#define SPOTIFY_PLAYER_ENDPOINT "/v1/me/player" +#define SPOTIFY_DEVICES_ENDPOINT "/v1/me/player/devices" + +#define SPOTIFY_PLAY_ENDPOINT "/v1/me/player/play" +#define SPOTIFY_SEARCH_ENDPOINT "/v1/search" +#define SPOTIFY_PAUSE_ENDPOINT "/v1/me/player/pause" +#define SPOTIFY_VOLUME_ENDPOINT "/v1/me/player/volume?volume_percent=%d" +#define SPOTIFY_SHUFFLE_ENDPOINT "/v1/me/player/shuffle?state=%s" +#define SPOTIFY_REPEAT_ENDPOINT "/v1/me/player/repeat?state=%s" + +#define SPOTIFY_NEXT_TRACK_ENDPOINT "/v1/me/player/next" +#define SPOTIFY_PREVIOUS_TRACK_ENDPOINT "/v1/me/player/previous" + +#define SPOTIFY_SEEK_ENDPOINT "/v1/me/player/seek" + +#define SPOTIFY_PLAYLIST_ENDPOINT "/v1/playlists/" + +#define SPOTIFY_TOKEN_ENDPOINT "/api/token" + +#define SPOTIFY_NUM_ALBUM_IMAGES 3 // Max spotify returns is 3, but the third one is probably too big for an ESP + +#define SPOTIFY_MAX_NUM_ARTISTS 5 + +#define SPOTIFY_ACCESS_TOKEN_LENGTH 309 + +enum RepeatOptions +{ + repeat_track, + repeat_context, + repeat_off +}; + +enum SpotifyPlayingType +{ + track, + episode, + other +}; + +struct SpotifyImage +{ + int height; + int width; + const char *url; +}; + +struct SpotifyDevice +{ + const char *id; + const char *name; + const char *type; + bool isActive; + bool isRestricted; + bool isPrivateSession; + int volumePercent; +}; + +struct PlayerDetails +{ + SpotifyDevice device; + + long progressMs; + bool isPlaying; + RepeatOptions repeateState; + bool shuffleState; +}; + +struct SpotifyArtist +{ + const char *artistName; + const char *artistUri; +}; + +struct SearchResult +{ + const char *albumName; + const char *albumUri; + const char *trackName; + const char *trackUri; + SpotifyArtist artists[SPOTIFY_MAX_NUM_ARTISTS]; + SpotifyImage albumImages[SPOTIFY_NUM_ALBUM_IMAGES]; + int numArtists; + int numImages; +}; + +struct PlaylistResult +{ + const char *albumName; + const char *albumUri; + const char *trackName; + const char *trackUri; + const char *artistName; + const char *artistUri; +}; + +struct CurrentlyPlaying +{ + SpotifyArtist artists[SPOTIFY_MAX_NUM_ARTISTS]; + int numArtists; + const char *albumName; + const char *albumUri; + const char *trackName; + const char *trackUri; + SpotifyImage albumImages[SPOTIFY_NUM_ALBUM_IMAGES]; + int numImages; + bool isPlaying; + long progressMs; + long durationMs; + const char *contextUri; + SpotifyPlayingType currentlyPlayingType; +}; + +typedef void (*processCurrentlyPlaying)(CurrentlyPlaying currentlyPlaying); +typedef void (*processPlayerDetails)(PlayerDetails playerDetails); +typedef bool (*processDevices)(SpotifyDevice device, int index, int numDevices); +typedef bool (*processSearch)(SearchResult result, int index, int numResults); +typedef bool (*processPlaylist)(PlaylistResult result, int index, int numResults); + + +class SpotifyArduino +{ +public: + SpotifyArduino(Client &client); + SpotifyArduino(Client &client, char *bearerToken); + SpotifyArduino(Client &client, const char *clientId, const char *clientSecret, const char *refreshToken = ""); + + // Auth Methods + void setRefreshToken(const char *refreshToken); + bool refreshAccessToken(); + bool checkAndRefreshAccessToken(); + const char *requestAccessTokens(const char *code, const char *redirectUrl); + + // Generic Request Methods + int makeGetRequest(const char *command, const char *authorization, const char *accept = "application/json", const char *host = SPOTIFY_HOST); + int makeRequestWithBody(const char *type, const char *command, const char *authorization, const char *body = "", const char *contentType = "application/json", const char *host = SPOTIFY_HOST); + int makePostRequest(const char *command, const char *authorization, const char *body = "", const char *contentType = "application/json", const char *host = SPOTIFY_HOST); + int makePutRequest(const char *command, const char *authorization, const char *body = "", const char *contentType = "application/json", const char *host = SPOTIFY_HOST); + + // User methods + int getCurrentlyPlaying(processCurrentlyPlaying currentlyPlayingCallback, const char *market = ""); + int getPlayerDetails(processPlayerDetails playerDetailsCallback, const char *market = ""); + int getDevices(processDevices devicesCallback); + bool play(const char *deviceId = ""); + bool playAdvanced(char *body, const char *deviceId = ""); + bool pause(const char *deviceId = ""); + bool setVolume(int volume, const char *deviceId = ""); + bool toggleShuffle(bool shuffle, const char *deviceId = ""); + bool setRepeatMode(RepeatOptions repeat, const char *deviceId = ""); + bool nextTrack(const char *deviceId = ""); + bool previousTrack(const char *deviceId = ""); + bool playerControl(char *command, const char *deviceId = "", const char *body = ""); + bool playerNavigate(char *command, const char *deviceId = ""); + bool seek(int position, const char *deviceId = ""); + bool transferPlayback(const char *deviceId, bool play = false); + + //Search + int searchForSong(String query, int limit, processSearch searchCallback, SearchResult results[]); + + //Playlist + int getPlaylist(String query, int limit, String market, processPlaylist playlistCallback, PlaylistResult results[]); + + // Image methods + bool getImage(char *imageUrl, Stream *file); + bool getImage(char *imageUrl, uint8_t **image, int *imageLength); + + int portNumber = 443; + int currentlyPlayingBufferSize = 3000; + int playerDetailsBufferSize = 2000; + int getDevicesBufferSize = 3000; + int searchDetailsBufferSize = 3000; + int playlistDetailsBufferSize = 3000; + bool autoTokenRefresh = true; + Client *client; + void lateInit(const char *clientId, const char *clientSecret, const char *refreshToken = ""); + +#ifdef SPOTIFY_DEBUG + char *stack_start; +#endif + +private: + char _bearerToken[SPOTIFY_ACCESS_TOKEN_LENGTH + 10]; //10 extra is for "bearer " at the start + char *_refreshToken; + const char *_clientId; + const char *_clientSecret; + unsigned int timeTokenRefreshed; + unsigned int tokenTimeToLiveMs; + int commonGetImage(char *imageUrl); + int getContentLength(); + int getHttpStatusCode(); + void skipHeaders(bool tossUnexpectedForJSON = true); + void closeClient(); + void parseError(); + const char *requestAccessTokensBody = + R"(grant_type=authorization_code&code=%s&redirect_uri=%s&client_id=%s&client_secret=%s)"; + const char *refreshAccessTokensBody = + R"(grant_type=refresh_token&refresh_token=%s&client_id=%s&client_secret=%s)"; +#ifdef SPOTIFY_DEBUG + void printStack(); +#endif +}; + +#endif +/* +SpotifyArduino - An Arduino library to wrap the Spotify API + Copyright (c) 2021 Brian Lough. This library is free software; you can redistribute it and/or From 679391e26ee487a1dfd2075989b20916626763dd Mon Sep 17 00:00:00 2001 From: Flavio Sperandeo <55345633+thegeek-sys@users.noreply.github.com> Date: Thu, 13 Feb 2025 10:10:21 +0100 Subject: [PATCH 5/6] taking market as input and filtering json --- src/SpotifyArduino.cpp | 300 +---------------------------------------- 1 file changed, 5 insertions(+), 295 deletions(-) diff --git a/src/SpotifyArduino.cpp b/src/SpotifyArduino.cpp index 1b41cc9..321417e 100644 --- a/src/SpotifyArduino.cpp +++ b/src/SpotifyArduino.cpp @@ -1,273 +1,6 @@ /* SpotifyArduino - An Arduino library to wrap the Spotify API -Copyright (c) 2020 Brian Lough. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef SpotifyArduino_h -#define SpotifyArduino_h - -// I find setting these types of flags unreliable from the Arduino IDE -// so uncomment this if its not working for you. -// NOTE: Do not use this option on live-streams, it will reveal your -// private tokens! - -#define SPOTIFY_DEBUG 1 - -// Comment out if you want to disable any serial output from this library (also comment out DEBUG and PRINT_JSON_PARSE) -#define SPOTIFY_SERIAL_OUTPUT 1 - -// Prints the JSON received to serial (only use for debugging as it will be slow) -//#define SPOTIFY_PRINT_JSON_PARSE 1 - -#include -#include -#include - -#ifdef SPOTIFY_PRINT_JSON_PARSE -#include -#endif - -#define SPOTIFY_HOST "api.spotify.com" -#define SPOTIFY_ACCOUNTS_HOST "accounts.spotify.com" - -// Fingerprint for "*.spotify.com", correct as of March 14, 2024 -#define SPOTIFY_FINGERPRINT "69 2B 36 29 F0 B5 FC 1B A3 57 A6 76 E6 92 EF 30 14 22 34 6A" - -// Fingerprint for "*.scdn.co", correct as of March 14, 2024 -#define SPOTIFY_IMAGE_SERVER_FINGERPRINT "0A 0F 59 45 2C FF 37 3C FE 37 27 AD 32 64 59 A9 5A B6 2F 30" - -#define SPOTIFY_TIMEOUT 2000 - -#define SPOTIFY_NAME_CHAR_LENGTH 100 //Increase if artists/song/album names are being cut off -#define SPOTIFY_URI_CHAR_LENGTH 40 -#define SPOTIFY_URL_CHAR_LENGTH 70 - -#define SPOTIFY_DEVICE_ID_CHAR_LENGTH 45 -#define SPOTIFY_DEVICE_NAME_CHAR_LENGTH 80 -#define SPOTIFY_DEVICE_TYPE_CHAR_LENGTH 30 - -#define SPOTIFY_CURRENTLY_PLAYING_ENDPOINT "/v1/me/player/currently-playing?additional_types=episode" - -#define SPOTIFY_PLAYER_ENDPOINT "/v1/me/player" -#define SPOTIFY_DEVICES_ENDPOINT "/v1/me/player/devices" - -#define SPOTIFY_PLAY_ENDPOINT "/v1/me/player/play" -#define SPOTIFY_SEARCH_ENDPOINT "/v1/search" -#define SPOTIFY_PAUSE_ENDPOINT "/v1/me/player/pause" -#define SPOTIFY_VOLUME_ENDPOINT "/v1/me/player/volume?volume_percent=%d" -#define SPOTIFY_SHUFFLE_ENDPOINT "/v1/me/player/shuffle?state=%s" -#define SPOTIFY_REPEAT_ENDPOINT "/v1/me/player/repeat?state=%s" - -#define SPOTIFY_NEXT_TRACK_ENDPOINT "/v1/me/player/next" -#define SPOTIFY_PREVIOUS_TRACK_ENDPOINT "/v1/me/player/previous" - -#define SPOTIFY_SEEK_ENDPOINT "/v1/me/player/seek" - -#define SPOTIFY_PLAYLIST_ENDPOINT "/v1/playlists/" - -#define SPOTIFY_TOKEN_ENDPOINT "/api/token" - -#define SPOTIFY_NUM_ALBUM_IMAGES 3 // Max spotify returns is 3, but the third one is probably too big for an ESP - -#define SPOTIFY_MAX_NUM_ARTISTS 5 - -#define SPOTIFY_ACCESS_TOKEN_LENGTH 309 - -enum RepeatOptions -{ - repeat_track, - repeat_context, - repeat_off -}; - -enum SpotifyPlayingType -{ - track, - episode, - other -}; - -struct SpotifyImage -{ - int height; - int width; - const char *url; -}; - -struct SpotifyDevice -{ - const char *id; - const char *name; - const char *type; - bool isActive; - bool isRestricted; - bool isPrivateSession; - int volumePercent; -}; - -struct PlayerDetails -{ - SpotifyDevice device; - - long progressMs; - bool isPlaying; - RepeatOptions repeateState; - bool shuffleState; -}; - -struct SpotifyArtist -{ - const char *artistName; - const char *artistUri; -}; - -struct SearchResult -{ - const char *albumName; - const char *albumUri; - const char *trackName; - const char *trackUri; - SpotifyArtist artists[SPOTIFY_MAX_NUM_ARTISTS]; - SpotifyImage albumImages[SPOTIFY_NUM_ALBUM_IMAGES]; - int numArtists; - int numImages; -}; - -struct PlaylistResult -{ - const char *albumName; - const char *albumUri; - const char *trackName; - const char *trackUri; - const char *artistName; - const char *artistUri; -}; - -struct CurrentlyPlaying -{ - SpotifyArtist artists[SPOTIFY_MAX_NUM_ARTISTS]; - int numArtists; - const char *albumName; - const char *albumUri; - const char *trackName; - const char *trackUri; - SpotifyImage albumImages[SPOTIFY_NUM_ALBUM_IMAGES]; - int numImages; - bool isPlaying; - long progressMs; - long durationMs; - const char *contextUri; - SpotifyPlayingType currentlyPlayingType; -}; - -typedef void (*processCurrentlyPlaying)(CurrentlyPlaying currentlyPlaying); -typedef void (*processPlayerDetails)(PlayerDetails playerDetails); -typedef bool (*processDevices)(SpotifyDevice device, int index, int numDevices); -typedef bool (*processSearch)(SearchResult result, int index, int numResults); -typedef bool (*processPlaylist)(PlaylistResult result, int index, int numResults); - - -class SpotifyArduino -{ -public: - SpotifyArduino(Client &client); - SpotifyArduino(Client &client, char *bearerToken); - SpotifyArduino(Client &client, const char *clientId, const char *clientSecret, const char *refreshToken = ""); - - // Auth Methods - void setRefreshToken(const char *refreshToken); - bool refreshAccessToken(); - bool checkAndRefreshAccessToken(); - const char *requestAccessTokens(const char *code, const char *redirectUrl); - - // Generic Request Methods - int makeGetRequest(const char *command, const char *authorization, const char *accept = "application/json", const char *host = SPOTIFY_HOST); - int makeRequestWithBody(const char *type, const char *command, const char *authorization, const char *body = "", const char *contentType = "application/json", const char *host = SPOTIFY_HOST); - int makePostRequest(const char *command, const char *authorization, const char *body = "", const char *contentType = "application/json", const char *host = SPOTIFY_HOST); - int makePutRequest(const char *command, const char *authorization, const char *body = "", const char *contentType = "application/json", const char *host = SPOTIFY_HOST); - - // User methods - int getCurrentlyPlaying(processCurrentlyPlaying currentlyPlayingCallback, const char *market = ""); - int getPlayerDetails(processPlayerDetails playerDetailsCallback, const char *market = ""); - int getDevices(processDevices devicesCallback); - bool play(const char *deviceId = ""); - bool playAdvanced(char *body, const char *deviceId = ""); - bool pause(const char *deviceId = ""); - bool setVolume(int volume, const char *deviceId = ""); - bool toggleShuffle(bool shuffle, const char *deviceId = ""); - bool setRepeatMode(RepeatOptions repeat, const char *deviceId = ""); - bool nextTrack(const char *deviceId = ""); - bool previousTrack(const char *deviceId = ""); - bool playerControl(char *command, const char *deviceId = "", const char *body = ""); - bool playerNavigate(char *command, const char *deviceId = ""); - bool seek(int position, const char *deviceId = ""); - bool transferPlayback(const char *deviceId, bool play = false); - - //Search - int searchForSong(String query, int limit, processSearch searchCallback, SearchResult results[]); - - //Playlist - int getPlaylist(String query, int limit, String market, processPlaylist playlistCallback, PlaylistResult results[]); - - // Image methods - bool getImage(char *imageUrl, Stream *file); - bool getImage(char *imageUrl, uint8_t **image, int *imageLength); - - int portNumber = 443; - int currentlyPlayingBufferSize = 3000; - int playerDetailsBufferSize = 2000; - int getDevicesBufferSize = 3000; - int searchDetailsBufferSize = 3000; - int playlistDetailsBufferSize = 3000; - bool autoTokenRefresh = true; - Client *client; - void lateInit(const char *clientId, const char *clientSecret, const char *refreshToken = ""); - -#ifdef SPOTIFY_DEBUG - char *stack_start; -#endif - -private: - char _bearerToken[SPOTIFY_ACCESS_TOKEN_LENGTH + 10]; //10 extra is for "bearer " at the start - char *_refreshToken; - const char *_clientId; - const char *_clientSecret; - unsigned int timeTokenRefreshed; - unsigned int tokenTimeToLiveMs; - int commonGetImage(char *imageUrl); - int getContentLength(); - int getHttpStatusCode(); - void skipHeaders(bool tossUnexpectedForJSON = true); - void closeClient(); - void parseError(); - const char *requestAccessTokensBody = - R"(grant_type=authorization_code&code=%s&redirect_uri=%s&client_id=%s&client_secret=%s)"; - const char *refreshAccessTokensBody = - R"(grant_type=refresh_token&refresh_token=%s&client_id=%s&client_secret=%s)"; -#ifdef SPOTIFY_DEBUG - void printStack(); -#endif -}; - -#endif -/* -SpotifyArduino - An Arduino library to wrap the Spotify API - Copyright (c) 2021 Brian Lough. This library is free software; you can redistribute it and/or @@ -1292,7 +1025,7 @@ int SpotifyArduino::searchForSong(String query, int limit, processSearch searchC return statusCode; } -int SpotifyArduino::getPlaylist(String query, int limit, processPlaylist playlistCallback, PlaylistResult results[]) +int SpotifyArduino::getPlaylist(String query, int limit, String market, processPlaylist playlistCallback, PlaylistResult results[]) { #ifdef SPOTIFY_DEBUG @@ -1307,7 +1040,7 @@ int SpotifyArduino::getPlaylist(String query, int limit, processPlaylist playlis checkAndRefreshAccessToken(); } - int statusCode = makeGetRequest((SPOTIFY_PLAYLIST_ENDPOINT + query).c_str(), _bearerToken); + int statusCode = makeGetRequest((SPOTIFY_PLAYLIST_ENDPOINT + query + "?market=" + market + "&fields=tracks.items%28track%28name%2Curi%2Calbum%28name%2Curi%29%2Cartists%28name%2Curi%29%29").c_str(), _bearerToken); #ifdef SPOTIFY_DEBUG Serial.print("Status Code: "); Serial.println(statusCode); @@ -1330,6 +1063,7 @@ int SpotifyArduino::getPlaylist(String query, int limit, processPlaylist playlis ReadLoggingStream loggingStream(*client, Serial); DeserializationError error = deserializeJson(doc, loggingStream); #endif + //serializeJsonPretty(doc, Serial); if (!error) { @@ -1345,35 +1079,11 @@ int SpotifyArduino::getPlaylist(String query, int limit, processPlaylist playlis JsonObject result = doc["tracks"]["items"][i]["track"]; playlistResult.trackUri = result["uri"].as(); playlistResult.trackName = result["name"].as(); - Serial.println(playlistResult.trackName); playlistResult.albumUri = result["album"]["uri"].as(); playlistResult.albumName = result["album"]["name"].as(); - //Pull artist Information for the result - uint8_t totalArtists = result["artists"].size(); - playlistResult.numArtists = totalArtists; - - SpotifyArtist artist; - for (int j = 0; j < totalArtists; j++) - { - JsonObject artistResult = result["artists"][j]; - artist.artistName = artistResult["name"].as(); - artist.artistUri = artistResult["uri"].as(); - playlistResult.artists[j] = artist; - } - - uint8_t totalImages = result["album"]["images"].size(); - playlistResult.numImages = totalImages; - - SpotifyImage image; - for (int j = 0; j < totalImages; j++) - { - JsonObject imageResult = result["album"]["images"][j]; - image.height = imageResult["height"].as(); - image.width = imageResult["width"].as(); - image.url = imageResult["url"].as(); - playlistResult.albumImages[j] = image; - } + playlistResult.artistName = result["artists"][0]["name"]; + playlistResult.artistUri = result["artists"][0]["uri"]; results[i] = playlistResult; From 98870ec02f692c68a3d6055d37d44df229d6960c Mon Sep 17 00:00:00 2001 From: Flavio Sperandeo <55345633+thegeek-sys@users.noreply.github.com> Date: Thu, 13 Feb 2025 10:11:13 +0100 Subject: [PATCH 6/6] edited PlaylistResult struct to make it fit up to 100 songs --- src/SpotifyArduino.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/SpotifyArduino.h b/src/SpotifyArduino.h index b2ed145..5f7dffc 100644 --- a/src/SpotifyArduino.h +++ b/src/SpotifyArduino.h @@ -84,7 +84,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define SPOTIFY_NUM_ALBUM_IMAGES 3 // Max spotify returns is 3, but the third one is probably too big for an ESP -#define SPOTIFY_MAX_NUM_ARTISTS 5 +#define SPOTIFY_MAX_NUM_ARTISTS 5 #define SPOTIFY_ACCESS_TOKEN_LENGTH 309 @@ -154,10 +154,8 @@ struct PlaylistResult const char *albumUri; const char *trackName; const char *trackUri; - SpotifyArtist artists[SPOTIFY_MAX_NUM_ARTISTS]; - SpotifyImage albumImages[SPOTIFY_NUM_ALBUM_IMAGES]; - int numArtists; - int numImages; + const char *artistName; + const char *artistUri; }; struct CurrentlyPlaying @@ -224,7 +222,7 @@ class SpotifyArduino int searchForSong(String query, int limit, processSearch searchCallback, SearchResult results[]); //Playlist - int getPlaylist(String query, int limit, processPlaylist playlistCallback, PlaylistResult results[]); + int getPlaylist(String query, int limit, String market, processPlaylist playlistCallback, PlaylistResult results[]); // Image methods bool getImage(char *imageUrl, Stream *file);