Mercurial > wgsdk
annotate src/deezer.c @ 12:dd427b7cc459 default tip
json: replace with nxjson library
more lightweight, reduces the binary size by about 40 kb
| author | Paper <paper@paper.us.eu.org> |
|---|---|
| date | Fri, 15 Mar 2024 20:46:18 -0400 |
| parents | e6a594f16403 |
| children |
| rev | line source |
|---|---|
| 11 | 1 #include "json.h" |
| 2 #include "utils.h" | |
| 3 | |
| 4 #include <windef.h> | |
| 5 #include <minwinbase.h> | |
| 6 #include <winhttp.h> | |
| 7 #include <winuser.h> | |
| 8 | |
| 9 HINTERNET session = NULL; | |
| 10 HINTERNET connection = NULL; | |
| 11 | |
| 12 /* preferably we would use some other API for this, but | |
| 13 * meh | |
| 14 */ | |
| 15 static int init_winhttp(void) { | |
| 16 session = WinHttpOpen( | |
| 17 NULL, | |
| 18 WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, | |
| 19 WINHTTP_NO_PROXY_NAME, | |
| 20 WINHTTP_NO_PROXY_BYPASS, | |
| 21 0 | |
| 22 ); | |
| 23 | |
| 24 return !!session; | |
| 25 } | |
| 26 | |
| 27 static int init_connect(void) { | |
| 28 if (!session) | |
| 29 return 0; | |
| 30 | |
| 31 connection = WinHttpConnect( | |
| 32 session, | |
| 33 L"api.deezer.com", | |
| 34 /* require HTTPS, we aren't in 2001 anymore */ | |
| 35 INTERNET_DEFAULT_HTTPS_PORT, | |
| 36 0 | |
| 37 ); | |
| 38 | |
| 39 return !!connection; | |
| 40 } | |
| 41 | |
| 42 /* do this on exit */ | |
| 43 void close_open_http_handles(void) { | |
| 44 if (session) WinHttpCloseHandle(session); | |
| 45 if (connection) WinHttpCloseHandle(connection); | |
| 46 } | |
| 47 | |
| 48 /* return MUST be free'd */ | |
| 49 static LPCWSTR deezer_get_thumbnail_build_query(LPCWSTR restrict artist, LPCWSTR restrict album) { | |
| 50 static LPCWSTR begin = L"/search/track?strict=on&q=artist:\""; | |
| 51 static LPCWSTR album_query = L" album:\""; | |
| 52 | |
| 53 size_t len = wcslen(begin) + wcslen(artist) + 1 /* quote */; | |
| 54 if (album && album[0]) { | |
| 55 len += 1 /* space */ + wcslen(album_query) + wcslen(album) + 1 /* quote */; | |
| 56 } | |
| 57 | |
| 58 LPWSTR final = calloc(len + 1, sizeof(WCHAR)); | |
| 59 | |
| 60 wcscpy(final, begin); | |
| 61 wcscat(final, artist); | |
| 62 wcscat(final, L"\""); | |
| 63 if (album && album[0]) { | |
| 64 wcscat(final, album_query); | |
| 65 wcscat(final, album); | |
| 66 wcscat(final, L"\""); | |
| 67 } | |
| 68 | |
| 69 return final; | |
| 70 } | |
| 71 | |
|
12
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
72 static int deezer_album_object_get_cover(const nx_json* album, const char* name, char** url) { |
| 11 | 73 if (!url) return 0; |
| 74 | |
|
12
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
75 const nx_json* cover = nx_json_get(album, name); |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
76 if (!cover) |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
77 return 0; |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
78 |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
79 free(*url); |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
80 |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
81 size_t len = strlen(cover->text_value); |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
82 *url = malloc((len + 1) * sizeof(char)); |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
83 (*url)[len] = '\0'; |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
84 |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
85 strncpy(*url, cover->text_value, len); |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
86 return 1; |
| 11 | 87 } |
| 88 | |
| 89 | |
| 90 static int deezer_get_thumbnail_download_url(LPCWSTR restrict query, char** restrict data, size_t* restrict size) { | |
| 91 if (!data || !size) | |
| 92 return -1; | |
| 93 | |
| 94 HINTERNET request = WinHttpOpenRequest( | |
| 95 connection, | |
| 96 L"GET", | |
| 97 query, | |
| 98 NULL, | |
| 99 WINHTTP_NO_REFERER, | |
| 100 WINHTTP_DEFAULT_ACCEPT_TYPES, | |
| 101 WINHTTP_FLAG_SECURE | WINHTTP_FLAG_ESCAPE_PERCENT | |
| 102 ); | |
| 103 if (!request) | |
| 104 return -1; | |
| 105 | |
| 106 BOOL result = WinHttpSendRequest( | |
| 107 request, | |
| 108 L"Content-Type: application/json; charset=utf-8", | |
| 109 0, | |
| 110 WINHTTP_NO_REQUEST_DATA, | |
| 111 0, | |
| 112 0, | |
| 113 0 | |
| 114 ); | |
| 115 if (!result) { | |
| 116 WinHttpCloseHandle(request); | |
| 117 return -1; | |
| 118 } | |
| 119 | |
| 120 result = WinHttpReceiveResponse(request, NULL); | |
| 121 if (!result) { | |
| 122 WinHttpCloseHandle(request); | |
| 123 return -1; | |
| 124 } | |
| 125 | |
| 126 DWORD data_available = 0; | |
| 127 DWORD data_downloaded = 0; | |
| 128 do { | |
| 129 data_available = 0; | |
| 130 if (!WinHttpQueryDataAvailable(request, &data_available)) { | |
| 131 WinHttpCloseHandle(request); | |
| 132 return -1; | |
| 133 } | |
| 134 | |
| 135 if (!data_available) | |
| 136 break; | |
| 137 | |
| 138 *data = realloc(*data, *size + data_available + 1); | |
| 139 (*data)[*size + data_available] = '\0'; | |
| 140 if (!WinHttpReadData(request, *data + *size, data_available, &data_downloaded)) { | |
| 141 WinHttpCloseHandle(request); | |
| 142 return -1; | |
| 143 } | |
| 144 *size += data_downloaded; | |
| 145 } while (data_available > 0); | |
| 146 | |
| 147 WinHttpCloseHandle(request); | |
| 148 return 0; | |
| 149 } | |
| 150 | |
|
12
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
151 /* note: please prefix these */ |
| 11 | 152 enum deezer_thumbnail_state { |
| 153 ERROR = 0, | |
| 154 ALBUM, | |
| 155 ARTIST, | |
| 156 }; | |
| 157 | |
|
12
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
158 static enum deezer_thumbnail_state deezer_get_thumbnail_parse_search_result(const nx_json* json, char** cover_url, int get_artist) { |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
159 const nx_json* album = nx_json_get(json, "album"); |
| 11 | 160 |
|
12
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
161 if (album->type == NX_JSON_OBJECT) { |
| 11 | 162 if (deezer_album_object_get_cover(album, "cover_medium", cover_url) |
| 163 || deezer_album_object_get_cover(album, "cover_large", cover_url) | |
| 164 || deezer_album_object_get_cover(album, "cover_small", cover_url) | |
| 165 || deezer_album_object_get_cover(album, "cover_xl", cover_url)) | |
| 166 return ALBUM; | |
|
12
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
167 } |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
168 |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
169 if (get_artist) { |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
170 const nx_json* artist = nx_json_get(json, "artist"); |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
171 if (artist->type != NX_JSON_OBJECT) |
| 11 | 172 return ERROR; |
| 173 | |
| 174 /* treat artist pictures as a fallback and only retrieve them once */ | |
| 175 if (deezer_album_object_get_cover(artist, "picture_medium", cover_url) | |
| 176 || deezer_album_object_get_cover(artist, "picture_large", cover_url) | |
| 177 || deezer_album_object_get_cover(artist, "picture_small", cover_url) | |
| 178 || deezer_album_object_get_cover(artist, "picture_xl", cover_url)) | |
| 179 return ARTIST; | |
| 180 } | |
| 181 | |
| 182 return ERROR; | |
| 183 } | |
| 184 | |
|
12
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
185 /* THIS MODIFIES `data` IN PLACE!! */ |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
186 static int deezer_get_thumbnail_parse_json(char** cover_url, char* data) { |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
187 const nx_json* json = nx_json_parse(data, NULL); |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
188 if (!json) |
| 11 | 189 return -1; |
| 190 | |
|
12
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
191 const nx_json* json_data = nx_json_get(json, "data"); |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
192 if (json_data->type != NX_JSON_ARRAY || !json_data->children.length) |
| 11 | 193 return -1; |
| 194 | |
| 195 int have_artist = 0; | |
|
12
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
196 for (size_t i = 0; i < json_data->children.length; i++) { |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
197 const nx_json* result = nx_json_item(json_data, i); |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
198 if (result->type != NX_JSON_OBJECT) |
| 11 | 199 return -1; |
| 200 | |
| 201 enum deezer_thumbnail_state state = deezer_get_thumbnail_parse_search_result(result, cover_url, have_artist); | |
| 202 switch (state) { | |
| 203 case ERROR: return -1; | |
| 204 case ARTIST: have_artist = 1; break; | |
| 205 case ALBUM: return 0; | |
| 206 } | |
| 207 } | |
| 208 | |
|
12
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
209 nx_json_free(json); |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
210 |
| 11 | 211 return !have_artist; |
| 212 } | |
| 213 | |
| 214 char* deezer_get_thumbnail(LPCWSTR restrict artist, LPCWSTR restrict album) { | |
| 215 char* response_data = NULL; | |
| 216 size_t response_size = 0; | |
| 217 LPCWSTR query = NULL; | |
| 218 char* cover_url = NULL; | |
| 219 | |
| 220 /* make sure everything is OK */ | |
| 221 if (!(session || init_winhttp()) || !(connection || init_connect()) || (!artist || !artist[0])) | |
| 222 return NULL; | |
| 223 | |
| 224 query = deezer_get_thumbnail_build_query(artist, album); | |
| 225 if (!query) | |
| 226 return NULL; | |
| 227 | |
| 228 if (deezer_get_thumbnail_download_url(query, &response_data, &response_size)) | |
| 229 return NULL; | |
| 230 | |
|
12
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
231 if (deezer_get_thumbnail_parse_json(&cover_url, response_data)) |
| 11 | 232 return NULL; |
| 233 | |
|
12
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
234 free(response_data); |
|
dd427b7cc459
json: replace with nxjson library
Paper <paper@paper.us.eu.org>
parents:
11
diff
changeset
|
235 |
| 11 | 236 return cover_url; |
| 237 } |
