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