diff --git a/src/discordrpc.c b/src/discordrpc.c new file mode 100644 index 0000000..ec9d329 --- /dev/null +++ b/src/discordrpc.c @@ -0,0 +1,158 @@ +/* + * OpenSubSonicPlayer + * Goldenkrew3000 2025 + * GPL-3.0 + * Discord Local RPC Handler + * Note: This provides server auth creds (encoded) directly to Discord, could use Spotify's API instead?? + */ + +#include +#include +#include +#include "external/discord-rpc/include/discord_rpc.h" +#include "discordrpc.h" + +const char* discordrpc_appid = "1407025303779278980"; +char* discordrpc_osString = NULL; +static int rc = 0; + +void discordrpc_struct_init(discordrpc_data** discordrpc_struct) { + (*discordrpc_struct) = malloc(sizeof(discordrpc_data)); + (*discordrpc_struct)->state = 0; + (*discordrpc_struct)->songLength = 0; + (*discordrpc_struct)->songTitle = NULL; + (*discordrpc_struct)->songArtist = NULL; + (*discordrpc_struct)->coverArtUrl = NULL; +} + +void discordrpc_struct_deinit(discordrpc_data** discordrpc_struct) { + if ((*discordrpc_struct)->songTitle != NULL) { free((*discordrpc_struct)->songTitle); } + if ((*discordrpc_struct)->songArtist != NULL) { free((*discordrpc_struct)->songArtist); } + if ((*discordrpc_struct)->coverArtUrl != NULL) { free((*discordrpc_struct)->coverArtUrl); } + if (*discordrpc_struct != NULL) { free(*discordrpc_struct); } +} + +static void handleDiscordReady(const DiscordUser* connectedUser) +{ + printf("\nDiscord: connected to user %s#%s - %s\n", + connectedUser->username, + connectedUser->discriminator, + connectedUser->userId); +} + +static void handleDiscordDisconnected(int errcode, const char* message) +{ + printf("\nDiscord: disconnected (%d: %s)\n", errcode, message); +} + +static void handleDiscordError(int errcode, const char* message) +{ + printf("\nDiscord: error (%d: %s)\n", errcode, message); +} + +void discordrpc_init() { + printf("[DiscordRPC] Initializing...\n"); + DiscordEventHandlers handlers; + memset(&handlers, 0, sizeof(handlers)); + handlers.ready = handleDiscordReady; + handlers.disconnected = handleDiscordDisconnected; + handlers.errored = handleDiscordError; + Discord_Initialize(discordrpc_appid, &handlers, 1, NULL); + + // Fetch OS String for RPC (Heap-allocated) + // TODO: Check if failed + discordrpc_osString = discordrpc_getOS(); +} + +void discordrpc_update(discordrpc_data** discordrpc_struct) { + printf("[DiscordRPC] Updating...\n"); + DiscordRichPresence presence; + char* detailsString = NULL; + char* stateString = NULL; + memset(&presence, 0, sizeof(presence)); + + if ((*discordrpc_struct)->state == DISCORDRPC_STATE_IDLE) { + asprintf(&detailsString, "Idle"); + presence.details = detailsString; + } else if ((*discordrpc_struct)->state == DISCORDRPC_STATE_PLAYING) { + asprintf(&detailsString, "%s", (*discordrpc_struct)->songTitle); + asprintf(&stateString, "by %s", (*discordrpc_struct)->songArtist); + presence.details = detailsString; + presence.state = stateString; + // TODO: Discord is currently broken for this rn + //presence.largeImageKey = (*discordrpc_struct)->coverArtUrl; + presence.largeImageText = discordrpc_osString; + } else if ((*discordrpc_struct)->state == DISCORDRPC_STATE_PAUSED) { + + } + + presence.activity_type = DISCORD_ACTIVITY_TYPE_LISTENING; + Discord_UpdatePresence(&presence); + + free(detailsString); + if (stateString != NULL) { free(stateString); } +} + +char* discordrpc_getOS() { + // NOTE: Could have made a sysctl function, but this is literally only done here, not worth it + // TODO: This is ONLY linux compatible at this point + + FILE* fp_ostype = fopen("/proc/sys/kernel/ostype", "r"); + char buf_ostype[16]; + if (!fp_ostype) { + logger_log_error(__func__, "Could not perform kernel.ostype sysctl."); + return NULL; + } + + FILE* fp_osrelease = fopen("/proc/sys/kernel/osrelease", "r"); + char buf_osrelease[32]; + if (!fp_osrelease) { + logger_log_error(__func__, "Could not perform kernel.osrelease sysctl."); + return NULL; + } + + FILE* fp_osarch = fopen("/proc/sys/kernel/arch", "r"); + char buf_osarch[16]; + if (!fp_osarch) { + logger_log_error(__func__, "Could not perform kernel.arch sysctl."); + return NULL; + } + + if (fgets(buf_ostype, sizeof(buf_ostype), fp_ostype) == NULL) { + logger_log_error(__func__, "Could not perform kernel.ostype sysctl."); + fclose(fp_ostype); + fclose(fp_osrelease); + fclose(fp_osarch); + return NULL; + } + if (fgets(buf_osrelease, sizeof(buf_osrelease), fp_osrelease) == NULL) { + logger_log_error(__func__, "Could not perform kernel.osrelease sysctl."); + fclose(fp_ostype); + fclose(fp_osrelease); + fclose(fp_osarch); + return NULL; + } + if (fgets(buf_osarch, sizeof(buf_osarch), fp_osarch) == NULL) { + logger_log_error(__func__, "Could not perform kernel.arch sysctl."); + fclose(fp_ostype); + fclose(fp_osrelease); + fclose(fp_osarch); + return NULL; + } + fclose(fp_ostype); + fclose(fp_osrelease); + fclose(fp_osarch); + + // HACK: Since Linux removed the sysctl interface, I have to manually remove newlines from the /proc contents + buf_ostype[strcspn(buf_ostype, "\n")] = '\0'; + buf_osrelease[strcspn(buf_osrelease, "\n")] = '\0'; + buf_osarch[strcspn(buf_osarch, "\n")] = '\0'; + + char* osString = NULL; + rc = asprintf(&osString, "on %s %s %s", buf_ostype, buf_osarch, buf_osrelease); + if (rc == -1) { + logger_log_error(__func__, "asprintf() failed."); + return NULL; + } + return osString; +} diff --git a/src/discordrpc.h b/src/discordrpc.h new file mode 100644 index 0000000..13ac7fc --- /dev/null +++ b/src/discordrpc.h @@ -0,0 +1,23 @@ +#ifndef _DISCORDRPC_H +#define _DISCORDRPC_H + +#define DISCORDRPC_STATE_IDLE 0 +#define DISCORDRPC_STATE_PLAYING 1 +#define DISCORDRPC_STATE_PAUSED 2 + +typedef struct { + int state; + long songLength; + char* songTitle; + char* songArtist; + char* coverArtUrl; +} discordrpc_data; + + +void discordrpc_struct_init(discordrpc_data** discordrpc_struct); +void discordrpc_struct_deinit(discordrpc_data** discordrpc_struct); +void discordrpc_init(); +void discordrpc_update(discordrpc_data** discordrpc_struct); +char* discordrpc_getOS(); + +#endif diff --git a/src/main.c b/src/main.c index 3d210dd..9964bca 100644 --- a/src/main.c +++ b/src/main.c @@ -2,7 +2,6 @@ #include #include #include -#include "external/discord-rpc/include/discord_rpc.h" #include "gui/gui_entry.hpp" #include "libopensubsonic/logger.h" #include "libopensubsonic/crypto.h" @@ -10,53 +9,7 @@ #include "libopensubsonic/endpoint_ping.h" #include "configHandler.h" #include "player/player.h" - -const char* APPID = "1407025303779278980"; - - - -static void handleDiscordReady(const DiscordUser* connectedUser) -{ - printf("\nDiscord: connected to user %s#%s - %s\n", - connectedUser->username, - connectedUser->discriminator, - connectedUser->userId); -} - -static void handleDiscordDisconnected(int errcode, const char* message) -{ - printf("\nDiscord: disconnected (%d: %s)\n", errcode, message); -} - -static void handleDiscordError(int errcode, const char* message) -{ - printf("\nDiscord: error (%d: %s)\n", errcode, message); -} - - - -static void discordInit() -{ - DiscordEventHandlers handlers; - memset(&handlers, 0, sizeof(handlers)); - handlers.ready = handleDiscordReady; - handlers.disconnected = handleDiscordDisconnected; - handlers.errored = handleDiscordError; - Discord_Initialize(APPID, &handlers, 1, NULL); -} - -static void updatePresence() { - char buffer[256]; - DiscordRichPresence presence; - memset(&presence, 0, sizeof(presence)); - sprintf(buffer, "Idle"); // Change to snprintf - presence.details = buffer; - presence.activity_type = DISCORD_ACTIVITY_TYPE_LISTENING; - Discord_UpdatePresence(&presence); -} - - - +#include "discordrpc.h" configHandler_config_t* configObj = NULL; int checkConfigFile(); @@ -99,8 +52,12 @@ int main(int argc, char** argv) { pthread_create(&pthr_player, NULL, OSSPlayer_ThrdInit, NULL); // Launch Discord RPC - discordInit(); - updatePresence(); + discordrpc_data* discordrpc = NULL; + discordrpc_struct_init(&discordrpc); + discordrpc->state = DISCORDRPC_STATE_IDLE; + discordrpc_init(); + discordrpc_update(&discordrpc); + discordrpc_struct_deinit(&discordrpc); // Launch QT frontend qt_gui_entry(argc, argv);