diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 590e188..d2741d6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,7 +22,8 @@ add_executable(ossp MACOSX_BUNDLE discordrpc.c localRadioDBHandler.c localMusicHandler.cpp - socket.c + socket/socket.c + socket/socketActions.c gui/gui_entry.cpp player/player.c player/playQueue.cpp diff --git a/src/socket.c b/src/socket/socket.c similarity index 67% rename from src/socket.c rename to src/socket/socket.c index 9e8ca7b..51078fc 100644 --- a/src/socket.c +++ b/src/socket/socket.c @@ -21,15 +21,13 @@ #include #include #include -#include "external/cJSON.h" -#include "configHandler.h" -#include "player/player.h" -#include "player/playQueue.hpp" -#include "libopensubsonic/utils.h" -#include "libopensubsonic/httpclient.h" -#include "libopensubsonic/endpoint_getStarred.h" -#include "libopensubsonic/endpoint_getSong.h" +#include "../external/cJSON.h" +#include "../configHandler.h" +#include "../player/player.h" +#include "../player/playQueue.hpp" +#include "../libopensubsonic/utils.h" #include "socket.h" +#include "socketActions.h" static int server_fd = -1; static int client_fd = -1; @@ -160,95 +158,14 @@ void socketHandler_cleanup() { void socketHandler_performAction(int id, char** retDataStr, cJSON** cliReqJson) { switch (id) { - case OSSP_SOCKET_ACTION_GETSTARREDSONGS: - printf("[SocketHandler] Client requested Starred Songs.\n"); - - // Fetch /getStarred endpoint - opensubsonic_httpClient_URL_t* url = malloc(sizeof(opensubsonic_httpClient_URL_t)); - opensubsonic_httpClient_URL_prepare(&url); - url->endpoint = OPENSUBSONIC_ENDPOINT_GETSTARRED; - opensubsonic_httpClient_formUrl(&url); - - opensubsonic_getStarred_struct* getStarredStruct; - opensubsonic_httpClient_fetchResponse(&url, (void**)&getStarredStruct); - opensubsonic_httpClient_URL_cleanup(&url); // Free URL - - - - // Format into JSON - cJSON* retData = cJSON_CreateObject(); - cJSON_AddItemToObject(retData, "songCount", cJSON_CreateNumber(getStarredStruct->songCount)); - - cJSON* songArray = cJSON_CreateArray(); - cJSON_AddItemToObject(retData, "songs", songArray); - - for (int i = 0; i < getStarredStruct->songCount; i++) { - // NOTE: For anything the client isn't directly accessing, only pass IDs - // Client uses cover art directly, so it needs a URL, but it only needs to pass a song ID back - cJSON* song = cJSON_CreateObject(); - cJSON_AddItemToObject(song, "title", cJSON_CreateString(getStarredStruct->songs[i].title)); - cJSON_AddItemToObject(song, "album", cJSON_CreateString(getStarredStruct->songs[i].album)); - cJSON_AddItemToObject(song, "artist", cJSON_CreateString(getStarredStruct->songs[i].artist)); - cJSON_AddItemToObject(song, "id", cJSON_CreateString(getStarredStruct->songs[i].id)); - cJSON_AddItemToObject(song, "size", cJSON_CreateNumber(getStarredStruct->songs[i].size)); - cJSON_AddItemToObject(song, "duration", cJSON_CreateNumber(getStarredStruct->songs[i].duration)); - cJSON_AddItemToObject(song, "albumId", cJSON_CreateString(getStarredStruct->songs[i].albumId)); - cJSON_AddItemToObject(song, "artistId", cJSON_CreateString(getStarredStruct->songs[i].artistId)); - - // Convert cover art ID to URL - opensubsonic_httpClient_URL_t* coverArtUrl = malloc(sizeof(opensubsonic_httpClient_URL_t)); - opensubsonic_httpClient_URL_prepare(&coverArtUrl); - coverArtUrl->endpoint = OPENSUBSONIC_ENDPOINT_GETCOVERART; - coverArtUrl->id = strdup(getStarredStruct->songs[i].coverArt); - opensubsonic_httpClient_formUrl(&coverArtUrl); - cJSON_AddItemToObject(song, "coverArtUrl", cJSON_CreateString(coverArtUrl->formedUrl)); - - cJSON_AddItemToArray(songArray, song); - opensubsonic_httpClient_URL_cleanup(&coverArtUrl); // Free Cover Art URL - } - - *retDataStr = cJSON_PrintUnformatted(retData); - cJSON_Delete(retData); - - opensubsonic_getStarred_struct_free(&getStarredStruct); // Free Struct + case OSSP_SOCKET_ACTION_GET_STARRED_SONGS: + printf("[SocketHandler] Client requested OSSP_SOCKET_ACTION_GET_STARRED_SONGS.\n"); + OSSPS_SocketAction_Get_Starred_Songs(retDataStr, cliReqJson); break; - - - case OSSP_SOCKET_ACTION_NOW_PLAYING: - printf("[SocketHandler] Client requested Now Playing.\n"); - - cJSON* retDatab = cJSON_CreateObject(); - - int currentPos = OSSPQ_getCurrentPos(); - if (currentPos == 0) { - // No songs added to queue yet - cJSON_AddItemToObject(retDatab, "totalQueueCount", cJSON_CreateNumber(0)); - } else { - // At least a single song has been added to the queue - OSSPQ_SongStruct* nowPlaying = OSSPQ_getAtPos(currentPos); - if (nowPlaying == NULL) { - // Could not pull queue item - printf("[SocketHandler] --\n"); - } - - cJSON_AddItemToObject(retDatab, "songTitle", cJSON_CreateString(nowPlaying->title)); - cJSON_AddItemToObject(retDatab, "songAlbum", cJSON_CreateString(nowPlaying->album)); - cJSON_AddItemToObject(retDatab, "songArtist", cJSON_CreateString(nowPlaying->artist)); - //cJSON_AddItemToObject(retData, "duration", cJSON_CreateString()); - cJSON_AddItemToObject(retDatab, "coverArtUrl", cJSON_CreateString(nowPlaying->coverArtUrl)); - } - - - - - - printf("%s\n", cJSON_PrintUnformatted(retDatab)); - - + printf("[SocketHandler] Client requested OSSP_SOCKET_ACTION_NOW_PLAYING.\n"); + OSSPS_SocketAction_Now_Playing(retDataStr, cliReqJson); break; - - case OSSP_SOCKET_ACTION_ADD_TO_QUEUE: printf("[SocketHandler] Client requested OSSP_SOCKET_ACTION_ADD_TO_QUEUE.\n"); OSSPS_SocketAction_Add_To_Queue(retDataStr, cliReqJson); @@ -257,28 +174,26 @@ void socketHandler_performAction(int id, char** retDataStr, cJSON** cliReqJson) case OSSP_SOCKET_ACTION_OSSPP_PREV: - // + OSSPlayer_GstECont_Playbin3_Prev(); + *retDataStr = strdup("OK"); break; case OSSP_SOCKET_ACTION_OSSPP_PLAYPAUSE: OSSPlayer_GstECont_Playbin3_PlayPause(); *retDataStr = strdup("OK"); break; case OSSP_SOCKET_ACTION_OSSPP_NEXT: - OSSPlayer_GstECont_Playbin3_Stop(); + OSSPlayer_GstECont_Playbin3_Next(); *retDataStr = strdup("OK"); break; case OSSP_SOCKET_ACTION_OSSPP_OUTVOLUME: - printf("OUT VOL CHANGED!\n"); - - int vol = 0; - OSS_Pioj(&vol, *cliReqJson, "vol"); - printf("New vol: %d\n", vol); - - float f_vol = (float)vol / 100.0f; - printf("New f_vol: %f\n", f_vol); - OSSPlayer_GstECont_OutVolume_set(f_vol); - - *retDataStr = strdup("OK"); + // New OutVolume set from client + printf("[SocketHandler] Client requested OSSP_SOCKET_ACTION_OSSPP_OUTVOLUME.\n"); + OSSPS_SocketAction_OSSPP_OutVolume(retDataStr, cliReqJson); + break; + case OSSP_SOCKET_ACTION_OSSPP_INVOLUME: + // New InVolume set from client + printf("[SocketHandler] Client requested OSSP_SOCKET_ACTION_OSSPP_INVOLUME.\n"); + OSSPS_SocketAction_OSSPP_InVolume(retDataStr, cliReqJson); break; @@ -291,58 +206,14 @@ void socketHandler_performAction(int id, char** retDataStr, cJSON** cliReqJson) -void OSSPS_SocketAction_Add_To_Queue(char** retDataStr, cJSON** cliReqJson) { - // Pull ID from request JSON - char* id = NULL; - OSS_Psoj(&id, *cliReqJson, "songId"); - if (id == NULL) { - printf("[SocketHandler] OSSP_SOCKET_ACTION_ADD_TO_QUEUE failed - 'id' is null.\n"); - *retDataStr = strdup("NOTOK"); - return; - } - // Create Stream URL from ID - opensubsonic_httpClient_URL_t* streamUrl = malloc(sizeof(opensubsonic_httpClient_URL_t)); - opensubsonic_httpClient_URL_prepare(&streamUrl); - streamUrl->endpoint = OPENSUBSONIC_ENDPOINT_STREAM; - streamUrl->id = strdup(id); - opensubsonic_httpClient_formUrl(&streamUrl); - // Contact the /getSong endpoint - opensubsonic_httpClient_URL_t* songUrl = malloc(sizeof(opensubsonic_httpClient_URL_t)); - opensubsonic_httpClient_URL_prepare(&songUrl); - songUrl->endpoint = OPENSUBSONIC_ENDPOINT_GETSONG; - songUrl->id = strdup(id); - opensubsonic_httpClient_formUrl(&songUrl); - opensubsonic_getSong_struct* getSongStruct; - opensubsonic_httpClient_fetchResponse(&songUrl, (void**)&getSongStruct); - // Create Cover Art URL from ID - opensubsonic_httpClient_URL_t* coverartUrl = (opensubsonic_httpClient_URL_t*)malloc(sizeof(opensubsonic_httpClient_URL_t)); - opensubsonic_httpClient_URL_prepare(&coverartUrl); - coverartUrl->endpoint = OPENSUBSONIC_ENDPOINT_GETCOVERART; - coverartUrl->id = strdup(id); - opensubsonic_httpClient_formUrl(&coverartUrl); - // Append to queue - OSSPQ_AppendToEnd(getSongStruct->title, - getSongStruct->album, - getSongStruct->artist, - id, - streamUrl->formedUrl, - coverartUrl->formedUrl, - getSongStruct->duration, - OSSPQ_MODE_OPENSUBSONIC); - // Free memory - opensubsonic_getSong_struct_free(&getSongStruct); - opensubsonic_httpClient_URL_cleanup(&songUrl); - opensubsonic_httpClient_URL_cleanup(&streamUrl); - opensubsonic_httpClient_URL_cleanup(&coverartUrl); - *retDataStr = strdup("OK"); - return; -} + + diff --git a/src/socket.h b/src/socket/socket.h similarity index 64% rename from src/socket.h rename to src/socket/socket.h index 4dd35de..83e8709 100644 --- a/src/socket.h +++ b/src/socket/socket.h @@ -1,17 +1,6 @@ #ifndef _SOCKET_H #define _SOCKET_H #include -#include "external/cJSON.h" - -#define OSSP_SOCKET_ACTION_GETSTARREDSONGS 101 -#define OSSP_SOCKET_ACTION_NOW_PLAYING 201 -#define OSSP_SOCKET_ACTION_STATS 202 -#define OSSP_SOCKET_ACTION_ADD_TO_QUEUE 203 - -#define OSSP_SOCKET_ACTION_OSSPP_PREV 301 // (OSSPP -> OSSP Player) -#define OSSP_SOCKET_ACTION_OSSPP_PLAYPAUSE 302 -#define OSSP_SOCKET_ACTION_OSSPP_NEXT 303 -#define OSSP_SOCKET_ACTION_OSSPP_OUTVOLUME 304 typedef struct { uint32_t signature; @@ -43,9 +32,4 @@ int socketHandler_sendJson(char* json, int size); uint32_t socketHandlerUtil_byteArrToUint32BE(uint8_t buf[]); uint32_t socketHandlerUtil_byteArrToUint32LE(uint8_t buf[]); - - -// Testing -void OSSPS_SocketAction_Add_To_Queue(char** retDataStr, cJSON** cliReqJson); - #endif diff --git a/src/socket/socketActions.c b/src/socket/socketActions.c new file mode 100644 index 0000000..a60b91f --- /dev/null +++ b/src/socket/socketActions.c @@ -0,0 +1,164 @@ +/* + * OpenSubsonicPlayer + * Goldenkrew3000 2025 + * License: GNU General Public License 3.0 + * Info: Socket Actions + */ + +#include +#include +#include +#include "socketActions.h" +#include "../player/player.h" +#include "../player/playQueue.hpp" +#include "../libopensubsonic/utils.h" +#include "../libopensubsonic/httpclient.h" +#include "../libopensubsonic/endpoint_getStarred.h" +#include "../libopensubsonic/endpoint_getSong.h" +#include "../external/cJSON.h" + +void OSSPS_SocketAction_Get_Starred_Songs(char** retDataStr, cJSON** cliReqJson) { + // Fetch /getStarred endpoint + opensubsonic_httpClient_URL_t* starredUrl = malloc(sizeof(opensubsonic_httpClient_URL_t)); + opensubsonic_httpClient_URL_prepare(&starredUrl); + starredUrl->endpoint = OPENSUBSONIC_ENDPOINT_GETSTARRED; + opensubsonic_httpClient_formUrl(&starredUrl); + opensubsonic_getStarred_struct* getStarredStruct; + opensubsonic_httpClient_fetchResponse(&starredUrl, (void**)&getStarredStruct); + opensubsonic_httpClient_URL_cleanup(&starredUrl); + + // Filter out the starred songs only + cJSON* retPayload = cJSON_CreateObject(); + cJSON_AddItemToObject(retPayload, "songCount", cJSON_CreateNumber(getStarredStruct->songCount)); + + cJSON* songArray = cJSON_CreateArray(); + cJSON_AddItemToObject(retPayload, "songs", songArray); + + for (int i = 0; i < getStarredStruct->songCount; i++) { + // NOTE: For anything the client isn't directly accessing, only pass IDs + // Client uses cover art directly, so it needs a URL, but it only needs to pass a song ID back + opensubsonic_httpClient_URL_t* coverArtUrl = malloc(sizeof(opensubsonic_httpClient_URL_t)); + opensubsonic_httpClient_URL_prepare(&coverArtUrl); + coverArtUrl->endpoint = OPENSUBSONIC_ENDPOINT_GETCOVERART; + coverArtUrl->id = strdup(getStarredStruct->songs[i].coverArt); + opensubsonic_httpClient_formUrl(&coverArtUrl); + + cJSON* songObj = cJSON_CreateObject(); + cJSON_AddItemToObject(songObj, "title", cJSON_CreateString(getStarredStruct->songs[i].title)); + cJSON_AddItemToObject(songObj, "album", cJSON_CreateString(getStarredStruct->songs[i].album)); + cJSON_AddItemToObject(songObj, "artist", cJSON_CreateString(getStarredStruct->songs[i].artist)); + cJSON_AddItemToObject(songObj, "id", cJSON_CreateString(getStarredStruct->songs[i].id)); + cJSON_AddItemToObject(songObj, "size", cJSON_CreateNumber(getStarredStruct->songs[i].size)); + cJSON_AddItemToObject(songObj, "duration", cJSON_CreateNumber(getStarredStruct->songs[i].duration)); + cJSON_AddItemToObject(songObj, "albumId", cJSON_CreateString(getStarredStruct->songs[i].albumId)); + cJSON_AddItemToObject(songObj, "artistId", cJSON_CreateString(getStarredStruct->songs[i].artistId)); + cJSON_AddItemToObject(songObj, "coverArtUrl", cJSON_CreateString(coverArtUrl->formedUrl)); + cJSON_AddItemToArray(songArray, songObj); + + opensubsonic_httpClient_URL_cleanup(&coverArtUrl); + } + + // Create return payload string and free memory + opensubsonic_getStarred_struct_free(&getStarredStruct); + *retDataStr = cJSON_PrintUnformatted(retPayload); + cJSON_Delete(retPayload); + return; +} + +void OSSPS_SocketAction_Now_Playing(char** retDataStr, cJSON** cliReqJson) { + cJSON* retPayload = cJSON_CreateObject(); + int currentPos = OSSPQ_getCurrentPos(); + if (currentPos == 0) { + // No songs added to queue yet + cJSON_AddItemToObject(retPayload, "totalQueueCount", cJSON_CreateNumber(0)); + } else { + // At least a single song has been added to the queue + OSSPQ_SongStruct* nowPlaying = OSSPQ_getAtPos(currentPos); + if (nowPlaying == NULL) { + // Could not pull queue item + // TODO + printf("[SocketHandler] --\n"); + } + + cJSON_AddItemToObject(retPayload, "songTitle", cJSON_CreateString(nowPlaying->title)); + cJSON_AddItemToObject(retPayload, "songAlbum", cJSON_CreateString(nowPlaying->album)); + cJSON_AddItemToObject(retPayload, "songArtist", cJSON_CreateString(nowPlaying->artist)); + //cJSON_AddItemToObject(retData, "duration", cJSON_CreateString()); + cJSON_AddItemToObject(retPayload, "coverArtUrl", cJSON_CreateString(nowPlaying->coverArtUrl)); + } + + *retDataStr = cJSON_PrintUnformatted(retPayload); + cJSON_Delete(retPayload); + return; +} + +void OSSPS_SocketAction_Add_To_Queue(char** retDataStr, cJSON** cliReqJson) { + // Pull ID from request JSON + char* id = NULL; + OSS_Psoj(&id, *cliReqJson, "songId"); + if (id == NULL) { + printf("[SocketHandler] OSSP_SOCKET_ACTION_ADD_TO_QUEUE failed - 'id' is null.\n"); + *retDataStr = strdup("NOTOK"); + return; + } + + // Create Stream URL from ID + opensubsonic_httpClient_URL_t* streamUrl = malloc(sizeof(opensubsonic_httpClient_URL_t)); + opensubsonic_httpClient_URL_prepare(&streamUrl); + streamUrl->endpoint = OPENSUBSONIC_ENDPOINT_STREAM; + streamUrl->id = strdup(id); + opensubsonic_httpClient_formUrl(&streamUrl); + + // Contact the /getSong endpoint + opensubsonic_httpClient_URL_t* songUrl = malloc(sizeof(opensubsonic_httpClient_URL_t)); + opensubsonic_httpClient_URL_prepare(&songUrl); + songUrl->endpoint = OPENSUBSONIC_ENDPOINT_GETSONG; + songUrl->id = strdup(id); + opensubsonic_httpClient_formUrl(&songUrl); + opensubsonic_getSong_struct* getSongStruct; + opensubsonic_httpClient_fetchResponse(&songUrl, (void**)&getSongStruct); + + // Create Cover Art URL from ID + opensubsonic_httpClient_URL_t* coverartUrl = (opensubsonic_httpClient_URL_t*)malloc(sizeof(opensubsonic_httpClient_URL_t)); + opensubsonic_httpClient_URL_prepare(&coverartUrl); + coverartUrl->endpoint = OPENSUBSONIC_ENDPOINT_GETCOVERART; + coverartUrl->id = strdup(id); + opensubsonic_httpClient_formUrl(&coverartUrl); + + // Append to queue + OSSPQ_AppendToEnd(getSongStruct->title, + getSongStruct->album, + getSongStruct->artist, + id, + streamUrl->formedUrl, + coverartUrl->formedUrl, + getSongStruct->duration, + OSSPQ_MODE_OPENSUBSONIC); + + // Free memory + opensubsonic_getSong_struct_free(&getSongStruct); + opensubsonic_httpClient_URL_cleanup(&songUrl); + opensubsonic_httpClient_URL_cleanup(&streamUrl); + opensubsonic_httpClient_URL_cleanup(&coverartUrl); + + *retDataStr = strdup("OK"); + return; +} + +// NOTE: Could refactor OSSPS_SocketAction_OSSPP_OutVolume and OSSPS_SocketAction_OSSPP_InVolume into a single function, +// but at this point, I don't think it's worth it. This might change later +void OSSPS_SocketAction_OSSPP_OutVolume(char** retDataStr, cJSON** cliReqJson) { + int vol = 0; + OSS_Pioj(&vol, *cliReqJson, "vol"); + float f_vol = (float)vol / 100.0f; + OSSPlayer_GstECont_OutVolume_set(f_vol); + *retDataStr = strdup("OK"); +} + +void OSSPS_SocketAction_OSSPP_InVolume(char** retDataStr, cJSON** cliReqJson) { + int vol = 0; + OSS_Pioj(&vol, *cliReqJson, "vol"); + float f_vol = (float)vol / 100.0f; + OSSPlayer_GstECont_InVolume_set(f_vol); + *retDataStr = strdup("OK"); +} diff --git a/src/socket/socketActions.h b/src/socket/socketActions.h new file mode 100644 index 0000000..a26f705 --- /dev/null +++ b/src/socket/socketActions.h @@ -0,0 +1,29 @@ +/* + * OpenSubsonicPlayer + * Goldenkrew3000 2025 + * License: GNU General Public License 3.0 + * Info: Socket Actions + */ + +#ifndef _SOCKETACTIONS_H +#define _SOCKETACTIONS_H +#include "../external/cJSON.h" + +#define OSSP_SOCKET_ACTION_GET_STARRED_SONGS 101 +#define OSSP_SOCKET_ACTION_NOW_PLAYING 201 +#define OSSP_SOCKET_ACTION_STATS 202 +#define OSSP_SOCKET_ACTION_ADD_TO_QUEUE 203 + +#define OSSP_SOCKET_ACTION_OSSPP_PREV 301 +#define OSSP_SOCKET_ACTION_OSSPP_PLAYPAUSE 302 +#define OSSP_SOCKET_ACTION_OSSPP_NEXT 303 +#define OSSP_SOCKET_ACTION_OSSPP_OUTVOLUME 304 +#define OSSP_SOCKET_ACTION_OSSPP_INVOLUME 305 + +void OSSPS_SocketAction_Get_Starred_Songs(char** retDataStr, cJSON** cliReqJson); +void OSSPS_SocketAction_Now_Playing(char** retDataStr, cJSON** cliReqJson); +void OSSPS_SocketAction_Add_To_Queue(char** retDataStr, cJSON** cliReqJson); +void OSSPS_SocketAction_OSSPP_OutVolume(char** retDataStr, cJSON** cliReqJson); +void OSSPS_SocketAction_OSSPP_InVolume(char** retDataStr, cJSON** cliReqJson); + +#endif