2024-11-01 14:10:00 +01:00
|
|
|
#ifndef _UTILS_
|
|
|
|
#define _UTILS_
|
2024-11-01 15:04:43 +01:00
|
|
|
#include <curl/include/curl/curl.h>
|
2024-11-02 17:17:12 +01:00
|
|
|
#include <wx/mstream.h>
|
|
|
|
#include <wx/wx.h>
|
2024-11-01 15:04:43 +01:00
|
|
|
|
2024-11-01 14:10:00 +01:00
|
|
|
#include <filesystem>
|
|
|
|
#include <fstream>
|
|
|
|
#include <nlohmann-json/single_include/nlohmann/json.hpp>
|
|
|
|
#include <sstream>
|
|
|
|
#include <string>
|
2024-11-03 12:56:42 +01:00
|
|
|
#include <vector>
|
2024-11-01 14:10:00 +01:00
|
|
|
|
|
|
|
#define DEFAULT_CLIENT_ID "1301849203378622545"
|
|
|
|
#define DEFAULT_APP_NAME "Music"
|
2024-11-03 12:56:42 +01:00
|
|
|
#define CONFIG_FILENAME "settings.json"
|
2024-11-01 14:10:00 +01:00
|
|
|
|
|
|
|
namespace utils {
|
2024-11-03 12:56:42 +01:00
|
|
|
struct App {
|
|
|
|
bool enabled;
|
|
|
|
std::string appName;
|
|
|
|
std::string clientId;
|
|
|
|
std::string searchEndpoint;
|
|
|
|
std::vector<std::string> processNames;
|
|
|
|
};
|
|
|
|
|
2024-11-04 16:09:00 +01:00
|
|
|
struct Settings {
|
|
|
|
bool autoStart;
|
|
|
|
bool anyOtherEnabled;
|
|
|
|
std::vector<App> apps;
|
|
|
|
};
|
|
|
|
|
2024-11-02 17:17:12 +01:00
|
|
|
inline wxIcon loadIconFromMemory(const unsigned char* data, size_t size) {
|
|
|
|
wxMemoryInputStream stream(data, size);
|
2024-11-03 12:56:42 +01:00
|
|
|
wxImage img(stream, wxBITMAP_TYPE_PNG);
|
2024-11-02 17:17:12 +01:00
|
|
|
if (img.IsOk()) {
|
|
|
|
wxBitmap bmp(img);
|
|
|
|
wxIcon icon;
|
|
|
|
icon.CopyFromBitmap(bmp);
|
|
|
|
return icon;
|
|
|
|
}
|
|
|
|
return wxNullIcon;
|
|
|
|
}
|
2024-11-03 12:56:42 +01:00
|
|
|
|
2024-11-01 16:49:48 +01:00
|
|
|
inline std::string ltrim(std::string& s) {
|
|
|
|
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline std::string rtrim(std::string& s) {
|
|
|
|
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end());
|
|
|
|
return s;
|
|
|
|
}
|
2024-11-03 12:56:42 +01:00
|
|
|
|
2024-11-01 16:49:48 +01:00
|
|
|
inline std::string trim(std::string& s) {
|
|
|
|
ltrim(s);
|
|
|
|
rtrim(s);
|
|
|
|
return s;
|
|
|
|
}
|
2024-11-03 12:56:42 +01:00
|
|
|
|
2024-11-01 15:04:43 +01:00
|
|
|
inline std::string urlEncode(std::string str) {
|
2024-11-04 15:32:46 +01:00
|
|
|
std::ostringstream encoded;
|
|
|
|
encoded << std::hex << std::uppercase;
|
|
|
|
|
|
|
|
for (unsigned char c : str) {
|
|
|
|
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
|
|
|
|
encoded << c;
|
|
|
|
} else {
|
|
|
|
encoded << '%' << std::setw(2) << std::setfill('0') << static_cast<int>(c);
|
2024-11-01 15:04:43 +01:00
|
|
|
}
|
|
|
|
}
|
2024-11-04 15:32:46 +01:00
|
|
|
|
|
|
|
return encoded.str();
|
2024-11-01 15:04:43 +01:00
|
|
|
}
|
2024-11-01 16:49:48 +01:00
|
|
|
|
2024-11-01 15:04:43 +01:00
|
|
|
inline size_t curlWriteCallback(char* contents, size_t size, size_t nmemb, void* userp) {
|
|
|
|
((std::string*)userp)->append((char*)contents, size * nmemb);
|
|
|
|
return size * nmemb;
|
|
|
|
}
|
2024-11-01 16:49:48 +01:00
|
|
|
|
2024-11-01 15:04:43 +01:00
|
|
|
inline std::string getRequest(std::string url) {
|
|
|
|
CURL* curl;
|
|
|
|
CURLcode res;
|
|
|
|
std::string buf;
|
|
|
|
curl_global_init(CURL_GLOBAL_ALL);
|
|
|
|
curl = curl_easy_init();
|
|
|
|
if (curl) {
|
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
|
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCallback);
|
2024-11-01 16:49:48 +01:00
|
|
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
2024-11-01 15:04:43 +01:00
|
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
|
|
|
|
res = curl_easy_perform(curl);
|
|
|
|
curl_easy_cleanup(curl);
|
|
|
|
}
|
|
|
|
curl_global_cleanup();
|
|
|
|
return buf;
|
|
|
|
}
|
2024-11-01 16:49:48 +01:00
|
|
|
|
2024-11-01 15:04:43 +01:00
|
|
|
inline std::string getArtworkURL(std::string query) {
|
|
|
|
std::string response =
|
|
|
|
getRequest("https://itunes.apple.com/search?media=music&entity=song&term=" + urlEncode(query));
|
|
|
|
nlohmann::json j = nlohmann::json::parse(response);
|
|
|
|
auto results = j["results"];
|
|
|
|
if (results.size() > 0) {
|
|
|
|
return results[0]["artworkUrl100"].get<std::string>();
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
2024-11-04 16:09:00 +01:00
|
|
|
inline void saveSettings(const App* newApp) {
|
|
|
|
nlohmann::json j;
|
2024-11-01 16:49:48 +01:00
|
|
|
|
2024-11-04 16:09:00 +01:00
|
|
|
if (std::filesystem::exists(CONFIG_FILENAME)) {
|
|
|
|
std::ifstream i(CONFIG_FILENAME);
|
|
|
|
i >> j;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!j.contains("apps")) {
|
|
|
|
j["apps"] = nlohmann::json::array();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool appFound = false;
|
|
|
|
|
|
|
|
for (auto& appJson : j["apps"]) {
|
|
|
|
if (appJson["name"] == newApp->appName) {
|
|
|
|
appJson["client_id"] = newApp->clientId;
|
|
|
|
appJson["search_endpoint"] = newApp->searchEndpoint;
|
|
|
|
appJson["enabled"] = newApp->enabled;
|
|
|
|
|
|
|
|
appJson["process_names"].clear();
|
|
|
|
for (const auto& processName : newApp->processNames) {
|
|
|
|
appJson["process_names"].push_back(processName);
|
|
|
|
}
|
|
|
|
|
|
|
|
appFound = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!appFound) {
|
|
|
|
nlohmann::json appJson;
|
|
|
|
appJson["name"] = newApp->appName;
|
|
|
|
appJson["client_id"] = newApp->clientId;
|
|
|
|
appJson["search_endpoint"] = newApp->searchEndpoint;
|
|
|
|
appJson["enabled"] = newApp->enabled;
|
|
|
|
|
|
|
|
for (const auto& processName : newApp->processNames) {
|
|
|
|
appJson["process_names"].push_back(processName);
|
|
|
|
}
|
|
|
|
|
|
|
|
j["apps"].push_back(appJson);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ofstream o(CONFIG_FILENAME);
|
|
|
|
o << j.dump(4);
|
|
|
|
o.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void saveSettings(const Settings& settings) {
|
|
|
|
nlohmann::json j;
|
|
|
|
j["autostart"] = settings.autoStart;
|
|
|
|
j["any_other"] = settings.anyOtherEnabled;
|
|
|
|
|
|
|
|
for (const auto& app : settings.apps) {
|
|
|
|
nlohmann::json appJson;
|
|
|
|
appJson["name"] = app.appName;
|
|
|
|
appJson["client_id"] = app.clientId;
|
|
|
|
appJson["search_endpoint"] = app.searchEndpoint;
|
|
|
|
appJson["enabled"] = app.enabled;
|
2024-11-03 12:56:42 +01:00
|
|
|
|
2024-11-04 16:09:00 +01:00
|
|
|
for (const auto& processName : app.processNames) appJson["process_names"].push_back(processName);
|
|
|
|
|
|
|
|
j["apps"].push_back(appJson);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ofstream o(CONFIG_FILENAME);
|
|
|
|
o << j.dump(4);
|
|
|
|
o.close();
|
|
|
|
}
|
|
|
|
inline Settings getSettings() {
|
|
|
|
Settings ret;
|
|
|
|
if (!std::filesystem::exists(CONFIG_FILENAME))
|
|
|
|
return ret;
|
2024-11-01 14:10:00 +01:00
|
|
|
|
|
|
|
try {
|
2024-11-04 16:09:00 +01:00
|
|
|
std::ifstream i(CONFIG_FILENAME);
|
|
|
|
nlohmann::json j;
|
|
|
|
i >> j;
|
|
|
|
|
|
|
|
ret.autoStart = j.value("autostart", false);
|
|
|
|
ret.anyOtherEnabled = j.value("any_other", false);
|
|
|
|
|
|
|
|
for (const auto& app : j["apps"]) {
|
2024-11-03 12:56:42 +01:00
|
|
|
App a;
|
2024-11-04 16:09:00 +01:00
|
|
|
a.appName = app.value("name", "");
|
|
|
|
a.clientId = app.value("client_id", "");
|
|
|
|
a.searchEndpoint = app.value("search_endpoint", "");
|
|
|
|
a.enabled = app.value("enabled", false);
|
|
|
|
|
|
|
|
for (const auto& process : app["process_names"]) a.processNames.push_back(process.get<std::string>());
|
|
|
|
|
|
|
|
ret.apps.push_back(a);
|
2024-11-01 14:10:00 +01:00
|
|
|
}
|
2024-11-04 16:09:00 +01:00
|
|
|
} catch (const nlohmann::json::parse_error&) {
|
|
|
|
} // TODO: handle error
|
|
|
|
return ret;
|
2024-11-03 12:56:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
inline App getApp(std::string processName) {
|
2024-11-04 16:09:00 +01:00
|
|
|
auto settings = getSettings();
|
|
|
|
for (auto app : settings.apps) {
|
2024-11-04 15:32:46 +01:00
|
|
|
for (auto procName : app.processNames) {
|
|
|
|
if (procName == processName)
|
2024-11-03 12:56:42 +01:00
|
|
|
return app;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
App a;
|
|
|
|
a.clientId = DEFAULT_CLIENT_ID;
|
|
|
|
a.appName = DEFAULT_APP_NAME;
|
2024-11-04 16:09:00 +01:00
|
|
|
a.enabled = settings.anyOtherEnabled;
|
2024-11-03 12:56:42 +01:00
|
|
|
a.searchEndpoint = "";
|
|
|
|
return a;
|
2024-11-01 14:10:00 +01:00
|
|
|
}
|
|
|
|
} // namespace utils
|
|
|
|
|
|
|
|
#undef DEFAULT_APP_NAME
|
|
|
|
#undef CONFIG_FILENAME
|
|
|
|
#undef DEFAULT_CLIENT_ID
|
|
|
|
#endif
|