mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
Merge pull request #665 from Palakis/feature/batch-requests
Batch Requests
This commit is contained in:
commit
80d82861ae
@ -46,6 +46,8 @@ const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap{
|
||||
{ "TriggerHotkeyByName", &WSRequestHandler::TriggerHotkeyByName },
|
||||
{ "TriggerHotkeyBySequence", &WSRequestHandler::TriggerHotkeyBySequence },
|
||||
|
||||
{ "ExecuteBatch", &WSRequestHandler::ExecuteBatch },
|
||||
|
||||
{ "SetCurrentScene", &WSRequestHandler::SetCurrentScene },
|
||||
{ "GetCurrentScene", &WSRequestHandler::GetCurrentScene },
|
||||
{ "GetSceneList", &WSRequestHandler::GetSceneList },
|
||||
@ -190,7 +192,7 @@ WSRequestHandler::WSRequestHandler(ConnectionProperties& connProperties) :
|
||||
{
|
||||
}
|
||||
|
||||
RpcResponse WSRequestHandler::processRequest(const RpcRequest& request){
|
||||
RpcResponse WSRequestHandler::processRequest(const RpcRequest& request) {
|
||||
if (GetConfig()->AuthRequired
|
||||
&& (!authNotRequired.contains(request.methodName()))
|
||||
&& (!_connProperties.isAuthenticated()))
|
||||
|
@ -64,6 +64,8 @@ class WSRequestHandler {
|
||||
RpcResponse TriggerHotkeyByName(const RpcRequest&);
|
||||
RpcResponse TriggerHotkeyBySequence(const RpcRequest&);
|
||||
|
||||
RpcResponse ExecuteBatch(const RpcRequest&);
|
||||
|
||||
RpcResponse SetCurrentScene(const RpcRequest&);
|
||||
RpcResponse GetCurrentScene(const RpcRequest&);
|
||||
RpcResponse GetSceneList(const RpcRequest&);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "Config.h"
|
||||
#include "Utils.h"
|
||||
#include "WSEvents.h"
|
||||
#include "protocol/OBSRemoteProtocol.h"
|
||||
|
||||
#define CASE(x) case x: return #x;
|
||||
const char *describe_output_format(int format) {
|
||||
@ -415,3 +416,60 @@ RpcResponse WSRequestHandler::TriggerHotkeyBySequence(const RpcRequest& request)
|
||||
|
||||
return request.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a list of requests sequentially (one-by-one on the same thread).
|
||||
*
|
||||
* @param {Array<Object>} `requests` Array of requests to perform. Executed in order.
|
||||
* @param {String} `requests.*.request-type` Request type. Eg. `GetVersion`.
|
||||
* @param {String (Optional)} `requests.*.message-id` ID of the individual request. Can be any string and not required to be unique. Defaults to empty string if not specified.
|
||||
* @param {boolean (Optional)} `abortOnFail` Stop processing batch requests if one returns a failure.
|
||||
*
|
||||
* @return {Array<Object>} `results` Batch requests results, ordered sequentially.
|
||||
* @return {String} `results.*.message-id` ID of the individual request which was originally provided by the client.
|
||||
* @return {String} `results.*.status` Status response as string. Either `ok` or `error`.
|
||||
* @return {String (Optional)} `results.*.error` Error message accompanying an `error` status.
|
||||
*
|
||||
* @api requests
|
||||
* @name ExecuteBatch
|
||||
* @category general
|
||||
* @since 4.9.0
|
||||
*/
|
||||
RpcResponse WSRequestHandler::ExecuteBatch(const RpcRequest& request) {
|
||||
if (!request.hasField("requests")) {
|
||||
return request.failed("missing request parameters");
|
||||
}
|
||||
|
||||
bool abortOnFail = obs_data_get_bool(request.parameters(), "abortOnFail");
|
||||
|
||||
OBSDataArrayAutoRelease results = obs_data_array_create();
|
||||
|
||||
OBSDataArrayAutoRelease requests = obs_data_get_array(request.parameters(), "requests");
|
||||
size_t requestsCount = obs_data_array_count(requests);
|
||||
for (size_t i = 0; i < requestsCount; i++) {
|
||||
OBSDataAutoRelease requestData = obs_data_array_item(requests, i);
|
||||
QString messageId = obs_data_get_string(requestData, "message-id");
|
||||
QString methodName = obs_data_get_string(requestData, "request-type");
|
||||
obs_data_unset_user_value(requestData, "request-type");
|
||||
obs_data_unset_user_value(requestData, "message-id");
|
||||
|
||||
// build RpcRequest from json data object
|
||||
RpcRequest subRequest(messageId, methodName, requestData);
|
||||
|
||||
// execute the request
|
||||
RpcResponse subResponse = processRequest(subRequest);
|
||||
|
||||
// transform response into json data
|
||||
OBSDataAutoRelease subResponseData = OBSRemoteProtocol::rpcResponseToJsonData(subResponse);
|
||||
|
||||
obs_data_array_push_back(results, subResponseData);
|
||||
|
||||
// if told to abort on fail and a failure occurs, stop request processing and return the progress
|
||||
if (abortOnFail && (subResponse.status() == RpcResponse::Status::Error))
|
||||
break;
|
||||
}
|
||||
|
||||
OBSDataAutoRelease response = obs_data_create();
|
||||
obs_data_set_array(response, "results", results);
|
||||
return request.success(response);
|
||||
}
|
||||
|
@ -133,8 +133,7 @@ void WSServer::stop()
|
||||
|
||||
void WSServer::broadcast(const RpcEvent& event)
|
||||
{
|
||||
OBSRemoteProtocol protocol;
|
||||
std::string message = protocol.encodeEvent(event);
|
||||
std::string message = OBSRemoteProtocol::encodeEvent(event);
|
||||
|
||||
if (GetConfig()->DebugEnabled) {
|
||||
blog(LOG_INFO, "Update << '%s'", message.c_str());
|
||||
@ -190,8 +189,7 @@ void WSServer::onMessage(connection_hdl hdl, server::message_ptr message)
|
||||
}
|
||||
|
||||
WSRequestHandler requestHandler(connProperties);
|
||||
OBSRemoteProtocol protocol;
|
||||
std::string response = protocol.processMessage(requestHandler, payload);
|
||||
std::string response = OBSRemoteProtocol::processMessage(requestHandler, payload);
|
||||
|
||||
if (GetConfig()->DebugEnabled) {
|
||||
blog(LOG_INFO, "Response << '%s'", response.c_str());
|
||||
|
@ -31,11 +31,15 @@ std::string OBSRemoteProtocol::processMessage(WSRequestHandler& requestHandler,
|
||||
OBSDataAutoRelease data = obs_data_create_from_json(msg);
|
||||
if (!data) {
|
||||
blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg);
|
||||
return errorResponse(QString::Null(), "invalid JSON payload");
|
||||
return jsonDataToString(
|
||||
errorResponse(nullptr, "invalid JSON payload")
|
||||
);
|
||||
}
|
||||
|
||||
if (!obs_data_has_user_value(data, "request-type") || !obs_data_has_user_value(data, "message-id")) {
|
||||
return errorResponse(QString::Null(), "missing request parameters");
|
||||
return jsonDataToString(
|
||||
errorResponse(nullptr, "missing request parameters")
|
||||
);
|
||||
}
|
||||
|
||||
QString methodName = obs_data_get_string(data, "request-type");
|
||||
@ -49,15 +53,8 @@ std::string OBSRemoteProtocol::processMessage(WSRequestHandler& requestHandler,
|
||||
RpcRequest request(messageId, methodName, params);
|
||||
RpcResponse response = requestHandler.processRequest(request);
|
||||
|
||||
OBSData additionalFields = response.additionalFields();
|
||||
switch (response.status()) {
|
||||
case RpcResponse::Status::Ok:
|
||||
return successResponse(messageId, additionalFields);
|
||||
case RpcResponse::Status::Error:
|
||||
return errorResponse(messageId, response.errorMessage(), additionalFields);
|
||||
}
|
||||
|
||||
return std::string();
|
||||
OBSDataAutoRelease responseData = rpcResponseToJsonData(response);
|
||||
return jsonDataToString(responseData);
|
||||
}
|
||||
|
||||
std::string OBSRemoteProtocol::encodeEvent(const RpcEvent& event)
|
||||
@ -87,33 +84,56 @@ std::string OBSRemoteProtocol::encodeEvent(const RpcEvent& event)
|
||||
return std::string(obs_data_get_json(eventData));
|
||||
}
|
||||
|
||||
std::string OBSRemoteProtocol::buildResponse(QString messageId, QString status, obs_data_t* fields)
|
||||
obs_data_t* OBSRemoteProtocol::rpcResponseToJsonData(const RpcResponse& response)
|
||||
{
|
||||
OBSDataAutoRelease response = obs_data_create();
|
||||
if (!messageId.isNull()) {
|
||||
obs_data_set_string(response, "message-id", messageId.toUtf8().constData());
|
||||
}
|
||||
obs_data_set_string(response, "status", status.toUtf8().constData());
|
||||
QByteArray messageIdBytes = response.messageId().toUtf8();
|
||||
const char* messageId = messageIdBytes.constData();
|
||||
|
||||
if (fields) {
|
||||
obs_data_apply(response, fields);
|
||||
OBSData additionalFields = response.additionalFields();
|
||||
switch (response.status()) {
|
||||
case RpcResponse::Status::Ok:
|
||||
return successResponse(messageId, additionalFields);
|
||||
case RpcResponse::Status::Error:
|
||||
return errorResponse(messageId, response.errorMessage().toUtf8().constData(), additionalFields);
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
std::string responseString = obs_data_get_json(response);
|
||||
return responseString;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string OBSRemoteProtocol::successResponse(QString messageId, obs_data_t* fields)
|
||||
obs_data_t* OBSRemoteProtocol::successResponse(const char* messageId, obs_data_t* fields)
|
||||
{
|
||||
return buildResponse(messageId, "ok", fields);
|
||||
}
|
||||
|
||||
std::string OBSRemoteProtocol::errorResponse(QString messageId, QString errorMessage, obs_data_t* additionalFields)
|
||||
obs_data_t* OBSRemoteProtocol::errorResponse(const char* messageId, const char* errorMessage, obs_data_t* additionalFields)
|
||||
{
|
||||
OBSDataAutoRelease fields = obs_data_create();
|
||||
if (additionalFields) {
|
||||
obs_data_apply(fields, additionalFields);
|
||||
}
|
||||
obs_data_set_string(fields, "error", errorMessage.toUtf8().constData());
|
||||
obs_data_set_string(fields, "error", errorMessage);
|
||||
return buildResponse(messageId, "error", fields);
|
||||
}
|
||||
|
||||
obs_data_t* OBSRemoteProtocol::buildResponse(const char* messageId, const char* status, obs_data_t* fields)
|
||||
{
|
||||
obs_data_t* response = obs_data_create();
|
||||
if (messageId) {
|
||||
obs_data_set_string(response, "message-id", messageId);
|
||||
}
|
||||
obs_data_set_string(response, "status", status);
|
||||
|
||||
if (fields) {
|
||||
obs_data_apply(response, fields);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
std::string OBSRemoteProtocol::jsonDataToString(OBSDataAutoRelease data)
|
||||
{
|
||||
std::string responseString = obs_data_get_json(data);
|
||||
return responseString;
|
||||
}
|
||||
|
@ -20,7 +20,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
|
||||
#include <string>
|
||||
#include <obs-data.h>
|
||||
#include <QtCore/QString>
|
||||
|
||||
#include "../rpc/RpcResponse.h"
|
||||
|
||||
class WSRequestHandler;
|
||||
class RpcEvent;
|
||||
@ -28,11 +29,13 @@ class RpcEvent;
|
||||
class OBSRemoteProtocol
|
||||
{
|
||||
public:
|
||||
std::string processMessage(WSRequestHandler& requestHandler, std::string message);
|
||||
std::string encodeEvent(const RpcEvent& event);
|
||||
static std::string processMessage(WSRequestHandler& requestHandler, std::string message);
|
||||
static std::string encodeEvent(const RpcEvent& event);
|
||||
static obs_data_t* rpcResponseToJsonData(const RpcResponse& response);
|
||||
|
||||
private:
|
||||
std::string buildResponse(QString messageId, QString status, obs_data_t* fields = nullptr);
|
||||
std::string successResponse(QString messageId, obs_data_t* fields = nullptr);
|
||||
std::string errorResponse(QString messageId, QString errorMessage, obs_data_t* additionalFields = nullptr);
|
||||
static obs_data_t* successResponse(const char* messageId, obs_data_t* fields = nullptr);
|
||||
static obs_data_t* errorResponse(const char* messageId, const char* errorMessage, obs_data_t* additionalFields = nullptr);
|
||||
static obs_data_t* buildResponse(const char* messageId, const char*, obs_data_t* fields = nullptr);
|
||||
static std::string jsonDataToString(OBSDataAutoRelease data);
|
||||
};
|
||||
|
@ -36,7 +36,7 @@ public:
|
||||
obs_data_t* additionalFields = nullptr
|
||||
);
|
||||
|
||||
Status status() {
|
||||
const Status status() const {
|
||||
return _status;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user