From 90607d2f5d1c6962e36b497ea17787dfa9106d9f Mon Sep 17 00:00:00 2001 From: EinTim23 Date: Sat, 10 May 2025 12:27:15 +0200 Subject: [PATCH] - add support for different activity types - switch from process naming matching to app user model id matching on windows - updated example settings file - added exception handling to itunes api --- settings.example.json | 21 ++++++++++++++ src/backends/windows.cpp | 60 ---------------------------------------- src/main.cpp | 2 +- src/utils.hpp | 34 +++++++++++++++++------ 4 files changed, 47 insertions(+), 70 deletions(-) diff --git a/settings.example.json b/settings.example.json index 06682b9..bd2b230 100644 --- a/settings.example.json +++ b/settings.example.json @@ -5,13 +5,34 @@ "client_id": "1245257414715113573", "enabled": true, "name": "Spotify", + "type": 2, "process_names": [ "org.mpris.MediaPlayer2.spotify", "com.spotify.client", "Spotify.exe" ], "search_endpoint": "https://open.spotify.com/search/" + }, + { + "client_id": "1337188104829665340", + "enabled": true, + "name": "Apple Music", + "type": 2, + "process_names": [ + "com.apple.Music", + "AppleInc.AppleMusicWin_nzyj5cx40ttqa!APP", + "AppleMusic.exe" + ], + "search_endpoint": "https://open.spotify.com/search/" } ], + "lastfm": { + "api_key": "", + "api_secret": "", + "enabled": false, + "password": "", + "username": "" + }, + "odesli": true, "autostart": false } \ No newline at end of file diff --git a/src/backends/windows.cpp b/src/backends/windows.cpp index 0e9707c..51f0c6c 100644 --- a/src/backends/windows.cpp +++ b/src/backends/windows.cpp @@ -35,59 +35,6 @@ std::string toStdString(winrt::hstring& in) { return result; } -std::string getAppModelIdOfProcess(HANDLE hProc) { - UINT32 length = 0; - LONG rc = GetApplicationUserModelId(hProc, &length, NULL); - if (rc != ERROR_INSUFFICIENT_BUFFER) - return ""; - - PWSTR fullName = (PWSTR)malloc(length * sizeof(*fullName)); - if (!fullName) - return ""; - - rc = GetApplicationUserModelId(hProc, &length, fullName); - if (rc != ERROR_SUCCESS) { - free(fullName); - return ""; - } - winrt::hstring wideName = fullName; - std::string name = toStdString(wideName); - free(fullName); - return name; -} - -std::string getProcessNameFromAppModelId(std::string appModelId) { - DWORD processes[1024]; - DWORD cbNeeded; - - if (!EnumProcesses(processes, sizeof(processes), &cbNeeded)) - return ""; - - unsigned int processCount = cbNeeded / sizeof(DWORD); - - for (DWORD i = 0; i < processCount; i++) { - DWORD processID = processes[i]; - - HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processID); - if (hProcess) { - std::string modelid = getAppModelIdOfProcess(hProcess); - - if (modelid != appModelId) { - CloseHandle(hProcess); - continue; - } - - char exeName[MAX_PATH]{}; - DWORD size = MAX_PATH; - QueryFullProcessImageNameA(hProcess, 0, exeName, &size); - std::filesystem::path exePath = exeName; - CloseHandle(hProcess); - return exePath.filename().string(); - } - } - return ""; -} - bool CreateShortcut(std::string source, std::string target) { CoInitialize(nullptr); WCHAR src[MAX_PATH]; @@ -185,13 +132,6 @@ std::shared_ptr backend::getMediaInformation() { std::string modelId = toStdString(currentSession.SourceAppUserModelId()); - // I do know that this is disgusting, but for some reason microsoft decided to switch out the exe name with the - // ApplicationUserModelId in some version of windows 11. So we check if it's an exe name, if not we are on some - // newer windows version and need to get the exe name from the model id. We cannot directly work with the model id - // because it's unique per machine and therefore would mess up configs with preconfigured apps. - if (modelId.find(".exe") == std::string::npos) - modelId = getProcessNameFromAppModelId(modelId); - return std::make_shared( playbackInfo.PlaybackStatus() == GlobalSystemMediaTransportControlsSessionPlaybackStatus::Paused, toStdString(mediaProperties.Title()), std::move(artist), std::move(albumName), std::move(modelId), diff --git a/src/main.cpp b/src/main.cpp index 04d46a6..4d155be 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -110,7 +110,7 @@ void handleMediaTasks() { std::string activityState = "by " + mediaInformation->songArtist; DiscordRichPresence activity{}; - activity.type = ActivityType::LISTENING; + activity.type = app.type; activity.details = mediaInformation->songTitle.c_str(); activity.state = activityState.c_str(); activity.smallImageText = serviceName.c_str(); diff --git a/src/utils.hpp b/src/utils.hpp index c608110..defcddc 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -21,6 +21,7 @@ namespace utils { struct App { bool enabled; + int type; std::string appName; std::string clientId; std::string searchEndpoint; @@ -68,6 +69,15 @@ namespace utils { return wxNullIcon; } + inline std::string toLower(const std::string& str) { + std::string lowerStr = str; + std::transform(lowerStr.begin(), lowerStr.end(), lowerStr.begin(), + [](unsigned char c) { return std::tolower(c); }); + return lowerStr; + } + + inline bool caseInsensitiveMatch(const std::string& a, const std::string& b) { return toLower(a) == toLower(b); } + 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; @@ -144,19 +154,23 @@ namespace utils { SongInfo ret{}; std::string response = httpRequest("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) { - ret.artworkURL = results[0]["artworkUrl100"].get(); - ret.trackId = results[0]["trackId"].get(); + try { + nlohmann::json j = nlohmann::json::parse(response); + auto results = j["results"]; + if (results.size() > 0) { + ret.artworkURL = results[0]["artworkUrl100"].get(); + ret.trackId = results[0]["trackId"].get(); + } + return ret; + } catch (...) { + return ret; } - return ret; } inline std::string getOdesliURL(SongInfo& song) { return std::string("https://song.link/i/" + std::to_string(song.trackId)); } - + inline void saveSettings(const App* newApp) { nlohmann::json j; @@ -224,7 +238,7 @@ namespace utils { appJson["client_id"] = app.clientId; appJson["search_endpoint"] = app.searchEndpoint; appJson["enabled"] = app.enabled; - + appJson["type"] = app.type; for (const auto& processName : app.processNames) appJson["process_names"].push_back(processName); j["apps"].push_back(appJson); @@ -269,6 +283,7 @@ namespace utils { a.clientId = app.value("client_id", ""); a.searchEndpoint = app.value("search_endpoint", ""); a.enabled = app.value("enabled", false); + a.type = app.value("type", 2); for (const auto& process : app["process_names"]) a.processNames.push_back(process.get()); @@ -283,7 +298,7 @@ namespace utils { auto settings = getSettings(); for (auto app : settings.apps) { for (auto procName : app.processNames) { - if (procName == processName) + if (caseInsensitiveMatch(procName, processName)) return app; } } @@ -291,6 +306,7 @@ namespace utils { a.clientId = DEFAULT_CLIENT_ID; a.appName = DEFAULT_APP_NAME; a.enabled = settings.anyOtherEnabled; + a.type = 2; // Default to listening a.searchEndpoint = ""; return a; }