add project files

This commit is contained in:
EinTim23 2024-11-01 14:10:00 +01:00
parent 161a740d2f
commit 498f95668d
13 changed files with 402 additions and 0 deletions

10
.clang-format Normal file
View File

@ -0,0 +1,10 @@
BasedOnStyle: Google
UseTab: Never
IndentWidth: 4
TabWidth: 4
BreakBeforeBraces: Attach
AllowShortIfStatementsOnASingleLine: false
IndentCaseLabels: false
AccessModifierOffset: -4
ColumnLimit: 120
NamespaceIndentation: All

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build/*

90
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,90 @@
{
"files.associations": {
"iostream": "cpp",
"thread": "cpp",
"algorithm": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"cctype": "cpp",
"charconv": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"coroutine": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"exception": "cpp",
"format": "cpp",
"forward_list": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"iterator": "cpp",
"limits": "cpp",
"list": "cpp",
"locale": "cpp",
"map": "cpp",
"memory": "cpp",
"mutex": "cpp",
"new": "cpp",
"optional": "cpp",
"ostream": "cpp",
"random": "cpp",
"ratio": "cpp",
"regex": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"string": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"unordered_map": "cpp",
"utility": "cpp",
"vector": "cpp",
"xfacet": "cpp",
"xhash": "cpp",
"xiosbase": "cpp",
"xlocale": "cpp",
"xlocbuf": "cpp",
"xlocinfo": "cpp",
"xlocmes": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xstring": "cpp",
"xtr1common": "cpp",
"xtree": "cpp",
"xutility": "cpp",
"xstddef": "cpp",
"fstream": "cpp",
"any": "cpp",
"csignal": "cpp",
"cstdarg": "cpp",
"deque": "cpp",
"filesystem": "cpp",
"functional": "cpp",
"numeric": "cpp",
"ranges": "cpp",
"set": "cpp",
"span": "cpp",
"unordered_set": "cpp",
"valarray": "cpp",
"variant": "cpp",
"codecvt": "cpp"
}
}

8
CMakeLists.txt Normal file
View File

@ -0,0 +1,8 @@
cmake_minimum_required (VERSION 3.8)
project ("globalRPC")
file(GLOB_RECURSE SOURCES "src/*.cpp")
add_executable (rpc ${SOURCES})
set_property(TARGET rpc PROPERTY CXX_STANDARD 20)
add_subdirectory("vendor")
target_link_libraries(rpc PUBLIC WindowsApp discord-rpc)
target_include_directories(rpc PRIVATE vendor)

12
known.json Normal file
View File

@ -0,0 +1,12 @@
{
"apps": [
{
"name": "Apple Music",
"process_names": [
"AppleMusic.exe"
],
"search_endpoint": "https://music.apple.com/search?term=",
"client_id": "1301881165984301106"
}
]
}

BIN
rpc.exe Normal file

Binary file not shown.

33
src/backend.hpp Normal file
View File

@ -0,0 +1,33 @@
#ifndef _BACKEND_
#define _BACKEND_
#include <stdint.h>
#include <memory>
#include <string>
struct MediaInfo {
bool paused;
std::string songTitle;
std::string songArtist;
std::string songAlbum;
std::string songThumbnailData;
int64_t songDuration;
int64_t songElapsedTime;
std::string playbackSource;
MediaInfo(bool p, std::string title, std::string artist, std::string album, std::string source,
std::string thumbnail, int duration, int elapsed)
: paused(p),
songTitle(title),
songArtist(artist),
songAlbum(album),
songDuration(duration),
songElapsedTime(elapsed),
playbackSource(source),
songThumbnailData(thumbnail) {}
};
namespace backend {
std::shared_ptr<MediaInfo> getMediaInformation();
}
#endif

61
src/backends/windows.cpp Normal file
View File

@ -0,0 +1,61 @@
#ifdef _WIN32
#include <winrt/windows.foundation.h>
#include <winrt/windows.media.control.h>
#include <winrt/windows.storage.streams.h>
#include <chrono>
#include "../backend.hpp"
using namespace winrt;
using namespace Windows::Media::Control;
using namespace Windows::Storage::Streams;
// a winrt::hstring is just an extended std::wstring, so we can use std::strings built in conversion.
std::string toStdString(winrt::hstring in) {
std::string converted = std::string(in.begin(), in.end());
return converted;
}
std::shared_ptr<MediaInfo> backend::getMediaInformation() {
auto sessionManager = GlobalSystemMediaTransportControlsSessionManager::RequestAsync().get();
auto currentSession = sessionManager.GetCurrentSession();
if (!currentSession)
return nullptr;
auto playbackInfo = currentSession.GetPlaybackInfo();
auto mediaProperties = currentSession.TryGetMediaPropertiesAsync().get();
auto timelineInformation = currentSession.GetTimelineProperties();
if (!mediaProperties)
return nullptr;
auto endTime = std::chrono::duration_cast<std::chrono::milliseconds>(timelineInformation.EndTime()).count();
auto elapsedTime = std::chrono::duration_cast<std::chrono::milliseconds>(timelineInformation.Position()).count();
auto thumbnail = mediaProperties.Thumbnail();
std::string thumbnailData = "";
if (thumbnail) {
auto stream = thumbnail.OpenReadAsync().get();
size_t size = static_cast<size_t>(stream.Size());
DataReader reader(stream);
reader.LoadAsync(static_cast<uint32_t>(size)).get();
std::vector<uint8_t> buffer(size);
reader.ReadBytes(buffer);
thumbnailData = std::string(buffer.begin(), buffer.end());
stream.Close();
}
std::string artist = toStdString(mediaProperties.Artist());
if (artist == "")
artist = toStdString(mediaProperties.AlbumArtist()); // Needed for some apps
return std::make_shared<MediaInfo>(
playbackInfo.PlaybackStatus() == GlobalSystemMediaTransportControlsSessionPlaybackStatus::Paused,
toStdString(mediaProperties.Title()), artist, toStdString(mediaProperties.AlbumTitle()),
toStdString(currentSession.SourceAppUserModelId()), thumbnailData, endTime, elapsedTime);
}
#endif

87
src/main.cpp Normal file
View File

@ -0,0 +1,87 @@
#include <discord-rpc/discord_rpc.h>
#include <chrono>
#include <fstream>
#include <iostream>
#include <thread>
#include "backend.hpp"
#include "utils.hpp"
std::string lastPlayingSong = "";
std::string lastMediaSource = "";
void handleRPCTasks() {
while (true) {
DiscordEventHandlers discordHandler{};
Discord_Initialize(utils::getClientID(lastMediaSource).c_str(), &discordHandler);
if (Discord_IsConnected())
break;
}
while (true) {
Discord_RunCallbacks();
if (!Discord_IsConnected())
break;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
handleRPCTasks(); // this could theoretically cause a stack overflow if discord is restarted often enough
}
int main() {
std::thread rpcThread(handleRPCTasks);
rpcThread.detach();
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
auto mediaInformation = backend::getMediaInformation();
if (!mediaInformation) {
Discord_ClearPresence(); // Nothing is playing rn, clear presence
continue;
}
if (mediaInformation->paused) {
Discord_ClearPresence(); // TODO: allow user to keep presence when paused(because for
// some reason some people want this)
continue;
}
std::string currentlyPlayingSong =
mediaInformation->songTitle + mediaInformation->songArtist + mediaInformation->songAlbum;
if (currentlyPlayingSong == lastPlayingSong)
continue;
lastPlayingSong = currentlyPlayingSong;
std::string currentMediaSource = mediaInformation->playbackSource;
std::cout << currentMediaSource << std::endl;
if (currentMediaSource != lastMediaSource)
Discord_Shutdown(); //reinitialize with new client id
std::string serviceName = utils::getAppName(lastMediaSource);
DiscordRichPresence activity{};
activity.type = ActivityType::LISTENING;
activity.details = mediaInformation->songTitle.c_str();
activity.state = std::string("by " + mediaInformation->songArtist).c_str();
activity.smallImageText = serviceName.c_str();
activity.smallImageKey = "icon";
activity.largeImageText = mediaInformation->songAlbum.c_str();
activity.largeImageKey = "";
if(mediaInformation->songDuration != 0) {
int64_t remainingTime = mediaInformation->songDuration - mediaInformation->songElapsedTime;
activity.startTimestamp = time(nullptr) - mediaInformation->songElapsedTime;
activity.endTimestamp = time(nullptr) + remainingTime;
}
std::string endpointURL = utils::getSearchEndpoint(lastMediaSource);
if(endpointURL != "") {
activity.button1name = std::string("Search on " + serviceName).c_str();
std::string searchQuery = mediaInformation->songTitle + " " + mediaInformation->songArtist;
activity.button1link = std::string(endpointURL + utils::urlEncode(searchQuery)).c_str();
}
lastMediaSource = currentMediaSource;
Discord_UpdatePresence(&activity);
}
}

87
src/utils.hpp Normal file
View File

@ -0,0 +1,87 @@
#ifndef _UTILS_
#define _UTILS_
#include <filesystem>
#include <fstream>
#include <nlohmann-json/single_include/nlohmann/json.hpp>
#include <sstream>
#include <string>
#define DEFAULT_CLIENT_ID "1301849203378622545"
#define DEFAULT_APP_NAME "Music"
#define CONFIG_FILENAME "known.json"
namespace utils {
inline nlohmann::json getApp(std::string processName) {
std::ifstream i("known.json");
std::stringstream s;
s << i.rdbuf();
i.close();
try {
nlohmann::json j = nlohmann::json::parse(s.str());
auto apps = j["apps"];
for (auto app : apps) {
auto processNames = app["process_names"];
for (auto process : processNames) {
if (process.get<std::string>() == processName)
return app;
}
}
} catch (nlohmann::json::parse_error& ex) {
} // TODO: handle parse errors
return nlohmann::json();
}
inline std::string getClientID(std::string processName) {
if (!std::filesystem::exists(CONFIG_FILENAME))
return DEFAULT_CLIENT_ID;
auto app = getApp(processName);
return app["client_id"] == "" ? DEFAULT_CLIENT_ID : app["client_id"];
}
inline std::string getAppName(std::string processName) {
if (!std::filesystem::exists(CONFIG_FILENAME))
return DEFAULT_APP_NAME;
auto app = getApp(processName);
return app["name"] == "" ? DEFAULT_APP_NAME : app["name"];
}
inline std::string getSearchEndpoint(std::string processName) {
if (!std::filesystem::exists(CONFIG_FILENAME))
return "";
auto app = getApp(processName);
return app["search_endpoint"] == "" ? "" : app["search_endpoint"];
}
inline std::string urlEncode(std::string str) {
std::string new_str = "";
char c;
int ic;
const char* chars = str.c_str();
char bufHex[10];
int len = strlen(chars);
for (int i = 0; i < len; i++) {
c = chars[i];
ic = c;
if (c == ' ')
new_str += '+';
else if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
new_str += c;
else {
sprintf(bufHex, "%X", c);
if (ic < 16)
new_str += "%0";
else
new_str += "%";
new_str += bufHex;
}
}
return new_str;
}
} // namespace utils
#undef DEFAULT_APP_NAME
#undef CONFIG_FILENAME
#undef DEFAULT_CLIENT_ID
#endif

0
vendor/.gitkeep vendored Normal file
View File

12
vendor/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,12 @@
add_subdirectory("discord-rpc")
SET(ENABLE_PROGRAMS OFF)
SET(ENABLE_TESTING OFF)
add_subdirectory("mbedtls")
SET(CURL_USE_MBEDTLS ON)
SET(BUILD_STATIC_LIBS ON)
SET(BUILD_SHARED_LIBS OFF)
SET(BUILD_CURL_EXE OFF)
SET(MBEDTLS_INCLUDE_DIRS ../mbedtls/include)
file(REMOVE curl/CMake/FindMbedTLS.cmake) #replace curls FindMbedTLS that expects mbedtls to be prebuilt with a dummy
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/dummy ${CMAKE_MODULE_PATH})
add_subdirectory("curl")

1
vendor/dummy/FindMbedTLS.cmake vendored Normal file
View File

@ -0,0 +1 @@
message(STATUS "This is a dummy file to prevent curl from fucking up my build.")