diff --git a/src/WebSocketApi.cpp b/src/WebSocketApi.cpp index f2e0a1eb..bd5540ba 100644 --- a/src/WebSocketApi.cpp +++ b/src/WebSocketApi.cpp @@ -55,7 +55,7 @@ enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::str std::shared_lock l(_mutex); if (_vendors.count(vendorName) == 0) - return WEBSOCKET_API_REQUEST_RETURN_CODE_NO_VENDOR; + return RequestReturnCode::NoVendor; auto v = _vendors[vendorName]; @@ -64,7 +64,7 @@ enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::str std::shared_lock v_l(v->_mutex); if (v->_requests.count(requestType) == 0) - return WEBSOCKET_API_REQUEST_RETURN_CODE_NO_VENDOR_REQUEST; + return RequestReturnCode::NoVendorRequest; auto cb = v->_requests[requestType]; @@ -72,7 +72,7 @@ enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::str cb.callback(requestData, responseData, cb.priv_data); - return WEBSOCKET_API_REQUEST_RETURN_CODE_NORMAL; + return RequestReturnCode::Normal; } void WebSocketApi::get_ph_cb(void *priv_data, calldata_t *cd) diff --git a/src/WebSocketApi.h b/src/WebSocketApi.h index 6a9be8c5..78d02cea 100644 --- a/src/WebSocketApi.h +++ b/src/WebSocketApi.h @@ -12,9 +12,9 @@ class WebSocketApi { public: enum RequestReturnCode { - WEBSOCKET_API_REQUEST_RETURN_CODE_NORMAL, - WEBSOCKET_API_REQUEST_RETURN_CODE_NO_VENDOR, - WEBSOCKET_API_REQUEST_RETURN_CODE_NO_VENDOR_REQUEST, + Normal, + NoVendor, + NoVendorRequest, }; typedef std::function EventCallback; diff --git a/src/obs-websocket.cpp b/src/obs-websocket.cpp index 405cba4d..1d6508da 100644 --- a/src/obs-websocket.cpp +++ b/src/obs-websocket.cpp @@ -138,7 +138,7 @@ void WebSocketApiEventCallback(std::string vendorName, std::string eventType, ob broadcastEventData["eventType"] = eventType; broadcastEventData["eventData"] = eventData; - _webSocketServer->BroadcastEvent(EventSubscription::ExternalPlugins, "ExternalPluginEvent", broadcastEventData); + _webSocketServer->BroadcastEvent(EventSubscription::ExternalPlugins, "VendorEvent", broadcastEventData); } void ___source_dummy_addref(obs_source_t*) {} @@ -151,3 +151,40 @@ void ___output_dummy_addref(obs_output_t*) {} void ___data_item_dummy_addref(obs_data_item_t*) {} void ___data_item_release(obs_data_item_t* dataItem){ obs_data_item_release(&dataItem); } void ___properties_dummy_addref(obs_properties_t*) {} + + +#define PLUGIN_API_TEST +#ifdef PLUGIN_API_TEST + +static void test_vendor_request_cb(obs_data_t *requestData, obs_data_t *responseData, void *priv_data) +{ + blog(LOG_INFO, "[test_vendor_request_cb] Request called!"); + + blog(LOG_INFO, "[test_vendor_request_cb] Request data: %s", obs_data_get_json(requestData)); + + // Set an item to the response data + obs_data_set_string(responseData, "test", "pp"); + + // Emit an event with the request data as the event data + obs_websocket_vendor_emit_event(priv_data, "TestEvent", requestData); +} + +void obs_module_post_load() +{ + blog(LOG_INFO, "[obs_module_post_load] Post load started."); + + auto vendor = obs_websocket_register_vendor("obs-websocket-test"); + if (!vendor) { + blog(LOG_WARNING, "[obs_module_post_load] Failed to create vendor!"); + return; + } + + if (!obs_websocket_vendor_register_request(vendor, "TestRequest", test_vendor_request_cb, vendor)) { + blog(LOG_WARNING, "[obs_module_post_load] Failed to register vendor request!"); + return; + } + + blog(LOG_INFO, "[obs_module_post_load] Post load completed."); +} + +#endif diff --git a/src/requesthandler/RequestHandler.cpp b/src/requesthandler/RequestHandler.cpp index 13ba5135..ee747307 100644 --- a/src/requesthandler/RequestHandler.cpp +++ b/src/requesthandler/RequestHandler.cpp @@ -25,6 +25,7 @@ const std::map RequestHandler::_handlerMap {"GetVersion", &RequestHandler::GetVersion}, {"GetStats", &RequestHandler::GetStats}, {"BroadcastCustomEvent", &RequestHandler::BroadcastCustomEvent}, + {"CallVendorRequest", &RequestHandler::CallVendorRequest}, {"GetHotkeyList", &RequestHandler::GetHotkeyList}, {"TriggerHotkeyByName", &RequestHandler::TriggerHotkeyByName}, {"TriggerHotkeyByKeySequence", &RequestHandler::TriggerHotkeyByKeySequence}, diff --git a/src/requesthandler/RequestHandler.h b/src/requesthandler/RequestHandler.h index eeda1ec5..4594a442 100644 --- a/src/requesthandler/RequestHandler.h +++ b/src/requesthandler/RequestHandler.h @@ -47,6 +47,7 @@ class RequestHandler { RequestResult GetVersion(const Request&); RequestResult GetStats(const Request&); RequestResult BroadcastCustomEvent(const Request&); + RequestResult CallVendorRequest(const Request&); RequestResult GetHotkeyList(const Request&); RequestResult TriggerHotkeyByName(const Request&); RequestResult TriggerHotkeyByKeySequence(const Request&); diff --git a/src/requesthandler/RequestHandler_General.cpp b/src/requesthandler/RequestHandler_General.cpp index 55de18cc..31c8375e 100644 --- a/src/requesthandler/RequestHandler_General.cpp +++ b/src/requesthandler/RequestHandler_General.cpp @@ -22,6 +22,7 @@ with this program. If not, see #include "RequestHandler.h" #include "../websocketserver/WebSocketServer.h" #include "../eventhandler/types/EventSubscription.h" +#include "../WebSocketApi.h" #include "../obs-websocket.h" @@ -111,13 +112,73 @@ RequestResult RequestHandler::BroadcastCustomEvent(const Request& request) auto webSocketServer = GetWebSocketServer(); if (!webSocketServer) - return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to send event."); + return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to send event due to internal error."); webSocketServer->BroadcastEvent(EventSubscription::General, "CustomEvent", request.RequestData["eventData"]); return RequestResult::Success(); } +/** + * Call a request registered to a vendor. + * + * A vendor is a unique name registered by a third-party plugin or script, which allows for custom requests and events to be added to obs-websocket. + * If a plugin or script implements vendor requests or events, documentation is expected to be provided with them. + * + * @requestField vendorName | String | Name of the vendor to use + * @requestField requestType | String | The request type to call + * @requestField ?requestData | Object | Object containing appropriate request data | {} + * + * @responseField responseData | Object | Object containing appropriate response data. May be null if vendor request does not implement responses + * + * @requestType CallVendorRequest + * @complexity 3 + * @rpcVersion -1 + * @initialVersion 5.0.0 + * @category general + * @api requests + */ +RequestResult RequestHandler::CallVendorRequest(const Request& request) +{ + RequestStatus::RequestStatus statusCode; + std::string comment; + if (!request.ValidateString("vendorName", statusCode, comment) || !request.ValidateString("requestType", statusCode, comment)) + return RequestResult::Error(statusCode, comment); + + std::string vendorName = request.RequestData["vendorName"]; + std::string requestType = request.RequestData["requestType"]; + + OBSDataAutoRelease requestData = obs_data_create(); + if (request.Contains("requestData")) { + if (!request.ValidateOptionalObject("requestData", statusCode, comment)) + return RequestResult::Error(statusCode, comment); + + requestData = Utils::Json::JsonToObsData(request.RequestData["requestData"]); + } + + OBSDataAutoRelease obsResponseData = obs_data_create(); + + auto webSocketApi = GetWebSocketApi(); + if (!webSocketApi) + return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to call request due to internal error."); + + auto ret = webSocketApi->PerformVendorRequest(vendorName, requestType, requestData, obsResponseData); + switch (ret) { + default: + case WebSocketApi::RequestReturnCode::Normal: + break; + case WebSocketApi::RequestReturnCode::NoVendor: + return RequestResult::Error(RequestStatus::ResourceNotFound, "No vendor was found by that name."); + case WebSocketApi::RequestReturnCode::NoVendorRequest: + return RequestResult::Error(RequestStatus::ResourceNotFound, "No request was found by that name."); + } + + json responseData; + responseData["responseData"] = Utils::Json::ObsDataToJson(obsResponseData); + + return RequestResult::Success(responseData); +} + /** * Gets an array of all hotkey names in OBS *