add project files
This commit is contained in:
parent
161a740d2f
commit
498f95668d
|
@ -0,0 +1,10 @@
|
||||||
|
BasedOnStyle: Google
|
||||||
|
UseTab: Never
|
||||||
|
IndentWidth: 4
|
||||||
|
TabWidth: 4
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
IndentCaseLabels: false
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
ColumnLimit: 120
|
||||||
|
NamespaceIndentation: All
|
|
@ -0,0 +1 @@
|
||||||
|
build/*
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"name": "Apple Music",
|
||||||
|
"process_names": [
|
||||||
|
"AppleMusic.exe"
|
||||||
|
],
|
||||||
|
"search_endpoint": "https://music.apple.com/search?term=",
|
||||||
|
"client_id": "1301881165984301106"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,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")
|
|
@ -0,0 +1 @@
|
||||||
|
message(STATUS "This is a dummy file to prevent curl from fucking up my build.")
|
Loading…
Reference in New Issue