diff --git a/PROTOCOL.md b/PROTOCOL.md index c08ff818..0f805344 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -13,7 +13,7 @@ The protocol in general is based on the OBS Remote protocol created by Bill Hami - **Scenes** - ["SwitchScenes"](#switchscenes) - ["ScenesChanged"](#sceneschanged) - - **Sources / Scene Items** + - **Scene Items** - ["SourceOrderChanged"](#sourceorderchanged) - ["SceneItemAdded"](#sceneitemadded) - ["SceneItemRemoved"](#sceneitemremoved) @@ -85,8 +85,10 @@ The protocol in general is based on the OBS Remote protocol created by Bill Hami - ["SetVolume"](#setvolume) - ["GetVolume"](#getvolume) - ["SetMute"](#setmute) + - ["GetMute"](#getmute) - ["ToggleMute"](#togglemute) - - ["SetSourceRender"](#setsourcerender) + - **Scene Items** + - ["SetSceneItemRender"](#setsourcerender) (a.k.a `SetSourceRender`) - ["SetSceneItemPosition"](#setsceneitemposition) - ["SetSceneItemTransform"](#setsceneitemtransform) - ["SetSceneItemCrop"](#setsceneitemcrop) @@ -246,7 +248,6 @@ A request to start streaming has been issued. #### "StreamStarted" Streaming started successfully. -*New in OBS Studio* --- @@ -258,31 +259,26 @@ A request to stop streaming has been issued. #### "StreamStopped" Streaming stopped successfully. -*New in OBS Studio* --- #### "RecordingStarting" A request to start recording has been issued. -*New in OBS Studio* --- #### "RecordingStarted" Recording started successfully. -*New in OBS Studio* --- #### "RecordingStopping" A request to stop streaming has been issued. -*New in OBS Studio* --- #### "RecordingStopped" Recording stopped successfully. -*New in OBS Studio* --- @@ -302,8 +298,7 @@ Sent every 2 seconds with the following information : --- #### "Exiting" -OBS is exiting. -*New in OBS Studio* +OBS is exiting. --- @@ -488,7 +483,6 @@ Toggles recording on or off. __Request fields__ : none __Response__ : always OK. No additional fields. -*New in OBS Studio* --- @@ -497,7 +491,6 @@ Start streaming. __Request fields__ : none __Response__ : always OK. No additional fields. -*New in OBS Studio* --- @@ -506,7 +499,6 @@ Stop streaming. __Request fields__ : none __Response__ : always OK. No additional fields. -*New in OBS Studio* --- @@ -515,7 +507,6 @@ Start recording. __Request fields__ : none __Response__ : always OK. No additional fields. -*New in OBS Studio* --- @@ -524,7 +515,6 @@ Stop recording. __Request fields__ : none __Response__ : always OK. No additional fields. -*New in OBS Studio* --- @@ -552,8 +542,6 @@ __Response__ : always OK, with these additional fields : Objects in the "transitions" array have only one field : - **"name"** (string) : name of the transition -*New in OBS Studio* - --- #### "GetCurrentTransition" @@ -564,17 +552,13 @@ __Response__ : always OK, with these additional fields : - **"name"** (string) : name of the selected transition - **"duration"** (integer, only if transition supports this) : transition duration -*New in OBS Studio* - --- #### "SetCurrentTransition" __Request fields__ : - **"transition-name"** (string) : The name of the transition. -__Response__ : OK if specified transition exists, error otherwise. - -*New in OBS Studio* +__Response__ : OK if specified transition exists, error otherwise. --- @@ -586,8 +570,6 @@ __Request fields__ : __Response__ : always OK. -*New in OBS Studio* - --- #### "GetTransitionDuration" @@ -597,8 +579,6 @@ __Request fields__ : none __Response__ : always OK, with these additional fields : - **"transition-duration"** (integer) : current transition duration, in milliseconds -*New in OBS Studio* - --- #### "SetVolume" @@ -610,8 +590,6 @@ __Request fields__ : __Response__ : OK if specified source exists, error otherwise. -*Updated for OBS Studio* - --- #### "GetVolume" @@ -621,11 +599,9 @@ __Request fields__ : - **"source"** (string) : name of the source __Response__ : OK if source exists, with these additional fields : -- **"name"** (string) : name of the requested source -- **"volume"** (double) : volume of the requested source, on a linear scale (0.0 to 1.0) -- **"muted"** (bool) : mute status of the requested source - -*Updated for OBS Studio* +- **"name"** (string) : source name +- **"volume"** (double) : source volume, on a linear scale (0.0 to 1.0) +- **"muted"** (bool) : source mute status --- @@ -638,7 +614,17 @@ __Request fields__ : __Response__ : OK if specified source exists, error otherwise. -*Updated for OBS Studio* +--- + +#### "GetMute" +Get mute status of a specific source. + +__Request fields__ : +- **"source"** (string) : the name of the source + +__Response__ : OK if source exists, with these additional fields : +- **"name"** (string) : source name +- **"muted"** (bool) : source mute status --- @@ -650,8 +636,6 @@ __Request fields__ : __Response__ : OK if specified source exists, error otherwise. -*Updated for OBS Studio* - --- #### "SetSceneItemPosition" @@ -663,8 +647,6 @@ __Request fields__ : __Response__ : OK if specified item exists, error otherwise. -*New in OBS Studio* - --- #### "SetSceneItemTransform" @@ -677,8 +659,6 @@ __Request fields__ : __Response__ : OK if specified item exists, error otherwise. -*New in OBS Studio* - --- #### "SetSceneItemCrop" diff --git a/README.md b/README.md index 48ca1b56..220998ad 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,11 @@ Follow the project on Twitter for news & updates : [@obswebsocket](https://twitt ## Downloads Binaries for Windows and Linux are available in the [Releases](https://github.com/Palakis/obs-websocket/releases) section. -## Sponsors +## Supporters +[Support Class](http://supportclass.net) designs and develops professional livestreams, with services ranging from broadcast graphics design and integration to event organization, along many other skills. + +They have contributed financially to the project and made possible the addition of several features into obs-websocket. Many thanks to them! + [![Support Class](doc/supportclass_logo_blacktext.png)](http://supportclass.net) ## Using obs-websocket diff --git a/WSRequestHandler.cpp b/WSRequestHandler.cpp index c2af012f..d8f8958f 100644 --- a/WSRequestHandler.cpp +++ b/WSRequestHandler.cpp @@ -23,10 +23,15 @@ with this program. If not, see #include "Config.h" #include "Utils.h" +bool str_valid(const char* str) +{ + return (str != nullptr && strlen(str) > 0); +} + WSRequestHandler::WSRequestHandler(QWebSocket *client) : _messageId(0), _requestType(""), - _requestData(nullptr) + data(nullptr) { _client = client; @@ -62,6 +67,7 @@ WSRequestHandler::WSRequestHandler(QWebSocket *client) : messageMap["GetVolume"] = WSRequestHandler::HandleGetVolume; messageMap["ToggleMute"] = WSRequestHandler::HandleToggleMute; messageMap["SetMute"] = WSRequestHandler::HandleSetMute; + messageMap["GetMute"] = WSRequestHandler::HandleGetMute; messageMap["SetCurrentSceneCollection"] = WSRequestHandler::HandleSetCurrentSceneCollection; messageMap["GetCurrentSceneCollection"] = WSRequestHandler::HandleGetCurrentSceneCollection; @@ -89,8 +95,8 @@ void WSRequestHandler::processIncomingMessage(QString textMessage) QByteArray msgData = textMessage.toUtf8(); const char *msg = msgData; - _requestData = obs_data_create_from_json(msg); - if (!_requestData) + data = obs_data_create_from_json(msg); + if (!data) { if (!msg) { @@ -102,8 +108,15 @@ void WSRequestHandler::processIncomingMessage(QString textMessage) return; } - _requestType = obs_data_get_string(_requestData, "request-type"); - _messageId = obs_data_get_string(_requestData, "message-id"); + if (!hasField("request-type") || + !hasField("message-id")) + { + SendErrorResponse("missing request parameters"); + return; + } + + _requestType = obs_data_get_string(data, "request-type"); + _messageId = obs_data_get_string(data, "message-id"); if (Config::Current()->AuthRequired && (_client->property(PROP_AUTHENTICATED).toBool() == false) @@ -116,23 +129,17 @@ void WSRequestHandler::processIncomingMessage(QString textMessage) void (*handlerFunc)(WSRequestHandler*) = (messageMap[_requestType]); if (handlerFunc != NULL) - { handlerFunc(this); - } else - { SendErrorResponse("invalid request type"); - } - obs_data_release(_requestData); + obs_data_release(data); } WSRequestHandler::~WSRequestHandler() { - if (_requestData != NULL) - { - obs_data_release(_requestData); - } + if (data) + obs_data_release(data); } void WSRequestHandler::SendOKResponse(obs_data_t *additionalFields) @@ -141,10 +148,8 @@ void WSRequestHandler::SendOKResponse(obs_data_t *additionalFields) obs_data_set_string(response, "status", "ok"); obs_data_set_string(response, "message-id", _messageId); - if (additionalFields != NULL) - { + if (additionalFields) obs_data_apply(response, additionalFields); - } _client->sendTextMessage(obs_data_get_json(response)); @@ -163,22 +168,30 @@ void WSRequestHandler::SendErrorResponse(const char *errorMessage) obs_data_release(response); } -void WSRequestHandler::HandleGetVersion(WSRequestHandler *owner) +bool WSRequestHandler::hasField(const char* name) +{ + if (!name || !data) + return false; + + return obs_data_has_user_value(data, name); +} + +void WSRequestHandler::HandleGetVersion(WSRequestHandler *req) { const char* obs_version = Utils::OBSVersionString(); obs_data_t *data = obs_data_create(); - obs_data_set_double(data, "version", 1.2); + obs_data_set_double(data, "version", API_VERSION); obs_data_set_string(data, "obs-websocket-version", OBS_WEBSOCKET_VERSION); obs_data_set_string(data, "obs-studio-version", obs_version); - owner->SendOKResponse(data); + req->SendOKResponse(data); obs_data_release(data); bfree((void*)obs_version); } -void WSRequestHandler::HandleGetAuthRequired(WSRequestHandler *owner) +void WSRequestHandler::HandleGetAuthRequired(WSRequestHandler *req) { bool authRequired = Config::Current()->AuthRequired; @@ -191,50 +204,62 @@ void WSRequestHandler::HandleGetAuthRequired(WSRequestHandler *owner) obs_data_set_string(data, "salt", Config::Current()->Salt); } - owner->SendOKResponse(data); + req->SendOKResponse(data); obs_data_release(data); } -void WSRequestHandler::HandleAuthenticate(WSRequestHandler *owner) +void WSRequestHandler::HandleAuthenticate(WSRequestHandler *req) { - const char *auth = obs_data_get_string(owner->_requestData, "auth"); - if (!auth || strlen(auth) < 1) + if (!req->hasField("auth")) { - owner->SendErrorResponse("auth not specified!"); + req->SendErrorResponse("missing request parameters"); return; } - if ((owner->_client->property(PROP_AUTHENTICATED).toBool() == false) && Config::Current()->CheckAuth(auth)) + const char *auth = obs_data_get_string(req->data, "auth"); + if (!str_valid(auth)) { - owner->_client->setProperty(PROP_AUTHENTICATED, true); - owner->SendOKResponse(); + req->SendErrorResponse("auth not specified!"); + return; + } + + if ((req->_client->property(PROP_AUTHENTICATED).toBool() == false) && Config::Current()->CheckAuth(auth)) + { + req->_client->setProperty(PROP_AUTHENTICATED, true); + req->SendOKResponse(); } else { - owner->SendErrorResponse("Authentication Failed."); + req->SendErrorResponse("Authentication Failed."); } } -void WSRequestHandler::HandleSetCurrentScene(WSRequestHandler *owner) +void WSRequestHandler::HandleSetCurrentScene(WSRequestHandler *req) { - const char *sceneName = obs_data_get_string(owner->_requestData, "scene-name"); + if (!req->hasField("scene-name")) + { + req->SendErrorResponse("missing request parameters"); + return; + } + + const char *sceneName = obs_data_get_string(req->data, "scene-name"); obs_source_t *source = obs_get_source_by_name(sceneName); if (source) { obs_frontend_set_current_scene(source); - owner->SendOKResponse(); + req->SendOKResponse(); } else { - owner->SendErrorResponse("requested scene does not exist"); + req->SendErrorResponse("requested scene does not exist"); } obs_source_release(source); } -void WSRequestHandler::HandleGetCurrentScene(WSRequestHandler *owner) +void WSRequestHandler::HandleGetCurrentScene(WSRequestHandler *req) { obs_source_t *current_scene = obs_frontend_get_current_scene(); const char *name = obs_source_get_name(current_scene); @@ -245,14 +270,14 @@ void WSRequestHandler::HandleGetCurrentScene(WSRequestHandler *owner) obs_data_set_string(data, "name", name); obs_data_set_array(data, "sources", scene_items); - owner->SendOKResponse(data); + req->SendOKResponse(data); obs_data_release(data); obs_data_array_release(scene_items); obs_source_release(current_scene); } -void WSRequestHandler::HandleGetSceneList(WSRequestHandler *owner) +void WSRequestHandler::HandleGetSceneList(WSRequestHandler *req) { obs_source_t *current_scene = obs_frontend_get_current_scene(); obs_data_array_t *scenes = Utils::GetScenes(); @@ -261,27 +286,35 @@ void WSRequestHandler::HandleGetSceneList(WSRequestHandler *owner) obs_data_set_string(data, "current-scene", obs_source_get_name(current_scene)); obs_data_set_array(data, "scenes", scenes); - owner->SendOKResponse(data); + req->SendOKResponse(data); obs_data_release(data); obs_data_array_release(scenes); obs_source_release(current_scene); } -void WSRequestHandler::HandleSetSceneItemRender(WSRequestHandler *owner) +void WSRequestHandler::HandleSetSceneItemRender(WSRequestHandler *req) { - const char *itemName = obs_data_get_string(owner->_requestData, "source"); - bool isVisible = obs_data_get_bool(owner->_requestData, "render"); - if (itemName == NULL) + if (!req->hasField("source") || + !req->hasField("render")) { - owner->SendErrorResponse("invalid request parameters"); + req->SendErrorResponse("missing request parameters"); return; } - const char *sceneName = obs_data_get_string(owner->_requestData, "scene-name"); + const char *itemName = obs_data_get_string(req->data, "source"); + bool isVisible = obs_data_get_bool(req->data, "render"); + + if (!itemName) + { + req->SendErrorResponse("invalid request parameters"); + return; + } + + const char *sceneName = obs_data_get_string(req->data, "scene-name"); obs_source_t *scene = Utils::GetSceneFromNameOrCurrent(sceneName); if (scene == NULL) { - owner->SendErrorResponse("requested scene doesn't exist"); + req->SendErrorResponse("requested scene doesn't exist"); return; } @@ -290,17 +323,17 @@ void WSRequestHandler::HandleSetSceneItemRender(WSRequestHandler *owner) { obs_sceneitem_set_visible(sceneItem, isVisible); obs_sceneitem_release(sceneItem); - owner->SendOKResponse(); + req->SendOKResponse(); } else { - owner->SendErrorResponse("specified scene item doesn't exist"); + req->SendErrorResponse("specified scene item doesn't exist"); } obs_source_release(scene); } -void WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler *owner) +void WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler *req) { obs_data_t *data = obs_data_create(); obs_data_set_bool(data, "streaming", obs_frontend_streaming_active()); @@ -322,11 +355,11 @@ void WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler *owner) bfree((void*)tc); } - owner->SendOKResponse(data); + req->SendOKResponse(data); obs_data_release(data); } -void WSRequestHandler::HandleStartStopStreaming(WSRequestHandler *owner) +void WSRequestHandler::HandleStartStopStreaming(WSRequestHandler *req) { if (obs_frontend_streaming_active()) { @@ -337,10 +370,10 @@ void WSRequestHandler::HandleStartStopStreaming(WSRequestHandler *owner) obs_frontend_streaming_start(); } - owner->SendOKResponse(); + req->SendOKResponse(); } -void WSRequestHandler::HandleStartStopRecording(WSRequestHandler *owner) +void WSRequestHandler::HandleStartStopRecording(WSRequestHandler *req) { if (obs_frontend_recording_active()) { @@ -351,42 +384,42 @@ void WSRequestHandler::HandleStartStopRecording(WSRequestHandler *owner) obs_frontend_recording_start(); } - owner->SendOKResponse(); + req->SendOKResponse(); } -void WSRequestHandler::HandleStartStreaming(WSRequestHandler *owner) +void WSRequestHandler::HandleStartStreaming(WSRequestHandler *req) { if (obs_frontend_streaming_active() == false) obs_frontend_streaming_start(); - owner->SendOKResponse(); + req->SendOKResponse(); } -void WSRequestHandler::HandleStopStreaming(WSRequestHandler *owner) +void WSRequestHandler::HandleStopStreaming(WSRequestHandler *req) { if (obs_frontend_streaming_active() == true) obs_frontend_streaming_stop(); - owner->SendOKResponse(); + req->SendOKResponse(); } -void WSRequestHandler::HandleStartRecording(WSRequestHandler *owner) +void WSRequestHandler::HandleStartRecording(WSRequestHandler *req) { if (obs_frontend_recording_active() == false) obs_frontend_recording_start(); - owner->SendOKResponse(); + req->SendOKResponse(); } -void WSRequestHandler::HandleStopRecording(WSRequestHandler *owner) +void WSRequestHandler::HandleStopRecording(WSRequestHandler *req) { if (obs_frontend_recording_active() == true) obs_frontend_recording_stop(); - owner->SendOKResponse(); + req->SendOKResponse(); } -void WSRequestHandler::HandleGetTransitionList(WSRequestHandler *owner) +void WSRequestHandler::HandleGetTransitionList(WSRequestHandler *req) { obs_source_t *current_transition = obs_frontend_get_current_transition(); obs_frontend_source_list transitionList = {}; @@ -409,14 +442,14 @@ void WSRequestHandler::HandleGetTransitionList(WSRequestHandler *owner) obs_data_set_string(response, "current-transition", obs_source_get_name(current_transition)); obs_data_set_array(response, "transitions", transitions); - owner->SendOKResponse(response); + req->SendOKResponse(response); obs_data_release(response); obs_data_array_release(transitions); obs_source_release(current_transition); } -void WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler *owner) +void WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler *req) { obs_source_t *current_transition = obs_frontend_get_current_transition(); @@ -428,190 +461,280 @@ void WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler *owner) obs_data_set_int(response, "duration", Utils::GetTransitionDuration()); } - owner->SendOKResponse(response); + req->SendOKResponse(response); obs_data_release(response); obs_source_release(current_transition); } -void WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler *owner) +void WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler *req) { - const char *name = obs_data_get_string(owner->_requestData, "transition-name"); + if (!req->hasField("transition-name")) + { + req->SendErrorResponse("missing request parameters"); + return; + } + + const char *name = obs_data_get_string(req->data, "transition-name"); bool success = Utils::SetTransitionByName(name); if (success) - owner->SendOKResponse(); + req->SendOKResponse(); else - owner->SendErrorResponse("requested transition does not exist"); + req->SendErrorResponse("requested transition does not exist"); } -void WSRequestHandler::HandleSetTransitionDuration(WSRequestHandler *owner) +void WSRequestHandler::HandleSetTransitionDuration(WSRequestHandler *req) { - int ms = obs_data_get_int(owner->_requestData, "duration"); + if (!req->hasField("duration")) + { + req->SendErrorResponse("missing request parameters"); + return; + } + + int ms = obs_data_get_int(req->data, "duration"); Utils::SetTransitionDuration(ms); - owner->SendOKResponse(); + req->SendOKResponse(); } -void WSRequestHandler::HandleGetTransitionDuration(WSRequestHandler *owner) +void WSRequestHandler::HandleGetTransitionDuration(WSRequestHandler *req) { obs_data_t* response = obs_data_create(); obs_data_set_int(response, "transition-duration", Utils::GetTransitionDuration()); - owner->SendOKResponse(response); + req->SendOKResponse(response); obs_data_release(response); } -void WSRequestHandler::HandleSetVolume(WSRequestHandler *owner) +void WSRequestHandler::HandleSetVolume(WSRequestHandler *req) { - const char *item_name = obs_data_get_string(owner->_requestData, "source"); - float item_volume = obs_data_get_double(owner->_requestData, "volume"); - - if (item_name == NULL || item_volume < 0.0 || item_volume > 1.0) + if (!req->hasField("source") || + !req->hasField("volume")) { - owner->SendErrorResponse("invalid request parameters"); + req->SendErrorResponse("missing request parameters"); return; } - obs_source_t* item = obs_get_source_by_name(item_name); - if (!item) + const char *source_name = obs_data_get_string(req->data, "source"); + float source_volume = obs_data_get_double(req->data, "volume"); + + if (source_name == NULL || strlen(source_name) < 1 || + source_volume < 0.0 || source_volume > 1.0) { - owner->SendErrorResponse("specified source doesn't exist"); + req->SendErrorResponse("invalid request parameters"); return; } - obs_source_set_volume(item, item_volume); - owner->SendOKResponse(); + obs_source_t* source = obs_get_source_by_name(source_name); + if (!source) + { + req->SendErrorResponse("specified source doesn't exist"); + return; + } - obs_source_release(item); + obs_source_set_volume(source, source_volume); + req->SendOKResponse(); + + obs_source_release(source); } -void WSRequestHandler::HandleGetVolume(WSRequestHandler *owner) +void WSRequestHandler::HandleGetVolume(WSRequestHandler *req) { - const char *item_name = obs_data_get_string(owner->_requestData, "source"); - - if (item_name) + if (!req->hasField("source")) { - obs_source_t* item = obs_get_source_by_name(item_name); + req->SendErrorResponse("missing request parameters"); + return; + } + + const char *source_name = obs_data_get_string(req->data, "source"); + + if (str_valid(source_name)) + { + obs_source_t* source = obs_get_source_by_name(source_name); obs_data_t* response = obs_data_create(); - obs_data_set_string(response, "name", item_name); - obs_data_set_double(response, "volume", obs_source_get_volume(item)); - obs_data_set_bool(response, "muted", obs_source_muted(item)); + obs_data_set_string(response, "name", source_name); + obs_data_set_double(response, "volume", obs_source_get_volume(source)); + obs_data_set_bool(response, "muted", obs_source_muted(source)); - owner->SendOKResponse(response); + req->SendOKResponse(response); obs_data_release(response); - obs_source_release(item); + obs_source_release(source); } else { - owner->SendErrorResponse("invalid request parameters"); + req->SendErrorResponse("invalid request parameters"); } } -void WSRequestHandler::HandleToggleMute(WSRequestHandler *owner) { - const char *item_name = obs_data_get_string(owner->_requestData, "source"); - if (item_name == NULL) - { - owner->SendErrorResponse("invalid request parameters"); - return; - } - - obs_source_t* item = obs_get_source_by_name(item_name); - if (!item) - { - owner->SendErrorResponse("invalid request parameters"); - return; - } - - obs_source_set_muted(item, !obs_source_muted(item)); - owner->SendOKResponse(); - - obs_source_release(item); -} - -void WSRequestHandler::HandleSetMute(WSRequestHandler *owner) +void WSRequestHandler::HandleToggleMute(WSRequestHandler *req) { - const char *item_name = obs_data_get_string(owner->_requestData, "source"); - bool mute = obs_data_get_bool(owner->_requestData, "mute"); - if (item_name == NULL) + if (!req->hasField("source")) { - owner->SendErrorResponse("invalid request parameters"); + req->SendErrorResponse("missing request parameters"); return; } - obs_source_t* item = obs_get_source_by_name(item_name); - if (!item) + const char *source_name = obs_data_get_string(req->data, "source"); + if (!str_valid(source_name)) { - owner->SendErrorResponse("specified source doesn't exist"); + req->SendErrorResponse("invalid request parameters"); return; } - obs_source_set_muted(item, mute); - owner->SendOKResponse(); + obs_source_t* source = obs_get_source_by_name(source_name); + if (!source) + { + req->SendErrorResponse("invalid request parameters"); + return; + } - obs_source_release(item); + obs_source_set_muted(source, !obs_source_muted(source)); + req->SendOKResponse(); + + obs_source_release(source); } -void WSRequestHandler::HandleSetSceneItemPosition(WSRequestHandler *owner) +void WSRequestHandler::HandleSetMute(WSRequestHandler *req) { - const char *item_name = obs_data_get_string(owner->_requestData, "item"); - if (!item_name) + if (!req->hasField("source") || + !req->hasField("mute")) { - owner->SendErrorResponse("invalid request parameters"); + req->SendErrorResponse("mssing request parameters"); return; } - const char *scene_name = obs_data_get_string(owner->_requestData, "scene-name"); + const char *source_name = obs_data_get_string(req->data, "source"); + bool mute = obs_data_get_bool(req->data, "mute"); + + if (!str_valid(source_name)) + { + req->SendErrorResponse("invalid request parameters"); + return; + } + + obs_source_t* source = obs_get_source_by_name(source_name); + if (!source) + { + req->SendErrorResponse("specified source doesn't exist"); + return; + } + + obs_source_set_muted(source, mute); + req->SendOKResponse(); + + obs_source_release(source); +} + +void WSRequestHandler::HandleGetMute(WSRequestHandler *req) +{ + if (!req->hasField("source")) + { + req->SendErrorResponse("mssing request parameters"); + return; + } + + const char *source_name = obs_data_get_string(req->data, "source"); + + if (!str_valid(source_name)) + { + req->SendErrorResponse("invalid request parameters"); + return; + } + + obs_source_t* source = obs_get_source_by_name(source_name); + if (!source) + { + req->SendErrorResponse("specified source doesn't exist"); + return; + } + + obs_data_t* response = obs_data_create(); + obs_data_set_string(response, "name", obs_source_get_name(source)); + obs_data_set_bool(response, "muted", obs_source_muted(source)); + + req->SendOKResponse(response); + + obs_source_release(source); + obs_data_release(response); +} + +void WSRequestHandler::HandleSetSceneItemPosition(WSRequestHandler *req) +{ + if (!req->hasField("item") || + !req->hasField("x") || !req->hasField("y")) + { + req->SendErrorResponse("missing request parameters"); + return; + } + + const char *item_name = obs_data_get_string(req->data, "item"); + if (!str_valid(item_name)) + { + req->SendErrorResponse("invalid request parameters"); + return; + } + + const char *scene_name = obs_data_get_string(req->data, "scene-name"); obs_source_t *scene = Utils::GetSceneFromNameOrCurrent(scene_name); - if (scene == NULL) { - owner->SendErrorResponse("requested scene could not be found"); + if (!scene) { + req->SendErrorResponse("requested scene could not be found"); return; } - vec2 item_position = {0}; - item_position.x = obs_data_get_double(owner->_requestData, "x"); - item_position.y = obs_data_get_double(owner->_requestData, "y"); - obs_sceneitem_t *scene_item = Utils::GetSceneItemFromName(scene, item_name); if (scene_item) { + vec2 item_position = { 0 }; + item_position.x = obs_data_get_double(req->data, "x"); + item_position.y = obs_data_get_double(req->data, "y"); + obs_sceneitem_set_pos(scene_item, &item_position); obs_sceneitem_release(scene_item); - owner->SendOKResponse(); + req->SendOKResponse(); } else { - owner->SendErrorResponse("specified scene item doesn't exist"); + req->SendErrorResponse("specified scene item doesn't exist"); } obs_source_release(scene); } -void WSRequestHandler::HandleSetSceneItemTransform(WSRequestHandler *owner) +void WSRequestHandler::HandleSetSceneItemTransform(WSRequestHandler *req) { - const char *item_name = obs_data_get_string(owner->_requestData, "item"); - if (!item_name) + if (!req->hasField("item") || + !req->hasField("x-scale") || + !req->hasField("y-scale") || + !req->hasField("rotation")) { - owner->SendErrorResponse("invalid request parameters"); + req->SendErrorResponse("missing request parameters"); return; } - const char *scene_name = obs_data_get_string(owner->_requestData, "scene-name"); + const char *item_name = obs_data_get_string(req->data, "item"); + if (!str_valid(item_name)) + { + req->SendErrorResponse("invalid request parameters"); + return; + } + + const char *scene_name = obs_data_get_string(req->data, "scene-name"); obs_source_t* scene = Utils::GetSceneFromNameOrCurrent(scene_name); - if (scene == NULL) { - owner->SendErrorResponse("requested scene doesn't exist"); + if (!scene) { + req->SendErrorResponse("requested scene doesn't exist"); return; } vec2 scale; - scale.x = obs_data_get_double(owner->_requestData, "x-scale"); - scale.y = obs_data_get_double(owner->_requestData, "y-scale"); + scale.x = obs_data_get_double(req->data, "x-scale"); + scale.y = obs_data_get_double(req->data, "y-scale"); - float rotation = obs_data_get_double(owner->_requestData, "rotation"); + float rotation = obs_data_get_double(req->data, "rotation"); obs_sceneitem_t *scene_item = Utils::GetSceneItemFromName(scene, item_name); @@ -621,29 +744,35 @@ void WSRequestHandler::HandleSetSceneItemTransform(WSRequestHandler *owner) obs_sceneitem_set_rot(scene_item, rotation); obs_sceneitem_release(scene_item); - owner->SendOKResponse(); + req->SendOKResponse(); } else { - owner->SendErrorResponse("specified scene item doesn't exist"); + req->SendErrorResponse("specified scene item doesn't exist"); } obs_source_release(scene); } -void WSRequestHandler::HandleSetSceneItemCrop(WSRequestHandler *owner) +void WSRequestHandler::HandleSetSceneItemCrop(WSRequestHandler *req) { - const char *item_name = obs_data_get_string(owner->_requestData, "item"); - if (!item_name) + if (!req->hasField("item")) { - owner->SendErrorResponse("invalid request parameters"); + req->SendErrorResponse("missing request parameters"); return; } - const char *scene_name = obs_data_get_string(owner->_requestData, "scene-name"); + const char *item_name = obs_data_get_string(req->data, "item"); + if (!str_valid(item_name)) + { + req->SendErrorResponse("invalid request parameters"); + return; + } + + const char *scene_name = obs_data_get_string(req->data, "scene-name"); obs_source_t* scene = Utils::GetSceneFromNameOrCurrent(scene_name); - if (scene == NULL) { - owner->SendErrorResponse("requested scene doesn't exist"); + if (!scene) { + req->SendErrorResponse("requested scene doesn't exist"); return; } @@ -652,119 +781,131 @@ void WSRequestHandler::HandleSetSceneItemCrop(WSRequestHandler *owner) if (scene_item) { struct obs_sceneitem_crop crop = { 0 }; - crop.top = obs_data_get_int(owner->_requestData, "top"); - crop.bottom = obs_data_get_int(owner->_requestData, "bottom");; - crop.left = obs_data_get_int(owner->_requestData, "left");; - crop.right = obs_data_get_int(owner->_requestData, "right"); + crop.top = obs_data_get_int(req->data, "top"); + crop.bottom = obs_data_get_int(req->data, "bottom");; + crop.left = obs_data_get_int(req->data, "left");; + crop.right = obs_data_get_int(req->data, "right"); obs_sceneitem_set_crop(scene_item, &crop); obs_sceneitem_release(scene_item); - owner->SendOKResponse(); + req->SendOKResponse(); } else { - owner->SendErrorResponse("specified scene item doesn't exist"); + req->SendErrorResponse("specified scene item doesn't exist"); } obs_source_release(scene); } -void WSRequestHandler::HandleSetCurrentSceneCollection(WSRequestHandler *owner) +void WSRequestHandler::HandleSetCurrentSceneCollection(WSRequestHandler *req) { - const char* scene_collection = obs_data_get_string(owner->_requestData, "sc-name"); + if (!req->hasField("sc-name")) + { + req->SendErrorResponse("missing request parameters"); + return; + } - if (scene_collection) + const char* scene_collection = obs_data_get_string(req->data, "sc-name"); + + if (str_valid(scene_collection)) { // TODO : Check if specified profile exists and if changing is allowed obs_frontend_set_current_scene_collection(scene_collection); - owner->SendOKResponse(); + req->SendOKResponse(); } else { - owner->SendErrorResponse("invalid request parameters"); + req->SendErrorResponse("invalid request parameters"); } } -void WSRequestHandler::HandleGetCurrentSceneCollection(WSRequestHandler *owner) +void WSRequestHandler::HandleGetCurrentSceneCollection(WSRequestHandler *req) { obs_data_t *response = obs_data_create(); obs_data_set_string(response, "sc-name", obs_frontend_get_current_scene_collection()); - owner->SendOKResponse(response); + req->SendOKResponse(response); obs_data_release(response); } -void WSRequestHandler::HandleListSceneCollections(WSRequestHandler *owner) +void WSRequestHandler::HandleListSceneCollections(WSRequestHandler *req) { obs_data_array_t *scene_collections = Utils::GetSceneCollections(); obs_data_t *response = obs_data_create(); obs_data_set_array(response, "scene-collections", scene_collections); - owner->SendOKResponse(response); + req->SendOKResponse(response); obs_data_release(response); obs_data_array_release(scene_collections); } -void WSRequestHandler::HandleSetCurrentProfile(WSRequestHandler *owner) +void WSRequestHandler::HandleSetCurrentProfile(WSRequestHandler *req) { - const char* profile_name = obs_data_get_string(owner->_requestData, "profile-name"); + if (!req->hasField("profile-name")) + { + req->SendErrorResponse("missing request parameters"); + return; + } - if (profile_name) + const char* profile_name = obs_data_get_string(req->data, "profile-name"); + + if (str_valid(profile_name)) { // TODO : check if profile exists obs_frontend_set_current_profile(profile_name); - owner->SendOKResponse(); + req->SendOKResponse(); } else { - owner->SendErrorResponse("invalid request parameters"); + req->SendErrorResponse("invalid request parameters"); } } -void WSRequestHandler::HandleGetCurrentProfile(WSRequestHandler *owner) +void WSRequestHandler::HandleGetCurrentProfile(WSRequestHandler *req) { obs_data_t *response = obs_data_create(); obs_data_set_string(response, "profile-name", obs_frontend_get_current_profile()); - owner->SendOKResponse(response); + req->SendOKResponse(response); obs_data_release(response); } -void WSRequestHandler::HandleListProfiles(WSRequestHandler *owner) +void WSRequestHandler::HandleListProfiles(WSRequestHandler *req) { obs_data_array_t *profiles = Utils::GetProfiles(); obs_data_t *response = obs_data_create(); obs_data_set_array(response, "profiles", profiles); - owner->SendOKResponse(response); + req->SendOKResponse(response); obs_data_release(response); obs_data_array_release(profiles); } -void WSRequestHandler::HandleGetStudioModeStatus(WSRequestHandler *owner) +void WSRequestHandler::HandleGetStudioModeStatus(WSRequestHandler *req) { bool previewActive = Utils::IsPreviewModeActive(); obs_data_t* response = obs_data_create(); obs_data_set_bool(response, "studio-mode", previewActive); - owner->SendOKResponse(response); + req->SendOKResponse(response); obs_data_release(response); } -void WSRequestHandler::HandleGetPreviewScene(WSRequestHandler *owner) +void WSRequestHandler::HandleGetPreviewScene(WSRequestHandler *req) { if (!Utils::IsPreviewModeActive()) { - owner->SendErrorResponse("studio mode not enabled"); + req->SendErrorResponse("studio mode not enabled"); return; } @@ -778,7 +919,7 @@ void WSRequestHandler::HandleGetPreviewScene(WSRequestHandler *owner) obs_data_set_string(data, "name", name); obs_data_set_array(data, "sources", scene_items); - owner->SendOKResponse(data); + req->SendOKResponse(data); obs_data_release(data); obs_data_array_release(scene_items); @@ -786,52 +927,57 @@ void WSRequestHandler::HandleGetPreviewScene(WSRequestHandler *owner) obs_scene_release(preview_scene); } -void WSRequestHandler::HandleSetPreviewScene(WSRequestHandler *owner) +void WSRequestHandler::HandleSetPreviewScene(WSRequestHandler *req) { if (!Utils::IsPreviewModeActive()) { - owner->SendErrorResponse("studio mode not enabled"); + req->SendErrorResponse("studio mode not enabled"); return; } - if (!obs_data_has_user_value(owner->_requestData, "scene-name")) + if (!req->hasField("scene-name")) { - owner->SendErrorResponse("invalid request parameters"); + req->SendErrorResponse("missing request parameters"); return; } - const char* scene_name = obs_data_get_string(owner->_requestData, "scene-name"); + const char* scene_name = obs_data_get_string(req->data, "scene-name"); Utils::SetPreviewScene(scene_name); - owner->SendOKResponse(); + req->SendOKResponse(); } -void WSRequestHandler::HandleTransitionToProgram(WSRequestHandler *owner) +void WSRequestHandler::HandleTransitionToProgram(WSRequestHandler *req) { if (!Utils::IsPreviewModeActive()) { - owner->SendErrorResponse("studio mode not enabled"); + req->SendErrorResponse("studio mode not enabled"); return; } - if (obs_data_has_user_value(owner->_requestData, "with-transition")) + if (req->hasField("with-transition")) { - obs_data_t* transitionInfo = obs_data_get_obj(owner->_requestData, "with-transition"); + obs_data_t* transitionInfo = obs_data_get_obj(req->data, "with-transition"); - if (obs_data_has_user_value(transitionInfo, "name")) + if (req->hasField("name")) { const char* transitionName = obs_data_get_string(transitionInfo, "name"); - bool success = Utils::SetTransitionByName(transitionName); + if (!str_valid(transitionName)) + { + req->SendErrorResponse("invalid request parameters"); + return; + } + bool success = Utils::SetTransitionByName(transitionName); if (!success) { - owner->SendErrorResponse("specified transition doesn't exist"); + req->SendErrorResponse("specified transition doesn't exist"); obs_data_release(transitionInfo); return; } } - if (obs_data_has_user_value(transitionInfo, "duration")) + if (req->hasField("duration")) { int transitionDuration = obs_data_get_int(transitionInfo, "duration"); Utils::SetTransitionDuration(transitionDuration); @@ -841,28 +987,23 @@ void WSRequestHandler::HandleTransitionToProgram(WSRequestHandler *owner) } Utils::TransitionToProgram(); - owner->SendOKResponse(); + req->SendOKResponse(); } -void WSRequestHandler::HandleEnableStudioMode(WSRequestHandler *owner) +void WSRequestHandler::HandleEnableStudioMode(WSRequestHandler *req) { Utils::EnablePreviewMode(); - owner->SendOKResponse(); + req->SendOKResponse(); } -void WSRequestHandler::HandleDisableStudioMode(WSRequestHandler *owner) +void WSRequestHandler::HandleDisableStudioMode(WSRequestHandler *req) { Utils::DisablePreviewMode(); - owner->SendOKResponse(); + req->SendOKResponse(); } -void WSRequestHandler::HandleToggleStudioMode(WSRequestHandler *owner) +void WSRequestHandler::HandleToggleStudioMode(WSRequestHandler *req) { Utils::TogglePreviewMode(); - owner->SendOKResponse(); -} - -void WSRequestHandler::ErrNotImplemented(WSRequestHandler *owner) -{ - owner->SendErrorResponse("not implemented"); + req->SendOKResponse(); } \ No newline at end of file diff --git a/WSRequestHandler.h b/WSRequestHandler.h index c1c67680..a253e5db 100644 --- a/WSRequestHandler.h +++ b/WSRequestHandler.h @@ -32,69 +32,69 @@ class WSRequestHandler : public QObject explicit WSRequestHandler(QWebSocket *client); ~WSRequestHandler(); void processIncomingMessage(QString textMessage); + bool hasField(const char* name); private: QWebSocket *_client; const char *_messageId; const char *_requestType; - obs_data_t *_requestData; + obs_data_t *data; QMap messageMap; QSet authNotRequired; void SendOKResponse(obs_data_t *additionalFields = NULL); void SendErrorResponse(const char *errorMessage); - static void ErrNotImplemented(WSRequestHandler *owner); - static void HandleGetVersion(WSRequestHandler *owner); - static void HandleGetAuthRequired(WSRequestHandler *owner); - static void HandleAuthenticate(WSRequestHandler *owner); + static void HandleGetVersion(WSRequestHandler *req); + static void HandleGetAuthRequired(WSRequestHandler *req); + static void HandleAuthenticate(WSRequestHandler *req); - static void HandleSetCurrentScene(WSRequestHandler *owner); - static void HandleGetCurrentScene(WSRequestHandler *owner); - static void HandleGetSceneList(WSRequestHandler *owner); + static void HandleSetCurrentScene(WSRequestHandler *req); + static void HandleGetCurrentScene(WSRequestHandler *req); + static void HandleGetSceneList(WSRequestHandler *req); - static void HandleSetSceneItemRender(WSRequestHandler *owner); - static void HandleSetSceneItemPosition(WSRequestHandler *owner); - static void HandleSetSceneItemTransform(WSRequestHandler *owner); - static void HandleSetSceneItemCrop(WSRequestHandler *owner); + static void HandleSetSceneItemRender(WSRequestHandler *req); + static void HandleSetSceneItemPosition(WSRequestHandler *req); + static void HandleSetSceneItemTransform(WSRequestHandler *req); + static void HandleSetSceneItemCrop(WSRequestHandler *req); - static void HandleGetStreamingStatus(WSRequestHandler *owner); - static void HandleStartStopStreaming(WSRequestHandler *owner); - static void HandleStartStopRecording(WSRequestHandler *owner); - static void HandleStartStreaming(WSRequestHandler *owner); - static void HandleStopStreaming(WSRequestHandler *owner); - static void HandleStartRecording(WSRequestHandler *owner); - static void HandleStopRecording(WSRequestHandler *owner); + static void HandleGetStreamingStatus(WSRequestHandler *req); + static void HandleStartStopStreaming(WSRequestHandler *req); + static void HandleStartStopRecording(WSRequestHandler *req); + static void HandleStartStreaming(WSRequestHandler *req); + static void HandleStopStreaming(WSRequestHandler *req); + static void HandleStartRecording(WSRequestHandler *req); + static void HandleStopRecording(WSRequestHandler *req); - static void HandleGetTransitionList(WSRequestHandler *owner); - static void HandleGetCurrentTransition(WSRequestHandler *owner); - static void HandleSetCurrentTransition(WSRequestHandler *owner); + static void HandleGetTransitionList(WSRequestHandler *req); + static void HandleGetCurrentTransition(WSRequestHandler *req); + static void HandleSetCurrentTransition(WSRequestHandler *req); - static void HandleSetVolume(WSRequestHandler *owner); - static void HandleGetVolume(WSRequestHandler *owner); - static void HandleToggleMute(WSRequestHandler *owner); - static void HandleSetMute(WSRequestHandler *owner); - // TODO : GetMute + static void HandleSetVolume(WSRequestHandler *req); + static void HandleGetVolume(WSRequestHandler *req); + static void HandleToggleMute(WSRequestHandler *req); + static void HandleSetMute(WSRequestHandler *req); + static void HandleGetMute(WSRequestHandler *req); - static void HandleSetCurrentSceneCollection(WSRequestHandler *owner); - static void HandleGetCurrentSceneCollection(WSRequestHandler *owner); - static void HandleListSceneCollections(WSRequestHandler *owner); + static void HandleSetCurrentSceneCollection(WSRequestHandler *req); + static void HandleGetCurrentSceneCollection(WSRequestHandler *req); + static void HandleListSceneCollections(WSRequestHandler *req); - static void HandleSetCurrentProfile(WSRequestHandler *owner); - static void HandleGetCurrentProfile(WSRequestHandler *owner); - static void HandleListProfiles(WSRequestHandler *owner); + static void HandleSetCurrentProfile(WSRequestHandler *req); + static void HandleGetCurrentProfile(WSRequestHandler *req); + static void HandleListProfiles(WSRequestHandler *req); - static void HandleSetTransitionDuration(WSRequestHandler *owner); - static void HandleGetTransitionDuration(WSRequestHandler *owner); + static void HandleSetTransitionDuration(WSRequestHandler *req); + static void HandleGetTransitionDuration(WSRequestHandler *req); - static void HandleGetStudioModeStatus(WSRequestHandler *owner); - static void HandleGetPreviewScene(WSRequestHandler *owner); - static void HandleSetPreviewScene(WSRequestHandler *owner); - static void HandleTransitionToProgram(WSRequestHandler *owner); - static void HandleEnableStudioMode(WSRequestHandler *owner); - static void HandleDisableStudioMode(WSRequestHandler *owner); - static void HandleToggleStudioMode(WSRequestHandler *owner); + static void HandleGetStudioModeStatus(WSRequestHandler *req); + static void HandleGetPreviewScene(WSRequestHandler *req); + static void HandleSetPreviewScene(WSRequestHandler *req); + static void HandleTransitionToProgram(WSRequestHandler *req); + static void HandleEnableStudioMode(WSRequestHandler *req); + static void HandleDisableStudioMode(WSRequestHandler *req); + static void HandleToggleStudioMode(WSRequestHandler *req); }; #endif // WSPROTOCOL_H diff --git a/obs-websocket.h b/obs-websocket.h index 5114d720..ed6f87d0 100644 --- a/obs-websocket.h +++ b/obs-websocket.h @@ -21,6 +21,7 @@ with this program. If not, see #define PROP_AUTHENTICATED "wsclient_authenticated" #define OBS_WEBSOCKET_VERSION "4.1.0" +#define API_VERSION 1.3 #define blog(level, msg, ...) blog(level, "[obs-websocket] " msg, ##__VA_ARGS__)