2021-11-20 09:26:50 +00:00
|
|
|
#include "WebSocketApi.h"
|
2022-05-11 05:49:47 +00:00
|
|
|
#include "requesthandler/RequestHandler.h"
|
2021-11-20 09:26:50 +00:00
|
|
|
#include "obs-websocket.h"
|
2022-05-11 05:49:47 +00:00
|
|
|
#include "utils/Json.h"
|
2021-11-20 09:26:50 +00:00
|
|
|
|
2022-05-14 04:17:17 +00:00
|
|
|
#define RETURN_STATUS(status) \
|
|
|
|
{ \
|
|
|
|
calldata_set_bool(cd, "success", status); \
|
|
|
|
return; \
|
|
|
|
}
|
2021-11-20 09:26:50 +00:00
|
|
|
#define RETURN_SUCCESS() RETURN_STATUS(true);
|
|
|
|
#define RETURN_FAILURE() RETURN_STATUS(false);
|
|
|
|
|
|
|
|
WebSocketApi::Vendor *get_vendor(calldata_t *cd)
|
|
|
|
{
|
|
|
|
void *voidVendor;
|
|
|
|
if (!calldata_get_ptr(cd, "vendor", &voidVendor)) {
|
2022-06-08 04:35:10 +00:00
|
|
|
blog(LOG_WARNING, "[WebSocketApi: get_vendor] Failed due to missing `vendor` pointer.");
|
2021-11-20 09:26:50 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-05-14 04:17:17 +00:00
|
|
|
return static_cast<WebSocketApi::Vendor *>(voidVendor);
|
2021-11-20 09:26:50 +00:00
|
|
|
}
|
|
|
|
|
2021-12-22 01:52:46 +00:00
|
|
|
WebSocketApi::WebSocketApi()
|
2021-11-20 09:26:50 +00:00
|
|
|
{
|
2021-11-20 09:50:49 +00:00
|
|
|
blog_debug("[WebSocketApi::WebSocketApi] Setting up...");
|
2021-11-20 09:26:50 +00:00
|
|
|
|
|
|
|
_procHandler = proc_handler_create();
|
|
|
|
|
2022-06-08 04:35:10 +00:00
|
|
|
proc_handler_add(_procHandler, "bool get_api_version(out int version)", &get_api_version, nullptr);
|
|
|
|
proc_handler_add(_procHandler, "bool call_request(in string request_type, in string request_data, out ptr response)",
|
|
|
|
&call_request, nullptr);
|
|
|
|
proc_handler_add(_procHandler, "bool vendor_register(in string name, out ptr vendor)", &vendor_register_cb, this);
|
|
|
|
proc_handler_add(_procHandler, "bool vendor_request_register(in ptr vendor, in string type, in ptr callback)",
|
|
|
|
&vendor_request_register_cb, this);
|
|
|
|
proc_handler_add(_procHandler, "bool vendor_request_unregister(in ptr vendor, in string type)",
|
|
|
|
&vendor_request_unregister_cb, this);
|
|
|
|
proc_handler_add(_procHandler, "bool vendor_event_emit(in ptr vendor, in string type, in ptr data)", &vendor_event_emit_cb,
|
|
|
|
this);
|
2021-11-20 09:26:50 +00:00
|
|
|
|
|
|
|
proc_handler_t *ph = obs_get_proc_handler();
|
|
|
|
assert(ph != NULL);
|
|
|
|
|
2022-06-08 04:35:10 +00:00
|
|
|
proc_handler_add(ph, "bool obs_websocket_api_get_ph(out ptr ph)", &get_ph_cb, this);
|
2021-11-20 09:26:50 +00:00
|
|
|
|
2021-11-20 09:50:49 +00:00
|
|
|
blog_debug("[WebSocketApi::WebSocketApi] Finished.");
|
2021-11-20 09:26:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
WebSocketApi::~WebSocketApi()
|
|
|
|
{
|
2021-11-20 09:50:49 +00:00
|
|
|
blog_debug("[WebSocketApi::~WebSocketApi] Shutting down...");
|
2021-11-20 09:26:50 +00:00
|
|
|
|
|
|
|
proc_handler_destroy(_procHandler);
|
|
|
|
|
|
|
|
for (auto vendor : _vendors) {
|
2022-06-08 04:35:10 +00:00
|
|
|
blog_debug("[WebSocketApi::~WebSocketApi] Deleting vendor: %s", vendor.first.c_str());
|
2021-11-20 09:26:50 +00:00
|
|
|
delete vendor.second;
|
|
|
|
}
|
|
|
|
|
2021-11-20 09:50:49 +00:00
|
|
|
blog_debug("[WebSocketApi::~WebSocketApi] Finished.");
|
2021-11-20 09:26:50 +00:00
|
|
|
}
|
|
|
|
|
2021-12-22 01:52:46 +00:00
|
|
|
void WebSocketApi::SetEventCallback(EventCallback cb)
|
|
|
|
{
|
|
|
|
_eventCallback = cb;
|
|
|
|
}
|
|
|
|
|
2022-06-08 04:35:10 +00:00
|
|
|
enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::string vendorName, std::string requestType,
|
|
|
|
obs_data_t *requestData, obs_data_t *responseData)
|
2021-11-20 09:26:50 +00:00
|
|
|
{
|
|
|
|
std::shared_lock l(_mutex);
|
|
|
|
|
|
|
|
if (_vendors.count(vendorName) == 0)
|
2021-12-15 02:22:52 +00:00
|
|
|
return RequestReturnCode::NoVendor;
|
2021-11-20 09:26:50 +00:00
|
|
|
|
|
|
|
auto v = _vendors[vendorName];
|
|
|
|
|
|
|
|
l.unlock();
|
|
|
|
|
|
|
|
std::shared_lock v_l(v->_mutex);
|
|
|
|
|
|
|
|
if (v->_requests.count(requestType) == 0)
|
2021-12-15 02:22:52 +00:00
|
|
|
return RequestReturnCode::NoVendorRequest;
|
2021-11-20 09:26:50 +00:00
|
|
|
|
|
|
|
auto cb = v->_requests[requestType];
|
|
|
|
|
|
|
|
v_l.unlock();
|
|
|
|
|
|
|
|
cb.callback(requestData, responseData, cb.priv_data);
|
|
|
|
|
2021-12-15 02:22:52 +00:00
|
|
|
return RequestReturnCode::Normal;
|
2021-11-20 09:26:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void WebSocketApi::get_ph_cb(void *priv_data, calldata_t *cd)
|
|
|
|
{
|
2022-05-14 04:17:17 +00:00
|
|
|
auto c = static_cast<WebSocketApi *>(priv_data);
|
2021-11-20 09:26:50 +00:00
|
|
|
|
2022-05-14 04:17:17 +00:00
|
|
|
calldata_set_ptr(cd, "ph", (void *)c->_procHandler);
|
2021-11-20 09:26:50 +00:00
|
|
|
|
|
|
|
RETURN_SUCCESS();
|
|
|
|
}
|
|
|
|
|
2022-05-11 05:49:47 +00:00
|
|
|
void WebSocketApi::get_api_version(void *, calldata_t *cd)
|
|
|
|
{
|
|
|
|
calldata_set_int(cd, "version", OBS_WEBSOCKET_API_VERSION);
|
|
|
|
|
|
|
|
RETURN_SUCCESS();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebSocketApi::call_request(void *, calldata_t *cd)
|
|
|
|
{
|
|
|
|
const char *request_type = calldata_string(cd, "request_type");
|
|
|
|
const char *request_data = calldata_string(cd, "request_data");
|
|
|
|
|
|
|
|
if (!request_type)
|
|
|
|
RETURN_FAILURE();
|
|
|
|
|
2022-06-08 04:35:10 +00:00
|
|
|
auto response = static_cast<obs_websocket_request_response *>(bzalloc(sizeof(struct obs_websocket_request_response)));
|
2022-05-11 05:49:47 +00:00
|
|
|
if (!response)
|
|
|
|
RETURN_FAILURE();
|
|
|
|
|
|
|
|
json requestData;
|
|
|
|
if (request_data)
|
|
|
|
requestData = json::parse(request_data);
|
|
|
|
|
|
|
|
RequestHandler requestHandler;
|
|
|
|
Request request(request_type, requestData);
|
|
|
|
RequestResult requestResult = requestHandler.ProcessRequest(request);
|
|
|
|
|
2022-05-14 02:06:03 +00:00
|
|
|
response->status_code = (unsigned int)requestResult.StatusCode;
|
2022-05-11 05:49:47 +00:00
|
|
|
if (!requestResult.Comment.empty())
|
|
|
|
response->comment = bstrdup(requestResult.Comment.c_str());
|
|
|
|
if (requestResult.ResponseData.is_object()) {
|
|
|
|
std::string responseData = requestResult.ResponseData.dump();
|
|
|
|
response->response_data = bstrdup(responseData.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
calldata_set_ptr(cd, "response", response);
|
|
|
|
|
2022-06-08 04:35:10 +00:00
|
|
|
blog_debug("[WebSocketApi::call_request] Request %s called, response status code is %u", request_type,
|
|
|
|
response->status_code);
|
2022-05-11 05:49:47 +00:00
|
|
|
|
|
|
|
RETURN_SUCCESS();
|
|
|
|
}
|
|
|
|
|
2021-11-20 09:26:50 +00:00
|
|
|
void WebSocketApi::vendor_register_cb(void *priv_data, calldata_t *cd)
|
|
|
|
{
|
2022-05-14 04:17:17 +00:00
|
|
|
auto c = static_cast<WebSocketApi *>(priv_data);
|
2021-11-20 09:26:50 +00:00
|
|
|
|
|
|
|
const char *vendorName;
|
2022-06-08 04:35:10 +00:00
|
|
|
if (!calldata_get_string(cd, "name", &vendorName) || strlen(vendorName) == 0) {
|
|
|
|
blog(LOG_WARNING, "[WebSocketApi::vendor_register_cb] Failed due to missing `name` string.");
|
2021-11-20 09:26:50 +00:00
|
|
|
RETURN_FAILURE();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Theoretically doesn't need a mutex, but it's good to be safe.
|
|
|
|
std::unique_lock l(c->_mutex);
|
|
|
|
|
|
|
|
if (c->_vendors.count(vendorName)) {
|
2022-06-08 04:35:10 +00:00
|
|
|
blog(LOG_WARNING, "[WebSocketApi::vendor_register_cb] Failed because `%s` is already a registered vendor.",
|
2022-05-14 04:17:17 +00:00
|
|
|
vendorName);
|
2021-11-20 09:26:50 +00:00
|
|
|
RETURN_FAILURE();
|
|
|
|
}
|
|
|
|
|
2022-05-14 04:17:17 +00:00
|
|
|
Vendor *v = new Vendor();
|
2021-11-20 09:26:50 +00:00
|
|
|
v->_name = vendorName;
|
|
|
|
|
|
|
|
c->_vendors[vendorName] = v;
|
|
|
|
|
2022-06-08 04:35:10 +00:00
|
|
|
blog_debug("[WebSocketApi::vendor_register_cb] [vendorName: %s] Registered new vendor.", v->_name.c_str());
|
2021-11-20 09:26:50 +00:00
|
|
|
|
2022-05-14 04:17:17 +00:00
|
|
|
calldata_set_ptr(cd, "vendor", static_cast<void *>(v));
|
2021-11-20 09:26:50 +00:00
|
|
|
|
|
|
|
RETURN_SUCCESS();
|
|
|
|
}
|
|
|
|
|
2021-11-21 11:08:06 +00:00
|
|
|
void WebSocketApi::vendor_request_register_cb(void *, calldata_t *cd)
|
2021-11-20 09:26:50 +00:00
|
|
|
{
|
|
|
|
Vendor *v = get_vendor(cd);
|
|
|
|
if (!v)
|
|
|
|
RETURN_FAILURE();
|
|
|
|
|
|
|
|
const char *requestType;
|
2022-06-08 04:35:10 +00:00
|
|
|
if (!calldata_get_string(cd, "type", &requestType) || strlen(requestType) == 0) {
|
2022-05-14 04:17:17 +00:00
|
|
|
blog(LOG_WARNING,
|
|
|
|
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing or empty `type` string.",
|
|
|
|
v->_name.c_str());
|
2021-11-20 09:26:50 +00:00
|
|
|
RETURN_FAILURE();
|
|
|
|
}
|
|
|
|
|
|
|
|
void *voidCallback;
|
|
|
|
if (!calldata_get_ptr(cd, "callback", &voidCallback) || !voidCallback) {
|
2022-05-14 04:17:17 +00:00
|
|
|
blog(LOG_WARNING,
|
|
|
|
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing `callback` pointer.",
|
|
|
|
v->_name.c_str());
|
2021-11-20 09:26:50 +00:00
|
|
|
RETURN_FAILURE();
|
|
|
|
}
|
|
|
|
|
2022-05-14 04:17:17 +00:00
|
|
|
auto cb = static_cast<obs_websocket_request_callback *>(voidCallback);
|
2021-11-20 09:26:50 +00:00
|
|
|
|
|
|
|
std::unique_lock l(v->_mutex);
|
|
|
|
|
|
|
|
if (v->_requests.count(requestType)) {
|
2022-05-14 04:17:17 +00:00
|
|
|
blog(LOG_WARNING,
|
|
|
|
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is already a registered request.",
|
|
|
|
v->_name.c_str(), requestType);
|
2021-11-20 09:26:50 +00:00
|
|
|
RETURN_FAILURE();
|
|
|
|
}
|
|
|
|
|
|
|
|
v->_requests[requestType] = *cb;
|
|
|
|
|
2022-06-08 04:35:10 +00:00
|
|
|
blog_debug("[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Registered new vendor request: %s",
|
|
|
|
v->_name.c_str(), requestType);
|
2021-11-20 09:26:50 +00:00
|
|
|
|
|
|
|
RETURN_SUCCESS();
|
|
|
|
}
|
|
|
|
|
2021-11-21 11:08:06 +00:00
|
|
|
void WebSocketApi::vendor_request_unregister_cb(void *, calldata_t *cd)
|
2021-11-20 09:26:50 +00:00
|
|
|
{
|
|
|
|
Vendor *v = get_vendor(cd);
|
|
|
|
if (!v)
|
|
|
|
RETURN_FAILURE();
|
|
|
|
|
|
|
|
const char *requestType;
|
2022-06-08 04:35:10 +00:00
|
|
|
if (!calldata_get_string(cd, "type", &requestType) || strlen(requestType) == 0) {
|
2022-05-14 04:17:17 +00:00
|
|
|
blog(LOG_WARNING,
|
|
|
|
"[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Failed due to missing `type` string.",
|
|
|
|
v->_name.c_str());
|
2021-11-20 09:26:50 +00:00
|
|
|
RETURN_FAILURE();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_lock l(v->_mutex);
|
|
|
|
|
|
|
|
if (!v->_requests.count(requestType)) {
|
2022-05-14 04:17:17 +00:00
|
|
|
blog(LOG_WARNING,
|
|
|
|
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is not a registered request.",
|
|
|
|
v->_name.c_str(), requestType);
|
2021-11-20 09:26:50 +00:00
|
|
|
RETURN_FAILURE();
|
|
|
|
}
|
|
|
|
|
|
|
|
v->_requests.erase(requestType);
|
|
|
|
|
2022-06-08 04:35:10 +00:00
|
|
|
blog_debug("[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Unregistered vendor request: %s",
|
|
|
|
v->_name.c_str(), requestType);
|
2021-11-20 09:26:50 +00:00
|
|
|
|
|
|
|
RETURN_SUCCESS();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebSocketApi::vendor_event_emit_cb(void *priv_data, calldata_t *cd)
|
|
|
|
{
|
2022-05-14 04:17:17 +00:00
|
|
|
auto c = static_cast<WebSocketApi *>(priv_data);
|
2021-11-20 09:26:50 +00:00
|
|
|
|
|
|
|
Vendor *v = get_vendor(cd);
|
|
|
|
if (!v)
|
|
|
|
RETURN_FAILURE();
|
|
|
|
|
|
|
|
const char *eventType;
|
2022-06-08 04:35:10 +00:00
|
|
|
if (!calldata_get_string(cd, "type", &eventType) || strlen(eventType) == 0) {
|
|
|
|
blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `type` string.",
|
2022-05-14 04:17:17 +00:00
|
|
|
v->_name.c_str());
|
2021-11-20 09:26:50 +00:00
|
|
|
RETURN_FAILURE();
|
|
|
|
}
|
|
|
|
|
|
|
|
void *voidEventData;
|
|
|
|
if (!calldata_get_ptr(cd, "data", &voidEventData)) {
|
2022-06-08 04:35:10 +00:00
|
|
|
blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `data` pointer.",
|
2022-05-14 04:17:17 +00:00
|
|
|
v->_name.c_str());
|
2021-11-20 09:26:50 +00:00
|
|
|
RETURN_FAILURE();
|
|
|
|
}
|
|
|
|
|
2022-05-14 04:17:17 +00:00
|
|
|
auto eventData = static_cast<obs_data_t *>(voidEventData);
|
2021-11-20 09:26:50 +00:00
|
|
|
|
2021-12-22 01:52:46 +00:00
|
|
|
if (!c->_eventCallback)
|
|
|
|
RETURN_FAILURE();
|
|
|
|
|
2021-11-20 09:26:50 +00:00
|
|
|
c->_eventCallback(v->_name, eventType, eventData);
|
|
|
|
|
|
|
|
RETURN_SUCCESS();
|
|
|
|
}
|