Mercurial > wgsdk
view src/deezer.c @ 11:e6a594f16403
*: huge refactor
the config file has changed drastically, moving to an ini file from
that custom format; i *would* have used the win32 functions for those,
but they were barely functional, so I decided on using ini.h which is
lightweight enough.
additionally, I've added Deezer support so album art will be displayed!
unfortunately though winhttp is a pain in the ass so if I send a request
with any form of unicode chars in it it just returns a "bad request" error.
I've tried debugging this but I could never really come up with anything:
my hypothesis is that deezer expects their characters in percent-encoded
UTF-8, but winhttp is sending them in some other encoding.
the config dialog was moved out of config.c (overdue) and many more options
are given in the config as well.
main.c has been renamed to plugin.c to better differentiate it from...
everything else.
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Thu, 14 Mar 2024 20:25:37 -0400 |
parents | |
children | dd427b7cc459 |
line wrap: on
line source
#include "json.h" #include "utils.h" #include <windef.h> #include <minwinbase.h> #include <winhttp.h> #include <winuser.h> HINTERNET session = NULL; HINTERNET connection = NULL; /* preferably we would use some other API for this, but * meh */ static int init_winhttp(void) { session = WinHttpOpen( NULL, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 ); return !!session; } static int init_connect(void) { if (!session) return 0; connection = WinHttpConnect( session, L"api.deezer.com", /* require HTTPS, we aren't in 2001 anymore */ INTERNET_DEFAULT_HTTPS_PORT, 0 ); return !!connection; } /* do this on exit */ void close_open_http_handles(void) { if (session) WinHttpCloseHandle(session); if (connection) WinHttpCloseHandle(connection); } /* return MUST be free'd */ static LPCWSTR deezer_get_thumbnail_build_query(LPCWSTR restrict artist, LPCWSTR restrict album) { static LPCWSTR begin = L"/search/track?strict=on&q=artist:\""; static LPCWSTR album_query = L" album:\""; size_t len = wcslen(begin) + wcslen(artist) + 1 /* quote */; if (album && album[0]) { len += 1 /* space */ + wcslen(album_query) + wcslen(album) + 1 /* quote */; } LPWSTR final = calloc(len + 1, sizeof(WCHAR)); wcscpy(final, begin); wcscat(final, artist); wcscat(final, L"\""); if (album && album[0]) { wcscat(final, album_query); wcscat(final, album); wcscat(final, L"\""); } return final; } static int deezer_album_object_get_cover(cJSON* restrict album, const char* restrict name, char** restrict url) { if (!url) return 0; cJSON* cover = cJSON_GetObjectItemCaseSensitive(album, name); if (cover) { if (*url) free(*url); size_t len = strlen(cover->valuestring); *url = malloc((len + 1) * sizeof(char)); (*url)[len] = '\0'; strncpy(*url, cover->valuestring, len); return 1; } else return 0; } static int deezer_get_thumbnail_download_url(LPCWSTR restrict query, char** restrict data, size_t* restrict size) { if (!data || !size) return -1; HINTERNET request = WinHttpOpenRequest( connection, L"GET", query, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE | WINHTTP_FLAG_ESCAPE_PERCENT ); if (!request) return -1; BOOL result = WinHttpSendRequest( request, L"Content-Type: application/json; charset=utf-8", 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0 ); if (!result) { WinHttpCloseHandle(request); return -1; } result = WinHttpReceiveResponse(request, NULL); if (!result) { WinHttpCloseHandle(request); return -1; } DWORD data_available = 0; DWORD data_downloaded = 0; do { data_available = 0; if (!WinHttpQueryDataAvailable(request, &data_available)) { WinHttpCloseHandle(request); return -1; } if (!data_available) break; *data = realloc(*data, *size + data_available + 1); (*data)[*size + data_available] = '\0'; if (!WinHttpReadData(request, *data + *size, data_available, &data_downloaded)) { WinHttpCloseHandle(request); return -1; } *size += data_downloaded; } while (data_available > 0); WinHttpCloseHandle(request); return 0; } enum deezer_thumbnail_state { ERROR = 0, ALBUM, ARTIST, }; static enum deezer_thumbnail_state deezer_get_thumbnail_parse_search_result(cJSON* json, char** restrict cover_url, int get_artist) { cJSON* album = cJSON_GetObjectItemCaseSensitive(json, "album"); if (cJSON_IsObject(album)) { if (deezer_album_object_get_cover(album, "cover_medium", cover_url) || deezer_album_object_get_cover(album, "cover_large", cover_url) || deezer_album_object_get_cover(album, "cover_small", cover_url) || deezer_album_object_get_cover(album, "cover_xl", cover_url)) return ALBUM; } else if (get_artist) { cJSON* artist = cJSON_GetObjectItemCaseSensitive(json, "artist"); if (!cJSON_IsObject(artist)) return ERROR; /* treat artist pictures as a fallback and only retrieve them once */ if (deezer_album_object_get_cover(artist, "picture_medium", cover_url) || deezer_album_object_get_cover(artist, "picture_large", cover_url) || deezer_album_object_get_cover(artist, "picture_small", cover_url) || deezer_album_object_get_cover(artist, "picture_xl", cover_url)) return ARTIST; } return ERROR; } static int deezer_get_thumbnail_parse_json(char** restrict cover_url, const char* restrict data, size_t size) { cJSON* json = cJSON_ParseWithLength(data, size); if (!json) { const char* err_ptr = cJSON_GetErrorPtr(); if (err_ptr) MessageBoxA(NULL, err_ptr, "wgsdk: Error parsing Deezer JSON!", MB_ICONERROR | MB_OK); cJSON_Delete(json); return -1; } cJSON* json_data = cJSON_GetObjectItemCaseSensitive(json, "data"); size_t json_data_size; if (!cJSON_IsArray(json_data) || !(json_data_size = cJSON_GetArraySize(json_data))) return -1; int have_artist = 0; for (size_t i = 0; i < json_data_size; i++) { cJSON* result = cJSON_GetArrayItem(json_data, i); if (!cJSON_IsObject(result)) return -1; enum deezer_thumbnail_state state = deezer_get_thumbnail_parse_search_result(result, cover_url, have_artist); switch (state) { case ERROR: return -1; case ARTIST: have_artist = 1; break; case ALBUM: return 0; } } cJSON_Delete(json); return !have_artist; } char* deezer_get_thumbnail(LPCWSTR restrict artist, LPCWSTR restrict album) { char* response_data = NULL; size_t response_size = 0; LPCWSTR query = NULL; char* cover_url = NULL; /* make sure everything is OK */ if (!(session || init_winhttp()) || !(connection || init_connect()) || (!artist || !artist[0])) return NULL; query = deezer_get_thumbnail_build_query(artist, album); if (!query) return NULL; if (deezer_get_thumbnail_download_url(query, &response_data, &response_size)) return NULL; if (deezer_get_thumbnail_parse_json(&cover_url, response_data, response_size)) return NULL; return cover_url; }