mirror of
https://github.com/sascha-hemi/SpotifyEsp32.git
synced 2026-03-21 03:03:58 +01:00
337 lines
17 KiB
C++
337 lines
17 KiB
C++
#ifndef SpotifyESP32
|
|
#define SpotifyESP32
|
|
|
|
#include <Arduino.h>
|
|
#include <WiFi.h>
|
|
#include <HTTPClient.h>
|
|
#include <ArduinoJson.h>
|
|
#include <base64.h>
|
|
#include <UrlEncode.h>
|
|
#include <map>
|
|
#include <regex>
|
|
|
|
namespace Spotify_types{
|
|
extern bool SHUFFLE_ON;//Shuffle on
|
|
extern bool SHUFFLE_OFF;//Shuffle off
|
|
extern char* REPEAT_OFF;//Repeat off
|
|
extern char* REPEAT_TRACK;//Repeat track
|
|
extern char* REPEAT_CONTEXT;//Repeat context
|
|
extern char* TYPE_ALBUM;//URI type album
|
|
extern char* TYPE_ARTIST;//URI type artist
|
|
extern char* TYPE_TRACK;//URI type track
|
|
extern char* TYPE_PLAYLIST;//URI type playlist
|
|
extern char* GROUP_ALBUM;//Get artist's albums include groups album
|
|
extern char* GROUP_SINGLE;//Get artist's albums include groups single
|
|
extern char* GROUP_APPEARS_ON;//Get artist's albums include groups appears on
|
|
extern char* GROUP_COMPILATION;//Get artist's albums include groups compilation
|
|
extern char* TIME_RANGE_SHORT;//4 weeks
|
|
extern char* TIME_RANGE_MEDIUM;//6 months
|
|
extern char* TIME_RANGE_LONG;//Several years
|
|
extern char* TOP_TYPE_ARTIST;//Users top artist's
|
|
extern char* TOP_TYPE_TRACKS;//Users top tracks
|
|
extern int SIZE_OF_URI;//Size to allocate for uri
|
|
extern int SIZE_OF_ID;//Size to allocate for id
|
|
};
|
|
typedef struct{
|
|
int status_code;
|
|
String reply;
|
|
} response;//Response object containing http status code and reply
|
|
|
|
struct recommendations {
|
|
char** seed_artists;
|
|
int seed_artists_size = 0;
|
|
char** seed_genres;
|
|
int seed_genres_size = 0;
|
|
char** seed_tracks;
|
|
int seed_tracks_size = 0;
|
|
float min_acousticness = -1.0;
|
|
float max_acousticness = -1.0;
|
|
float target_acousticness = -1.0;
|
|
float min_danceability = -1.0;
|
|
float max_danceability = -1.0;
|
|
float target_danceability = -1.0;
|
|
float min_duration_ms = -1.0;
|
|
float max_duration_ms = -1.0;
|
|
float target_duration_ms = -1.0;
|
|
float min_energy = -1.0;
|
|
float max_energy = -1.0;
|
|
float target_energy = -1.0;
|
|
float min_instrumentalness = -1.0;
|
|
float max_instrumentalness = -1.0;
|
|
float target_instrumentalness = -1.0;
|
|
float min_key = -1.0;
|
|
float max_key = -1.0;
|
|
float target_key = -1.0;
|
|
float min_liveness = -1.0;
|
|
float max_liveness = -1.0;
|
|
float target_liveness = -1.0;
|
|
float min_loudness = -1.0;
|
|
float max_loudness = -1.0;
|
|
float target_loudness = -1.0;
|
|
float min_mode = -1.0;
|
|
float max_mode = -1.0;
|
|
float target_mode = -1.0;
|
|
float min_popularity = -1.0;
|
|
float max_popularity = -1.0;
|
|
float target_popularity = -1.0;
|
|
float min_speechiness = -1.0;
|
|
float max_speechiness = -1.0;
|
|
float target_speechiness = -1.0;
|
|
float min_tempo = -1.0;
|
|
float max_tempo = -1.0;
|
|
float target_tempo = -1.0;
|
|
float min_time_signature = -1.0;
|
|
float max_time_signature = -1.0;
|
|
float target_time_signature = -1.0;
|
|
float min_valence = -1.0;
|
|
float max_valence = -1.0;
|
|
float target_valence = -1.0;
|
|
};//Fill in the values you want to use for recommendations, at least one seed is required
|
|
|
|
|
|
void print_response(response response_obj);//Print response object
|
|
|
|
class Spotify {
|
|
public:
|
|
Spotify(const char* refresh_token, const char* redirect_uri,const char* client_id,const char* client_secret, bool debug_on);
|
|
Spotify(const char* refresh_token, const char* redirect_uri,const char* client_id,const char* client_secret, bool debug_on, int max_num_retry);
|
|
Spotify(const char* refresh_token, const char* redirect_uri,const char* client_id,const char* client_secret);
|
|
//Player
|
|
/*@brief Get information about the user's current playback state, including track, track progress, and active device.
|
|
* @return response object containing http status code and reply
|
|
*/
|
|
response currently_playing();
|
|
/*@brief Start or resume playback. If no device_id is provided, the user's currently active device is the target. If no context is provided, the user's currently playing context (e.g. album, playlist, etc.) is the target.
|
|
* @param context_uri Spotify URI of the context to play (Required)
|
|
* @param offset Indicates from where in the context playback should start, Only works with albums or Playlists(Optional)
|
|
* @param position_ms Indicates from what position in the context playback should start in milliseconds(Optional)
|
|
* @param device_id Id of the device this command is targeting (Optional)
|
|
* @return response object containing http status code and reply
|
|
*/
|
|
response start_resume_playback(char* context_uri, int offset = 0, int position_ms = 0, char* device_id = nullptr);
|
|
/*@brief Start or resume playback.
|
|
* @param size Number of uris in uris array
|
|
* @param uris Array of Spotify URIs of the tracks to play
|
|
* @param device_id Id of the device this command is targeting (Optional)
|
|
* @return response object containing http status code and reply
|
|
*/
|
|
response start_resume_playback(int size, char ** uris ,char* device_id = nullptr);
|
|
/*@brief Start or resume playback on provided device
|
|
* @param device_id Id of the device this command is targeting(Optional)
|
|
* @return response object containing http status code and reply
|
|
*/
|
|
response start_resume_playback(char* device_id = nullptr);
|
|
/*@brief Pause playback on Spotify
|
|
* @return response object containing http status code and reply
|
|
*/
|
|
response pause_playback();
|
|
/*@brief Skip to next track
|
|
* @return response object containing http status code and reply
|
|
*/
|
|
response skip();
|
|
/*@brief Skip to previous track
|
|
* @return response object containing http status code and reply
|
|
*/
|
|
response previous();
|
|
/*@brief get information about the user's available devices
|
|
* @return response object containing http status code and reply
|
|
*/
|
|
response available_devices();
|
|
/*@brief get information about the user's current playback state, including track, track progress, and active device, shuffle etc.
|
|
* @return response object containing http status code and reply
|
|
*/
|
|
response current_playback_state();
|
|
/*@brief Get recently played tracks
|
|
* @param limit The maximum number of items to return. Default: 10. Minimum: 1. Maximum: 50
|
|
*/
|
|
response recently_played_tracks(int limit = 10);
|
|
/*@brief Seek to position of current context
|
|
* @param time_ms Position in milliseconds to seek to, if the value is greater than the length of the track the player will skip to the next track
|
|
* @return response object containing http status code and reply
|
|
*/
|
|
response seek_to_position(int time_ms);
|
|
/*@brief get users queue, response can be empty or containing episode or track objects
|
|
* @return response object containing http status code and reply
|
|
*/
|
|
response get_queue();
|
|
/*@Brief Set repeat mode, allowed values are REPEAT_OFF, REPEAT_TRACK, REPEAT_CONTEXT
|
|
* @param mode Repeat mode
|
|
* @return response object containing http status code and reply
|
|
*/
|
|
response repeat_mode(char* mode);
|
|
/*@Brief Set shuffle mode, allowed values are SHUFFLE_ON, SHUFFLE_OFF
|
|
* @param mode Shuffle mode
|
|
* @return response object containing http status code and reply
|
|
*/
|
|
response shuffle(bool mode);
|
|
/*@Brief Transfer playback to another device
|
|
* @param device_id Id of the device this command is targeting
|
|
* @return response object containing http status code and reply
|
|
*/
|
|
response transfer_playback(char* device_id);
|
|
/*@Brief Set volume, does not work with all devices(eg. does not work on Phones)
|
|
* @param value Volume value between 0 and 100
|
|
* @return response object containing http status code and reply
|
|
*/
|
|
response set_volume(int value);
|
|
/*@Brief Add context to queue
|
|
* @param context_uri Spotify URI of the context to add to queue
|
|
*/
|
|
response add_to_queue(char* context_uri);
|
|
//Albums
|
|
response get_album(char* album_id);
|
|
response get_albums(int size, char** album_ids);
|
|
response get_album_tracks(char* album_id, int limit = 10, int offset = 0);
|
|
response get_users_saved_albums(int limit = 10, int offset = 0);
|
|
response save_albums_for_current_user(int size, char** album_ids);
|
|
response remove_users_saved_albums(int size, char** album_ids);
|
|
response check_useres_saved_albums(int size, char** album_ids);
|
|
response get_new_releases(char* country, int limit = 10, int offset = 0);
|
|
//Artists
|
|
response get_artist(char* artist_id);
|
|
response get_several_artists(int size, char** artist_ids);
|
|
response get_artist_albums(char* artist_id,int size_groups, char** include_groups, int limit = 10, int offset = 0);
|
|
response get_artist_top_tracks(char* artist_id, char* country);
|
|
response get_artist_related_artist(char* artist_id);
|
|
//Audiobooks (Only Available in US, UK, Canada, Ireland, New Zealand and Australia)
|
|
response get_audiobook(char* audiobook_id);
|
|
response get_several_audiobooks(int size, char** audiobook_ids);
|
|
response get_audiobook_chapters(char* audiobook_id, int limit = 10, int offset = 0);
|
|
response get_users_audiobooks(int limit = 10, int offset = 0);
|
|
response save_audiobooks_for_current_user(int size, char** audiobook_ids);
|
|
response remove_audiobooks_for_current_user(int size, char** audiobook_ids);
|
|
response check_users_saved_audiobooks(int size, char** audiobook_ids);
|
|
//Categories
|
|
response get_several_browse_categories(char* country, char* locale, int limit = 10, int offset = 0);
|
|
response get_single_browse_category(char* category_id, char* country, char* locale);
|
|
//Chapters (Only Available in US, UK, Canada, Ireland, New Zealand and Australia)
|
|
response get_chapter(char* chapter_id);
|
|
response get_several_chapters(int size, char** chapter_ids);
|
|
//Episodes
|
|
response get_episode(char* episode_id);
|
|
response get_several_episodes(int size, char** episode_ids);
|
|
response get_users_saved_episodes(int limit = 10, int offset = 0);
|
|
response save_episodes_for_current_user(int size, char** episode_ids);
|
|
response remove_episodes_for_current_user(int size, char** episode_ids);
|
|
response check_users_saved_episodes(int size, char** episode_ids);
|
|
//Genres
|
|
response get_available_genre_seeds();
|
|
//Markets
|
|
response get_available_markets();
|
|
//Playlists
|
|
response get_playlist(char* playlist_id, char* fields);
|
|
response change_playlist_details(char* playlist_id, char* name, bool is_public, bool is_collaborative, char* description);
|
|
response get_playlist_items(char* playlist_id, char* fields, int limit = 10, int offset = 0);
|
|
response update_playlist_items(char* playlist_id, int size, char** uris = nullptr, int range_length = 1, int range_start = 0, int insert_before = 1 );
|
|
response add_items_to_playlist(char* playlist_id, int size, char** uris, int position = 0);
|
|
response remove_playlist_items(char* playlist_id, int size, char** uris);
|
|
response get_current_users_playlists(int limit = 10, int offset = 0);
|
|
response get_user_playlists(char* user_id,int limit = 10, int offset = 0);
|
|
response create_playlist(char* user_id, char* name, bool is_public, bool is_collaborative, char* description);
|
|
response get_featured_playlists(char* country, char* locale, char* timestamp,int limit = 10, int offset = 0);
|
|
response get_category_playlists(char* category_id, char* country, int limit = 10, int offset = 0);
|
|
response get_playlist_cover_image(char* playlist_id);
|
|
response add_custom_playlist_cover_image(char* playlist_id, char* data);
|
|
//Search
|
|
response search(char* q, char* type, int limit = 10, int offset = 0);
|
|
//Shows
|
|
response get_show(char* show_id);
|
|
response get_several_shows(int size, char** show_ids);
|
|
response get_show_episodes(char* show_id, int limit = 10, int offset = 0);
|
|
response get_users_saved_shows(int limit = 10, int offset = 0);
|
|
response save_shows_for_current_user(int size, char** show_ids);
|
|
response remove_shows_for_current_user(int size, char** show_ids);
|
|
response check_users_saved_shows(int size, char** show_ids);
|
|
//Tracks
|
|
response get_track(char* track_id);
|
|
response get_several_tracks(int size, char** track_ids);
|
|
response get_user_saved_tracks(int limit = 10, int offset = 0);
|
|
response save_tracks_for_current_user(int size, char** track_ids);
|
|
response remove_user_saved_tracks(int size, char** track_ids);
|
|
response check_user_saved_tracks(int size, char** track_ids);
|
|
response get_tracks_audio_features(int size, char** track_ids);
|
|
response get_track_audio_features(char* track_id);
|
|
response get_track_audio_analysis(char* track_id);
|
|
response get_recommendations(recommendations& recom, int limit = 10);
|
|
|
|
//Users
|
|
response get_current_user_profile();
|
|
response get_user_top_items(char* type, char* time_range = "medium_term", int limit = 10, int offset = 0);
|
|
response get_user_profile(char* user_id);
|
|
response follow_playlist(char* playlist_id, bool is_public);
|
|
response unfollow_playlist(char* playlist_id);
|
|
response get_followed_artists(char* after, char* type = "artist", int limit = 10);
|
|
response follow_artists_or_users(char* type, int size, char** artist_user_ids);
|
|
response unfollow_artists_or_users(char* type, int size, char** artist_user_ids);
|
|
response check_if_user_follows_artists_or_users(char* type, int size, char** artist_user_ids);
|
|
response check_if_users_follow_playlist(char* playlist_id, int size, char** user_ids);
|
|
|
|
//Simplified versions of the above
|
|
String current_track_name();
|
|
String current_track_id();
|
|
String current_device_id();
|
|
String current_artist_names();
|
|
char* current_device_id(char* device_id);
|
|
|
|
//String convert_id_to_uri(String id, String type);
|
|
char convert_id_to_uri(char* id, char* type);
|
|
char* convert_id_to_uri(char* id, char* type, char* uri);
|
|
bool is_playing();
|
|
|
|
private:
|
|
|
|
static const int _max_num_items = 20;//Number of items to be sent in one request
|
|
static const int _max_char_size = 35*_max_num_items + 150;// 35 beeing the size of a uri + comma + 150 as buffer for url etc.
|
|
static const int _size_of_uri = 45;
|
|
static const int _size_of_id = 25;
|
|
int _max_num_retry = 3;//Max number of retries for a request
|
|
const char* _refresh_token;
|
|
const char* _redirect_uri;
|
|
const char* _client_id;
|
|
const char* _client_secret;
|
|
int _retry;
|
|
bool _debug_on;
|
|
String _access_token;
|
|
bool get_token();//Get access token
|
|
void init_response(response* response_obj);//Initialize response object
|
|
|
|
response RestApiPut(char* rest_url, int payload_size = 0, char* payload = nullptr);
|
|
response RestApiPost(char* rest_url, int payload_size = 0, char* payload = nullptr);
|
|
response RestApiDelete(char* rest_url, char* payload = nullptr);
|
|
response RestApiGet(char* rest_url);
|
|
|
|
char* array_to_char(int size, char** array);//Convert array of chars to one comma separated char
|
|
void array_to_json_array(int size, char** array, char* data, int data_size = _max_char_size);//Convert array of chars to one json array
|
|
bool is_valid_value(float param);//Check if recommendation value is valid
|
|
bool is_valid_value(int param);//Check if recommendation value is valid
|
|
void populate_char_values(std::map<char*, char*>& map, recommendations& recom);//Populate recommendation char values
|
|
void populate_float_values(std::map<char*, float>& map, recommendations& recom);//Populate recommendation float values
|
|
const char * extract_endpoint(char* rest_url);//Extract endpoint from url with regex
|
|
|
|
|
|
|
|
const char* _spotify_root_ca = \
|
|
"-----BEGIN CERTIFICATE-----\n" \
|
|
"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n" \
|
|
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"\
|
|
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n"\
|
|
"QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n"\
|
|
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n"\
|
|
"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n"\
|
|
"9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n"\
|
|
"CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n"\
|
|
"nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n"\
|
|
"43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n"\
|
|
"T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n"\
|
|
"gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n"\
|
|
"BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n"\
|
|
"TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n"\
|
|
"DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n"\
|
|
"hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n"\
|
|
"06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n"\
|
|
"PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n"\
|
|
"YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n"\
|
|
"CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n"\
|
|
"-----END CERTIFICATE-----\n";
|
|
};
|
|
#endif |