- 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
This commit is contained in:
parent
cd5656de63
commit
90607d2f5d
|
@ -5,13 +5,34 @@
|
||||||
"client_id": "1245257414715113573",
|
"client_id": "1245257414715113573",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"name": "Spotify",
|
"name": "Spotify",
|
||||||
|
"type": 2,
|
||||||
"process_names": [
|
"process_names": [
|
||||||
"org.mpris.MediaPlayer2.spotify",
|
"org.mpris.MediaPlayer2.spotify",
|
||||||
"com.spotify.client",
|
"com.spotify.client",
|
||||||
"Spotify.exe"
|
"Spotify.exe"
|
||||||
],
|
],
|
||||||
"search_endpoint": "https://open.spotify.com/search/"
|
"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
|
"autostart": false
|
||||||
}
|
}
|
|
@ -35,59 +35,6 @@ std::string toStdString(winrt::hstring& in) {
|
||||||
return result;
|
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) {
|
bool CreateShortcut(std::string source, std::string target) {
|
||||||
CoInitialize(nullptr);
|
CoInitialize(nullptr);
|
||||||
WCHAR src[MAX_PATH];
|
WCHAR src[MAX_PATH];
|
||||||
|
@ -185,13 +132,6 @@ std::shared_ptr<MediaInfo> backend::getMediaInformation() {
|
||||||
|
|
||||||
std::string modelId = toStdString(currentSession.SourceAppUserModelId());
|
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<MediaInfo>(
|
return std::make_shared<MediaInfo>(
|
||||||
playbackInfo.PlaybackStatus() == GlobalSystemMediaTransportControlsSessionPlaybackStatus::Paused,
|
playbackInfo.PlaybackStatus() == GlobalSystemMediaTransportControlsSessionPlaybackStatus::Paused,
|
||||||
toStdString(mediaProperties.Title()), std::move(artist), std::move(albumName), std::move(modelId),
|
toStdString(mediaProperties.Title()), std::move(artist), std::move(albumName), std::move(modelId),
|
||||||
|
|
|
@ -110,7 +110,7 @@ void handleMediaTasks() {
|
||||||
|
|
||||||
std::string activityState = "by " + mediaInformation->songArtist;
|
std::string activityState = "by " + mediaInformation->songArtist;
|
||||||
DiscordRichPresence activity{};
|
DiscordRichPresence activity{};
|
||||||
activity.type = ActivityType::LISTENING;
|
activity.type = app.type;
|
||||||
activity.details = mediaInformation->songTitle.c_str();
|
activity.details = mediaInformation->songTitle.c_str();
|
||||||
activity.state = activityState.c_str();
|
activity.state = activityState.c_str();
|
||||||
activity.smallImageText = serviceName.c_str();
|
activity.smallImageText = serviceName.c_str();
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
namespace utils {
|
namespace utils {
|
||||||
struct App {
|
struct App {
|
||||||
bool enabled;
|
bool enabled;
|
||||||
|
int type;
|
||||||
std::string appName;
|
std::string appName;
|
||||||
std::string clientId;
|
std::string clientId;
|
||||||
std::string searchEndpoint;
|
std::string searchEndpoint;
|
||||||
|
@ -68,6 +69,15 @@ namespace utils {
|
||||||
return wxNullIcon;
|
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) {
|
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); }));
|
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
|
||||||
return s;
|
return s;
|
||||||
|
@ -144,13 +154,17 @@ namespace utils {
|
||||||
SongInfo ret{};
|
SongInfo ret{};
|
||||||
std::string response =
|
std::string response =
|
||||||
httpRequest("https://itunes.apple.com/search?media=music&entity=song&term=" + urlEncode(query));
|
httpRequest("https://itunes.apple.com/search?media=music&entity=song&term=" + urlEncode(query));
|
||||||
nlohmann::json j = nlohmann::json::parse(response);
|
try {
|
||||||
auto results = j["results"];
|
nlohmann::json j = nlohmann::json::parse(response);
|
||||||
if (results.size() > 0) {
|
auto results = j["results"];
|
||||||
ret.artworkURL = results[0]["artworkUrl100"].get<std::string>();
|
if (results.size() > 0) {
|
||||||
ret.trackId = results[0]["trackId"].get<int64_t>();
|
ret.artworkURL = results[0]["artworkUrl100"].get<std::string>();
|
||||||
|
ret.trackId = results[0]["trackId"].get<int64_t>();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
} catch (...) {
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string getOdesliURL(SongInfo& song) {
|
inline std::string getOdesliURL(SongInfo& song) {
|
||||||
|
@ -224,7 +238,7 @@ namespace utils {
|
||||||
appJson["client_id"] = app.clientId;
|
appJson["client_id"] = app.clientId;
|
||||||
appJson["search_endpoint"] = app.searchEndpoint;
|
appJson["search_endpoint"] = app.searchEndpoint;
|
||||||
appJson["enabled"] = app.enabled;
|
appJson["enabled"] = app.enabled;
|
||||||
|
appJson["type"] = app.type;
|
||||||
for (const auto& processName : app.processNames) appJson["process_names"].push_back(processName);
|
for (const auto& processName : app.processNames) appJson["process_names"].push_back(processName);
|
||||||
|
|
||||||
j["apps"].push_back(appJson);
|
j["apps"].push_back(appJson);
|
||||||
|
@ -269,6 +283,7 @@ namespace utils {
|
||||||
a.clientId = app.value("client_id", "");
|
a.clientId = app.value("client_id", "");
|
||||||
a.searchEndpoint = app.value("search_endpoint", "");
|
a.searchEndpoint = app.value("search_endpoint", "");
|
||||||
a.enabled = app.value("enabled", false);
|
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<std::string>());
|
for (const auto& process : app["process_names"]) a.processNames.push_back(process.get<std::string>());
|
||||||
|
|
||||||
|
@ -283,7 +298,7 @@ namespace utils {
|
||||||
auto settings = getSettings();
|
auto settings = getSettings();
|
||||||
for (auto app : settings.apps) {
|
for (auto app : settings.apps) {
|
||||||
for (auto procName : app.processNames) {
|
for (auto procName : app.processNames) {
|
||||||
if (procName == processName)
|
if (caseInsensitiveMatch(procName, processName))
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -291,6 +306,7 @@ namespace utils {
|
||||||
a.clientId = DEFAULT_CLIENT_ID;
|
a.clientId = DEFAULT_CLIENT_ID;
|
||||||
a.appName = DEFAULT_APP_NAME;
|
a.appName = DEFAULT_APP_NAME;
|
||||||
a.enabled = settings.anyOtherEnabled;
|
a.enabled = settings.anyOtherEnabled;
|
||||||
|
a.type = 2; // Default to listening
|
||||||
a.searchEndpoint = "";
|
a.searchEndpoint = "";
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue