Merge pull request #390 from Palakis/handler-protocol-refactor

Request handler refactor
This commit is contained in:
Stéphane Lepin 2020-01-29 14:06:46 +01:00 committed by GitHub
commit e65958f33e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1441 additions and 1023 deletions

View File

@ -46,6 +46,10 @@ set(obs-websocket_SOURCES
src/WSEvents.cpp src/WSEvents.cpp
src/Config.cpp src/Config.cpp
src/Utils.cpp src/Utils.cpp
src/rpc/RpcRequest.cpp
src/rpc/RpcResponse.cpp
src/rpc/RpcEvent.cpp
src/protocol/OBSRemoteProtocol.cpp
src/forms/settings-dialog.cpp) src/forms/settings-dialog.cpp)
set(obs-websocket_HEADERS set(obs-websocket_HEADERS
@ -56,6 +60,10 @@ set(obs-websocket_HEADERS
src/WSEvents.h src/WSEvents.h
src/Config.h src/Config.h
src/Utils.h src/Utils.h
src/rpc/RpcRequest.h
src/rpc/RpcResponse.h
src/rpc/RpcEvent.h
src/protocol/OBSRemoteProtocol.h
src/forms/settings-dialog.h) src/forms/settings-dialog.h)
# --- Platform-independent build settings --- # --- Platform-independent build settings ---
@ -174,8 +182,8 @@ if(UNIX AND NOT APPLE)
install(TARGETS obs-websocket install(TARGETS obs-websocket
LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/obs-plugins") LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/obs-plugins")
# Dirty fix for Ubuntu # Dirty fix for Ubuntu
install(TARGETS obs-websocket install(TARGETS obs-websocket
LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/${UNAME_MACHINE}-linux-gnu/obs-plugins") LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/${UNAME_MACHINE}-linux-gnu/obs-plugins")
install(FILES ${locale_files} install(FILES ${locale_files}
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/obs/obs-plugins/obs-websocket/locale") DESTINATION "${CMAKE_INSTALL_PREFIX}/share/obs/obs-plugins/obs-websocket/locale")

View File

@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/> with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
#include <inttypes.h>
#include <QtWidgets/QMainWindow> #include <QtWidgets/QMainWindow>
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtCore/QUrl> #include <QtCore/QUrl>
@ -822,3 +823,17 @@ void Utils::PauseRecording(bool pause)
pauseRecording(pause); pauseRecording(pause);
} }
QString Utils::nsToTimestamp(uint64_t ns)
{
uint64_t ms = ns / 1000000ULL;
uint64_t secs = ms / 1000ULL;
uint64_t minutes = secs / 60ULL;
uint64_t hoursPart = minutes / 60ULL;
uint64_t minutesPart = minutes % 60ULL;
uint64_t secsPart = secs % 60ULL;
uint64_t msPart = ms % 1000ULL;
return QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
}

View File

@ -86,4 +86,6 @@ class Utils {
static bool RecordingPauseSupported(); static bool RecordingPauseSupported();
static bool RecordingPaused(); static bool RecordingPaused();
static void PauseRecording(bool pause); static void PauseRecording(bool pause);
static QString nsToTimestamp(uint64_t ns);
}; };

View File

@ -23,27 +23,15 @@
#include <QtWidgets/QPushButton> #include <QtWidgets/QPushButton>
#include "Config.h"
#include "Utils.h"
#include "WSEvents.h" #include "WSEvents.h"
#include "obs-websocket.h" #include "obs-websocket.h"
#include "Config.h"
#include "Utils.h"
#include "rpc/RpcEvent.h"
#define STATUS_INTERVAL 2000 #define STATUS_INTERVAL 2000
QString nsToTimestamp(uint64_t ns) {
uint64_t ms = ns / 1000000ULL;
uint64_t secs = ms / 1000ULL;
uint64_t minutes = secs / 60ULL;
uint64_t hoursPart = minutes / 60ULL;
uint64_t minutesPart = minutes % 60ULL;
uint64_t secsPart = secs % 60ULL;
uint64_t msPart = ms % 1000ULL;
return QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
}
const char* sourceTypeToString(obs_source_type type) { const char* sourceTypeToString(obs_source_type type) {
switch (type) { switch (type) {
case OBS_SOURCE_TYPE_INPUT: case OBS_SOURCE_TYPE_INPUT:
@ -252,28 +240,11 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private
void WSEvents::broadcastUpdate(const char* updateType, void WSEvents::broadcastUpdate(const char* updateType,
obs_data_t* additionalFields = nullptr) obs_data_t* additionalFields = nullptr)
{ {
OBSDataAutoRelease update = obs_data_create(); uint64_t streamTime = getStreamingTime();
obs_data_set_string(update, "update-type", updateType); uint64_t recordingTime = getStreamingTime();
RpcEvent event(QString(updateType), streamTime, recordingTime, additionalFields);
if (obs_frontend_streaming_active()) { _srv->broadcast(event);
QString streamingTimecode = getStreamingTimecode();
obs_data_set_string(update, "stream-timecode", streamingTimecode.toUtf8().constData());
}
if (obs_frontend_recording_active()) {
QString recordingTimecode = getRecordingTimecode();
obs_data_set_string(update, "rec-timecode", recordingTimecode.toUtf8().constData());
}
if (additionalFields)
obs_data_apply(update, additionalFields);
QString json = obs_data_get_json(update);
_srv->broadcast(json.toStdString());
if (GetConfig()->DebugEnabled) {
blog(LOG_INFO, "Update << '%s'", json.toUtf8().constData());
}
} }
void WSEvents::connectSourceSignals(obs_source_t* source) { void WSEvents::connectSourceSignals(obs_source_t* source) {
@ -410,11 +381,11 @@ uint64_t WSEvents::getRecordingTime() {
} }
QString WSEvents::getStreamingTimecode() { QString WSEvents::getStreamingTimecode() {
return nsToTimestamp(getStreamingTime()); return Utils::nsToTimestamp(getStreamingTime());
} }
QString WSEvents::getRecordingTimecode() { QString WSEvents::getRecordingTimecode() {
return nsToTimestamp(getRecordingTime()); return Utils::nsToTimestamp(getRecordingTime());
} }
/** /**

View File

@ -17,6 +17,8 @@
* with this program. If not, see <https://www.gnu.org/licenses/> * with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
#include <functional>
#include <obs-data.h> #include <obs-data.h>
#include "Config.h" #include "Config.h"
@ -24,265 +26,148 @@
#include "WSRequestHandler.h" #include "WSRequestHandler.h"
QHash<QString, HandlerResponse(*)(WSRequestHandler*)> WSRequestHandler::messageMap{ using namespace std::placeholders;
{ "GetVersion", WSRequestHandler::HandleGetVersion },
{ "GetAuthRequired", WSRequestHandler::HandleGetAuthRequired },
{ "Authenticate", WSRequestHandler::HandleAuthenticate },
{ "GetStats", WSRequestHandler::HandleGetStats }, const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap {
{ "SetHeartbeat", WSRequestHandler::HandleSetHeartbeat }, { "GetVersion", &WSRequestHandler::GetVersion },
{ "GetVideoInfo", WSRequestHandler::HandleGetVideoInfo }, { "GetAuthRequired", &WSRequestHandler::GetAuthRequired },
{ "Authenticate", &WSRequestHandler::Authenticate },
{ "SetFilenameFormatting", WSRequestHandler::HandleSetFilenameFormatting }, { "GetStats", &WSRequestHandler::GetStats },
{ "GetFilenameFormatting", WSRequestHandler::HandleGetFilenameFormatting }, { "SetHeartbeat", &WSRequestHandler::SetHeartbeat },
{ "GetVideoInfo", &WSRequestHandler::GetVideoInfo },
{ "BroadcastCustomMessage", WSRequestHandler::HandleBroadcastCustomMessage }, { "SetFilenameFormatting", &WSRequestHandler::SetFilenameFormatting },
{ "GetFilenameFormatting", &WSRequestHandler::GetFilenameFormatting },
{ "SetCurrentScene", WSRequestHandler::HandleSetCurrentScene }, { "BroadcastCustomMessage", &WSRequestHandler::BroadcastCustomMessage },
{ "GetCurrentScene", WSRequestHandler::HandleGetCurrentScene },
{ "GetSceneList", WSRequestHandler::HandleGetSceneList },
{ "SetSourceRender", WSRequestHandler::HandleSetSceneItemRender }, // Retrocompat { "SetCurrentScene", &WSRequestHandler::SetCurrentScene },
{ "SetSceneItemRender", WSRequestHandler::HandleSetSceneItemRender }, { "GetCurrentScene", &WSRequestHandler::GetCurrentScene },
{ "SetSceneItemPosition", WSRequestHandler::HandleSetSceneItemPosition }, { "GetSceneList", &WSRequestHandler::GetSceneList },
{ "SetSceneItemTransform", WSRequestHandler::HandleSetSceneItemTransform },
{ "SetSceneItemCrop", WSRequestHandler::HandleSetSceneItemCrop },
{ "GetSceneItemProperties", WSRequestHandler::HandleGetSceneItemProperties },
{ "SetSceneItemProperties", WSRequestHandler::HandleSetSceneItemProperties },
{ "ResetSceneItem", WSRequestHandler::HandleResetSceneItem },
{ "DeleteSceneItem", WSRequestHandler::HandleDeleteSceneItem },
{ "DuplicateSceneItem", WSRequestHandler::HandleDuplicateSceneItem },
{ "ReorderSceneItems", WSRequestHandler::HandleReorderSceneItems },
{ "GetStreamingStatus", WSRequestHandler::HandleGetStreamingStatus }, { "SetSourceRender", &WSRequestHandler::SetSceneItemRender }, // Retrocompat
{ "StartStopStreaming", WSRequestHandler::HandleStartStopStreaming }, { "SetSceneItemRender", &WSRequestHandler::SetSceneItemRender },
{ "StartStopRecording", WSRequestHandler::HandleStartStopRecording }, { "SetSceneItemPosition", &WSRequestHandler::SetSceneItemPosition },
{ "SetSceneItemTransform", &WSRequestHandler::SetSceneItemTransform },
{ "SetSceneItemCrop", &WSRequestHandler::SetSceneItemCrop },
{ "GetSceneItemProperties", &WSRequestHandler::GetSceneItemProperties },
{ "SetSceneItemProperties", &WSRequestHandler::SetSceneItemProperties },
{ "ResetSceneItem", &WSRequestHandler::ResetSceneItem },
{ "DeleteSceneItem", &WSRequestHandler::DeleteSceneItem },
{ "DuplicateSceneItem", &WSRequestHandler::DuplicateSceneItem },
{ "ReorderSceneItems", &WSRequestHandler::ReorderSceneItems },
{ "StartStreaming", WSRequestHandler::HandleStartStreaming }, { "GetStreamingStatus", &WSRequestHandler::GetStreamingStatus },
{ "StopStreaming", WSRequestHandler::HandleStopStreaming }, { "StartStopStreaming", &WSRequestHandler::StartStopStreaming },
{ "StartStopRecording", &WSRequestHandler::StartStopRecording },
{ "StartRecording", WSRequestHandler::HandleStartRecording }, { "StartStreaming", &WSRequestHandler::StartStreaming },
{ "StopRecording", WSRequestHandler::HandleStopRecording }, { "StopStreaming", &WSRequestHandler::StopStreaming },
{ "PauseRecording", WSRequestHandler::HandlePauseRecording },
{ "ResumeRecording", WSRequestHandler::HandleResumeRecording },
{ "StartStopReplayBuffer", WSRequestHandler::HandleStartStopReplayBuffer }, { "StartRecording", &WSRequestHandler::StartRecording },
{ "StartReplayBuffer", WSRequestHandler::HandleStartReplayBuffer }, { "StopRecording", &WSRequestHandler::StopRecording },
{ "StopReplayBuffer", WSRequestHandler::HandleStopReplayBuffer }, { "PauseRecording", &WSRequestHandler::PauseRecording },
{ "SaveReplayBuffer", WSRequestHandler::HandleSaveReplayBuffer }, { "ResumeRecording", &WSRequestHandler::ResumeRecording },
{ "SetRecordingFolder", WSRequestHandler::HandleSetRecordingFolder }, { "StartStopReplayBuffer", &WSRequestHandler::StartStopReplayBuffer },
{ "GetRecordingFolder", WSRequestHandler::HandleGetRecordingFolder }, { "StartReplayBuffer", &WSRequestHandler::StartReplayBuffer },
{ "StopReplayBuffer", &WSRequestHandler::StopReplayBuffer },
{ "SaveReplayBuffer", &WSRequestHandler::SaveReplayBuffer },
{ "GetTransitionList", WSRequestHandler::HandleGetTransitionList }, { "SetRecordingFolder", &WSRequestHandler::SetRecordingFolder },
{ "GetCurrentTransition", WSRequestHandler::HandleGetCurrentTransition }, { "GetRecordingFolder", &WSRequestHandler::GetRecordingFolder },
{ "SetCurrentTransition", WSRequestHandler::HandleSetCurrentTransition },
{ "SetTransitionDuration", WSRequestHandler::HandleSetTransitionDuration },
{ "GetTransitionDuration", WSRequestHandler::HandleGetTransitionDuration },
{ "SetVolume", WSRequestHandler::HandleSetVolume }, { "GetTransitionList", &WSRequestHandler::GetTransitionList },
{ "GetVolume", WSRequestHandler::HandleGetVolume }, { "GetCurrentTransition", &WSRequestHandler::GetCurrentTransition },
{ "ToggleMute", WSRequestHandler::HandleToggleMute }, { "SetCurrentTransition", &WSRequestHandler::SetCurrentTransition },
{ "SetMute", WSRequestHandler::HandleSetMute }, { "SetTransitionDuration", &WSRequestHandler::SetTransitionDuration },
{ "GetMute", WSRequestHandler::HandleGetMute }, { "GetTransitionDuration", &WSRequestHandler::GetTransitionDuration },
{ "SetSyncOffset", WSRequestHandler::HandleSetSyncOffset },
{ "GetSyncOffset", WSRequestHandler::HandleGetSyncOffset },
{ "GetSpecialSources", WSRequestHandler::HandleGetSpecialSources },
{ "GetSourcesList", WSRequestHandler::HandleGetSourcesList },
{ "GetSourceTypesList", WSRequestHandler::HandleGetSourceTypesList },
{ "GetSourceSettings", WSRequestHandler::HandleGetSourceSettings },
{ "SetSourceSettings", WSRequestHandler::HandleSetSourceSettings },
{ "TakeSourceScreenshot", WSRequestHandler::HandleTakeSourceScreenshot },
{ "GetSourceFilters", WSRequestHandler::HandleGetSourceFilters }, { "SetVolume", &WSRequestHandler::SetVolume },
{ "GetSourceFilterInfo", WSRequestHandler::HandleGetSourceFilterInfo }, { "GetVolume", &WSRequestHandler::GetVolume },
{ "AddFilterToSource", WSRequestHandler::HandleAddFilterToSource }, { "ToggleMute", &WSRequestHandler::ToggleMute },
{ "RemoveFilterFromSource", WSRequestHandler::HandleRemoveFilterFromSource }, { "SetMute", &WSRequestHandler::SetMute },
{ "ReorderSourceFilter", WSRequestHandler::HandleReorderSourceFilter }, { "GetMute", &WSRequestHandler::GetMute },
{ "MoveSourceFilter", WSRequestHandler::HandleMoveSourceFilter }, { "SetSyncOffset", &WSRequestHandler::SetSyncOffset },
{ "SetSourceFilterSettings", WSRequestHandler::HandleSetSourceFilterSettings }, { "GetSyncOffset", &WSRequestHandler::GetSyncOffset },
{ "SetSourceFilterVisibility", WSRequestHandler::HandleSetSourceFilterVisibility }, { "GetSpecialSources", &WSRequestHandler::GetSpecialSources },
{ "GetSourcesList", &WSRequestHandler::GetSourcesList },
{ "GetSourceTypesList", &WSRequestHandler::GetSourceTypesList },
{ "GetSourceSettings", &WSRequestHandler::GetSourceSettings },
{ "SetSourceSettings", &WSRequestHandler::SetSourceSettings },
{ "TakeSourceScreenshot", &WSRequestHandler::TakeSourceScreenshot },
{ "SetCurrentSceneCollection", WSRequestHandler::HandleSetCurrentSceneCollection }, { "GetSourceFilters", &WSRequestHandler::GetSourceFilters },
{ "GetCurrentSceneCollection", WSRequestHandler::HandleGetCurrentSceneCollection }, { "GetSourceFilterInfo", &WSRequestHandler::GetSourceFilterInfo },
{ "ListSceneCollections", WSRequestHandler::HandleListSceneCollections }, { "AddFilterToSource", &WSRequestHandler::AddFilterToSource },
{ "RemoveFilterFromSource", &WSRequestHandler::RemoveFilterFromSource },
{ "ReorderSourceFilter", &WSRequestHandler::ReorderSourceFilter },
{ "MoveSourceFilter", &WSRequestHandler::MoveSourceFilter },
{ "SetSourceFilterSettings", &WSRequestHandler::SetSourceFilterSettings },
{ "SetSourceFilterVisibility", &WSRequestHandler::SetSourceFilterVisibility },
{ "SetCurrentProfile", WSRequestHandler::HandleSetCurrentProfile }, { "SetCurrentSceneCollection", &WSRequestHandler::SetCurrentSceneCollection },
{ "GetCurrentProfile", WSRequestHandler::HandleGetCurrentProfile }, { "GetCurrentSceneCollection", &WSRequestHandler::GetCurrentSceneCollection },
{ "ListProfiles", WSRequestHandler::HandleListProfiles }, { "ListSceneCollections", &WSRequestHandler::ListSceneCollections },
{ "SetStreamSettings", WSRequestHandler::HandleSetStreamSettings }, { "SetCurrentProfile", &WSRequestHandler::SetCurrentProfile },
{ "GetStreamSettings", WSRequestHandler::HandleGetStreamSettings }, { "GetCurrentProfile", &WSRequestHandler::GetCurrentProfile },
{ "SaveStreamSettings", WSRequestHandler::HandleSaveStreamSettings }, { "ListProfiles", &WSRequestHandler::ListProfiles },
{ "SetStreamSettings", &WSRequestHandler::SetStreamSettings },
{ "GetStreamSettings", &WSRequestHandler::GetStreamSettings },
{ "SaveStreamSettings", &WSRequestHandler::SaveStreamSettings },
#if BUILD_CAPTIONS #if BUILD_CAPTIONS
{ "SendCaptions", WSRequestHandler::HandleSendCaptions }, { "SendCaptions", &WSRequestHandler::SendCaptions },
#endif #endif
{ "GetStudioModeStatus", WSRequestHandler::HandleGetStudioModeStatus }, { "GetStudioModeStatus", &WSRequestHandler::GetStudioModeStatus },
{ "GetPreviewScene", WSRequestHandler::HandleGetPreviewScene }, { "GetPreviewScene", &WSRequestHandler::GetPreviewScene },
{ "SetPreviewScene", WSRequestHandler::HandleSetPreviewScene }, { "SetPreviewScene", &WSRequestHandler::SetPreviewScene },
{ "TransitionToProgram", WSRequestHandler::HandleTransitionToProgram }, { "TransitionToProgram", &WSRequestHandler::TransitionToProgram },
{ "EnableStudioMode", WSRequestHandler::HandleEnableStudioMode }, { "EnableStudioMode", &WSRequestHandler::EnableStudioMode },
{ "DisableStudioMode", WSRequestHandler::HandleDisableStudioMode }, { "DisableStudioMode", &WSRequestHandler::DisableStudioMode },
{ "ToggleStudioMode", WSRequestHandler::HandleToggleStudioMode }, { "ToggleStudioMode", &WSRequestHandler::ToggleStudioMode },
{ "SetTextGDIPlusProperties", WSRequestHandler::HandleSetTextGDIPlusProperties }, { "SetTextGDIPlusProperties", &WSRequestHandler::SetTextGDIPlusProperties },
{ "GetTextGDIPlusProperties", WSRequestHandler::HandleGetTextGDIPlusProperties }, { "GetTextGDIPlusProperties", &WSRequestHandler::GetTextGDIPlusProperties },
{ "SetTextFreetype2Properties", WSRequestHandler::HandleSetTextFreetype2Properties }, { "SetTextFreetype2Properties", &WSRequestHandler::SetTextFreetype2Properties },
{ "GetTextFreetype2Properties", WSRequestHandler::HandleGetTextFreetype2Properties }, { "GetTextFreetype2Properties", &WSRequestHandler::GetTextFreetype2Properties },
{ "GetBrowserSourceProperties", WSRequestHandler::HandleGetBrowserSourceProperties }, { "GetBrowserSourceProperties", &WSRequestHandler::GetBrowserSourceProperties },
{ "SetBrowserSourceProperties", WSRequestHandler::HandleSetBrowserSourceProperties }, { "SetBrowserSourceProperties", &WSRequestHandler::SetBrowserSourceProperties },
{ "ListOutputs", WSRequestHandler::HandleListOutputs }, { "ListOutputs", &WSRequestHandler::ListOutputs },
{ "GetOutputInfo", WSRequestHandler::HandleGetOutputInfo }, { "GetOutputInfo", &WSRequestHandler::GetOutputInfo },
{ "StartOutput", WSRequestHandler::HandleStartOutput }, { "StartOutput", &WSRequestHandler::StartOutput },
{ "StopOutput", WSRequestHandler::HandleStopOutput } { "StopOutput", &WSRequestHandler::StopOutput }
}; };
QSet<QString> WSRequestHandler::authNotRequired { const QSet<QString> WSRequestHandler::authNotRequired {
"GetVersion", "GetVersion",
"GetAuthRequired", "GetAuthRequired",
"Authenticate" "Authenticate"
}; };
WSRequestHandler::WSRequestHandler(ConnectionProperties& connProperties) : WSRequestHandler::WSRequestHandler(ConnectionProperties& connProperties) :
_messageId(0),
_requestType(""),
data(nullptr),
_connProperties(connProperties) _connProperties(connProperties)
{ {
} }
std::string WSRequestHandler::processIncomingMessage(std::string& textMessage) { RpcResponse WSRequestHandler::processRequest(const RpcRequest& request){
if (GetConfig()->DebugEnabled) {
blog(LOG_INFO, "Request >> '%s'", textMessage.c_str());
}
OBSDataAutoRelease responseData = processRequest(textMessage);
std::string response = obs_data_get_json(responseData);
if (GetConfig()->DebugEnabled) {
blog(LOG_INFO, "Response << '%s'", response.c_str());
}
return response;
}
HandlerResponse WSRequestHandler::processRequest(std::string& textMessage){
std::string msgContainer(textMessage);
const char* msg = msgContainer.c_str();
data = obs_data_create_from_json(msg);
if (!data) {
blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg);
return SendErrorResponse("invalid JSON payload");
}
if (!hasField("request-type") || !hasField("message-id")) {
return SendErrorResponse("missing request parameters");
}
_requestType = obs_data_get_string(data, "request-type");
_messageId = obs_data_get_string(data, "message-id");
if (GetConfig()->AuthRequired if (GetConfig()->AuthRequired
&& (!authNotRequired.contains(_requestType)) && (!authNotRequired.contains(request.methodName()))
&& (!_connProperties.isAuthenticated())) && (!_connProperties.isAuthenticated()))
{ {
return SendErrorResponse("Not Authenticated"); return RpcResponse::fail(request, "Not Authenticated");
} }
HandlerResponse (*handlerFunc)(WSRequestHandler*) = (messageMap[_requestType]); RpcMethodHandler handlerFunc = messageMap[request.methodName()];
if (!handlerFunc) { if (!handlerFunc) {
return SendErrorResponse("invalid request type"); return RpcResponse::fail(request, "invalid request type");
} }
return handlerFunc(this); return std::bind(handlerFunc, this, _1)(request);
}
WSRequestHandler::~WSRequestHandler() {
}
HandlerResponse WSRequestHandler::SendOKResponse(obs_data_t* additionalFields) {
return SendResponse("ok", additionalFields);
}
HandlerResponse WSRequestHandler::SendErrorResponse(QString errorMessage) {
OBSDataAutoRelease fields = obs_data_create();
obs_data_set_string(fields, "error", errorMessage.toUtf8().constData());
return SendResponse("error", fields);
}
HandlerResponse WSRequestHandler::SendErrorResponse(obs_data_t* additionalFields) {
return SendResponse("error", additionalFields);
}
HandlerResponse WSRequestHandler::SendResponse(const char* status, obs_data_t* fields) {
obs_data_t* response = obs_data_create();
obs_data_set_string(response, "message-id", _messageId);
obs_data_set_string(response, "status", status);
if (fields) {
obs_data_apply(response, fields);
}
return response;
}
bool WSRequestHandler::hasField(QString name, obs_data_type expectedFieldType, obs_data_number_type expectedNumberType) {
if (!data || name.isEmpty() || name.isNull()) {
return false;
}
OBSDataItemAutoRelease dataItem = obs_data_item_byname(data, name.toUtf8());
if (!dataItem) {
return false;
}
if (expectedFieldType != OBS_DATA_NULL) {
obs_data_type fieldType = obs_data_item_gettype(dataItem);
if (fieldType != expectedFieldType) {
return false;
}
if (fieldType == OBS_DATA_NUMBER && expectedNumberType != OBS_DATA_NUM_INVALID) {
obs_data_number_type numberType = obs_data_item_numtype(dataItem);
if (numberType != expectedNumberType) {
return false;
}
}
}
return true;
}
bool WSRequestHandler::hasBool(QString fieldName) {
return this->hasField(fieldName, OBS_DATA_BOOLEAN);
}
bool WSRequestHandler::hasString(QString fieldName) {
return this->hasField(fieldName, OBS_DATA_STRING);
}
bool WSRequestHandler::hasNumber(QString fieldName, obs_data_number_type expectedNumberType) {
return this->hasField(fieldName, OBS_DATA_NUMBER, expectedNumberType);
}
bool WSRequestHandler::hasInteger(QString fieldName) {
return this->hasNumber(fieldName, OBS_DATA_NUM_INT);
}
bool WSRequestHandler::hasDouble(QString fieldName) {
return this->hasNumber(fieldName, OBS_DATA_NUM_DOUBLE);
}
bool WSRequestHandler::hasArray(QString fieldName) {
return this->hasField(fieldName, OBS_DATA_ARRAY);
}
bool WSRequestHandler::hasObject(QString fieldName) {
return this->hasField(fieldName, OBS_DATA_OBJECT);
} }

View File

@ -19,171 +19,146 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#pragma once #pragma once
#include <QtCore/QString>
#include <QtCore/QHash> #include <QtCore/QHash>
#include <QtCore/QSet> #include <QtCore/QSet>
#include <QtCore/QVariantHash>
#include <QtCore/QString>
#include <QtCore/QSharedPointer>
#include <obs.hpp> #include <obs.hpp>
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
#include "ConnectionProperties.h" #include "ConnectionProperties.h"
#include "rpc/RpcRequest.h"
#include "rpc/RpcResponse.h"
#include "obs-websocket.h" #include "obs-websocket.h"
typedef obs_data_t* HandlerResponse; class WSRequestHandler;
typedef RpcResponse(WSRequestHandler::*RpcMethodHandler)(const RpcRequest&);
class WSRequestHandler : public QObject {
Q_OBJECT
class WSRequestHandler {
public: public:
explicit WSRequestHandler(ConnectionProperties& connProperties); explicit WSRequestHandler(ConnectionProperties& connProperties);
~WSRequestHandler(); RpcResponse processRequest(const RpcRequest& textMessage);
std::string processIncomingMessage(std::string& textMessage);
bool hasField(QString fieldName, obs_data_type expectedFieldType = OBS_DATA_NULL,
obs_data_number_type expectedNumberType = OBS_DATA_NUM_INVALID);
bool hasBool(QString fieldName);
bool hasString(QString fieldName);
bool hasNumber(QString fieldName, obs_data_number_type expectedNumberType = OBS_DATA_NUM_INVALID);
bool hasInteger(QString fieldName);
bool hasDouble(QString fieldName);
bool hasArray(QString fieldName);
bool hasObject(QString fieldName);
HandlerResponse SendOKResponse(obs_data_t* additionalFields = nullptr);
HandlerResponse SendErrorResponse(QString errorMessage);
HandlerResponse SendErrorResponse(obs_data_t* additionalFields = nullptr);
HandlerResponse SendResponse(const char* status, obs_data_t* additionalFields = nullptr);
obs_data_t* parameters() {
return this->data;
}
private: private:
const char* _messageId;
const char* _requestType;
ConnectionProperties& _connProperties; ConnectionProperties& _connProperties;
OBSDataAutoRelease data;
HandlerResponse processRequest(std::string& textMessage); static const QHash<QString, RpcMethodHandler> messageMap;
static const QSet<QString> authNotRequired;
static QHash<QString, HandlerResponse(*)(WSRequestHandler*)> messageMap; RpcResponse GetVersion(const RpcRequest&);
static QSet<QString> authNotRequired; RpcResponse GetAuthRequired(const RpcRequest&);
RpcResponse Authenticate(const RpcRequest&);
static HandlerResponse HandleGetVersion(WSRequestHandler* req); RpcResponse GetStats(const RpcRequest&);
static HandlerResponse HandleGetAuthRequired(WSRequestHandler* req); RpcResponse SetHeartbeat(const RpcRequest&);
static HandlerResponse HandleAuthenticate(WSRequestHandler* req); RpcResponse GetVideoInfo(const RpcRequest&);
static HandlerResponse HandleGetStats(WSRequestHandler* req); RpcResponse SetFilenameFormatting(const RpcRequest&);
static HandlerResponse HandleSetHeartbeat(WSRequestHandler* req); RpcResponse GetFilenameFormatting(const RpcRequest&);
static HandlerResponse HandleGetVideoInfo(WSRequestHandler* req);
static HandlerResponse HandleSetFilenameFormatting(WSRequestHandler* req); RpcResponse BroadcastCustomMessage(const RpcRequest&);
static HandlerResponse HandleGetFilenameFormatting(WSRequestHandler* req);
static HandlerResponse HandleBroadcastCustomMessage(WSRequestHandler* req); RpcResponse SetCurrentScene(const RpcRequest&);
RpcResponse GetCurrentScene(const RpcRequest&);
RpcResponse GetSceneList(const RpcRequest&);
static HandlerResponse HandleSetCurrentScene(WSRequestHandler* req); RpcResponse SetSceneItemRender(const RpcRequest&);
static HandlerResponse HandleGetCurrentScene(WSRequestHandler* req); RpcResponse SetSceneItemPosition(const RpcRequest&);
static HandlerResponse HandleGetSceneList(WSRequestHandler* req); RpcResponse SetSceneItemTransform(const RpcRequest&);
RpcResponse SetSceneItemCrop(const RpcRequest&);
RpcResponse GetSceneItemProperties(const RpcRequest&);
RpcResponse SetSceneItemProperties(const RpcRequest&);
RpcResponse ResetSceneItem(const RpcRequest&);
RpcResponse DuplicateSceneItem(const RpcRequest&);
RpcResponse DeleteSceneItem(const RpcRequest&);
RpcResponse ReorderSceneItems(const RpcRequest&);
static HandlerResponse HandleSetSceneItemRender(WSRequestHandler* req); RpcResponse GetStreamingStatus(const RpcRequest&);
static HandlerResponse HandleSetSceneItemPosition(WSRequestHandler* req); RpcResponse StartStopStreaming(const RpcRequest&);
static HandlerResponse HandleSetSceneItemTransform(WSRequestHandler* req); RpcResponse StartStopRecording(const RpcRequest&);
static HandlerResponse HandleSetSceneItemCrop(WSRequestHandler* req);
static HandlerResponse HandleGetSceneItemProperties(WSRequestHandler* req);
static HandlerResponse HandleSetSceneItemProperties(WSRequestHandler* req);
static HandlerResponse HandleResetSceneItem(WSRequestHandler* req);
static HandlerResponse HandleDuplicateSceneItem(WSRequestHandler* req);
static HandlerResponse HandleDeleteSceneItem(WSRequestHandler* req);
static HandlerResponse HandleReorderSceneItems(WSRequestHandler* req);
static HandlerResponse HandleGetStreamingStatus(WSRequestHandler* req); RpcResponse StartStreaming(const RpcRequest&);
static HandlerResponse HandleStartStopStreaming(WSRequestHandler* req); RpcResponse StopStreaming(const RpcRequest&);
static HandlerResponse HandleStartStopRecording(WSRequestHandler* req);
static HandlerResponse HandleStartStreaming(WSRequestHandler* req); RpcResponse StartRecording(const RpcRequest&);
static HandlerResponse HandleStopStreaming(WSRequestHandler* req); RpcResponse StopRecording(const RpcRequest&);
RpcResponse PauseRecording(const RpcRequest&);
RpcResponse ResumeRecording(const RpcRequest&);
static HandlerResponse HandleStartRecording(WSRequestHandler* req); RpcResponse StartStopReplayBuffer(const RpcRequest&);
static HandlerResponse HandleStopRecording(WSRequestHandler* req); RpcResponse StartReplayBuffer(const RpcRequest&);
static HandlerResponse HandlePauseRecording(WSRequestHandler* req); RpcResponse StopReplayBuffer(const RpcRequest&);
static HandlerResponse HandleResumeRecording(WSRequestHandler* req); RpcResponse SaveReplayBuffer(const RpcRequest&);
static HandlerResponse HandleStartStopReplayBuffer(WSRequestHandler* req); RpcResponse SetRecordingFolder(const RpcRequest&);
static HandlerResponse HandleStartReplayBuffer(WSRequestHandler* req); RpcResponse GetRecordingFolder(const RpcRequest&);
static HandlerResponse HandleStopReplayBuffer(WSRequestHandler* req);
static HandlerResponse HandleSaveReplayBuffer(WSRequestHandler* req);
static HandlerResponse HandleSetRecordingFolder(WSRequestHandler* req); RpcResponse GetTransitionList(const RpcRequest&);
static HandlerResponse HandleGetRecordingFolder(WSRequestHandler* req); RpcResponse GetCurrentTransition(const RpcRequest&);
RpcResponse SetCurrentTransition(const RpcRequest&);
static HandlerResponse HandleGetTransitionList(WSRequestHandler* req); RpcResponse SetVolume(const RpcRequest&);
static HandlerResponse HandleGetCurrentTransition(WSRequestHandler* req); RpcResponse GetVolume(const RpcRequest&);
static HandlerResponse HandleSetCurrentTransition(WSRequestHandler* req); RpcResponse ToggleMute(const RpcRequest&);
RpcResponse SetMute(const RpcRequest&);
RpcResponse GetMute(const RpcRequest&);
RpcResponse SetSyncOffset(const RpcRequest&);
RpcResponse GetSyncOffset(const RpcRequest&);
RpcResponse GetSpecialSources(const RpcRequest&);
RpcResponse GetSourcesList(const RpcRequest&);
RpcResponse GetSourceTypesList(const RpcRequest&);
RpcResponse GetSourceSettings(const RpcRequest&);
RpcResponse SetSourceSettings(const RpcRequest&);
RpcResponse TakeSourceScreenshot(const RpcRequest&);
static HandlerResponse HandleSetVolume(WSRequestHandler* req); RpcResponse GetSourceFilters(const RpcRequest&);
static HandlerResponse HandleGetVolume(WSRequestHandler* req); RpcResponse GetSourceFilterInfo(const RpcRequest&);
static HandlerResponse HandleToggleMute(WSRequestHandler* req); RpcResponse AddFilterToSource(const RpcRequest&);
static HandlerResponse HandleSetMute(WSRequestHandler* req); RpcResponse RemoveFilterFromSource(const RpcRequest&);
static HandlerResponse HandleGetMute(WSRequestHandler* req); RpcResponse ReorderSourceFilter(const RpcRequest&);
static HandlerResponse HandleSetSyncOffset(WSRequestHandler* req); RpcResponse MoveSourceFilter(const RpcRequest&);
static HandlerResponse HandleGetSyncOffset(WSRequestHandler* req); RpcResponse SetSourceFilterSettings(const RpcRequest&);
static HandlerResponse HandleGetSpecialSources(WSRequestHandler* req); RpcResponse SetSourceFilterVisibility(const RpcRequest&);
static HandlerResponse HandleGetSourcesList(WSRequestHandler* req);
static HandlerResponse HandleGetSourceTypesList(WSRequestHandler* req);
static HandlerResponse HandleGetSourceSettings(WSRequestHandler* req);
static HandlerResponse HandleSetSourceSettings(WSRequestHandler* req);
static HandlerResponse HandleTakeSourceScreenshot(WSRequestHandler* req);
static HandlerResponse HandleGetSourceFilters(WSRequestHandler* req); RpcResponse SetCurrentSceneCollection(const RpcRequest&);
static HandlerResponse HandleGetSourceFilterInfo(WSRequestHandler* req); RpcResponse GetCurrentSceneCollection(const RpcRequest&);
static HandlerResponse HandleAddFilterToSource(WSRequestHandler* req); RpcResponse ListSceneCollections(const RpcRequest&);
static HandlerResponse HandleRemoveFilterFromSource(WSRequestHandler* req);
static HandlerResponse HandleReorderSourceFilter(WSRequestHandler* req);
static HandlerResponse HandleMoveSourceFilter(WSRequestHandler* req);
static HandlerResponse HandleSetSourceFilterSettings(WSRequestHandler* req);
static HandlerResponse HandleSetSourceFilterVisibility(WSRequestHandler* req);
static HandlerResponse HandleSetCurrentSceneCollection(WSRequestHandler* req); RpcResponse SetCurrentProfile(const RpcRequest&);
static HandlerResponse HandleGetCurrentSceneCollection(WSRequestHandler* req); RpcResponse GetCurrentProfile(const RpcRequest&);
static HandlerResponse HandleListSceneCollections(WSRequestHandler* req); RpcResponse ListProfiles(const RpcRequest&);
static HandlerResponse HandleSetCurrentProfile(WSRequestHandler* req); RpcResponse SetStreamSettings(const RpcRequest&);
static HandlerResponse HandleGetCurrentProfile(WSRequestHandler* req); RpcResponse GetStreamSettings(const RpcRequest&);
static HandlerResponse HandleListProfiles(WSRequestHandler* req); RpcResponse SaveStreamSettings(const RpcRequest&);
static HandlerResponse HandleSetStreamSettings(WSRequestHandler* req);
static HandlerResponse HandleGetStreamSettings(WSRequestHandler* req);
static HandlerResponse HandleSaveStreamSettings(WSRequestHandler* req);
#if BUILD_CAPTIONS #if BUILD_CAPTIONS
static HandlerResponse HandleSendCaptions(WSRequestHandler * req); RpcResponse SendCaptions(const RpcRequest&);
#endif #endif
static HandlerResponse HandleSetTransitionDuration(WSRequestHandler* req); RpcResponse SetTransitionDuration(const RpcRequest&);
static HandlerResponse HandleGetTransitionDuration(WSRequestHandler* req); RpcResponse GetTransitionDuration(const RpcRequest&);
static HandlerResponse HandleGetStudioModeStatus(WSRequestHandler* req); RpcResponse GetStudioModeStatus(const RpcRequest&);
static HandlerResponse HandleGetPreviewScene(WSRequestHandler* req); RpcResponse GetPreviewScene(const RpcRequest&);
static HandlerResponse HandleSetPreviewScene(WSRequestHandler* req); RpcResponse SetPreviewScene(const RpcRequest&);
static HandlerResponse HandleTransitionToProgram(WSRequestHandler* req); RpcResponse TransitionToProgram(const RpcRequest&);
static HandlerResponse HandleEnableStudioMode(WSRequestHandler* req); RpcResponse EnableStudioMode(const RpcRequest&);
static HandlerResponse HandleDisableStudioMode(WSRequestHandler* req); RpcResponse DisableStudioMode(const RpcRequest&);
static HandlerResponse HandleToggleStudioMode(WSRequestHandler* req); RpcResponse ToggleStudioMode(const RpcRequest&);
static HandlerResponse HandleSetTextGDIPlusProperties(WSRequestHandler* req); RpcResponse SetTextGDIPlusProperties(const RpcRequest&);
static HandlerResponse HandleGetTextGDIPlusProperties(WSRequestHandler* req); RpcResponse GetTextGDIPlusProperties(const RpcRequest&);
static HandlerResponse HandleSetTextFreetype2Properties(WSRequestHandler* req); RpcResponse SetTextFreetype2Properties(const RpcRequest&);
static HandlerResponse HandleGetTextFreetype2Properties(WSRequestHandler* req); RpcResponse GetTextFreetype2Properties(const RpcRequest&);
static HandlerResponse HandleSetBrowserSourceProperties(WSRequestHandler* req); RpcResponse SetBrowserSourceProperties(const RpcRequest&);
static HandlerResponse HandleGetBrowserSourceProperties(WSRequestHandler* req); RpcResponse GetBrowserSourceProperties(const RpcRequest&);
static HandlerResponse HandleListOutputs(WSRequestHandler* req); RpcResponse ListOutputs(const RpcRequest&);
static HandlerResponse HandleGetOutputInfo(WSRequestHandler* req); RpcResponse GetOutputInfo(const RpcRequest&);
static HandlerResponse HandleStartOutput(WSRequestHandler* req); RpcResponse StartOutput(const RpcRequest&);
static HandlerResponse HandleStopOutput(WSRequestHandler* req); RpcResponse StopOutput(const RpcRequest&);
}; };

View File

@ -66,10 +66,10 @@ const char *describe_scale_type(int scale) {
* @category general * @category general
* @since 0.3 * @since 0.3
*/ */
HandlerResponse WSRequestHandler::HandleGetVersion(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetVersion(const RpcRequest& request) {
QString obsVersion = Utils::OBSVersionString(); QString obsVersion = Utils::OBSVersionString();
QList<QString> names = req->messageMap.keys(); QList<QString> names = messageMap.keys();
names.sort(Qt::CaseInsensitive); names.sort(Qt::CaseInsensitive);
// (Palakis) OBS' data arrays only support object arrays, so I improvised. // (Palakis) OBS' data arrays only support object arrays, so I improvised.
@ -85,7 +85,7 @@ HandlerResponse WSRequestHandler::HandleGetVersion(WSRequestHandler* req) {
obs_data_set_string(data, "obs-studio-version", obsVersion.toUtf8()); obs_data_set_string(data, "obs-studio-version", obsVersion.toUtf8());
obs_data_set_string(data, "available-requests", requests.toUtf8()); obs_data_set_string(data, "available-requests", requests.toUtf8());
return req->SendOKResponse(data); return request.success(data);
} }
/** /**
@ -101,7 +101,7 @@ HandlerResponse WSRequestHandler::HandleGetVersion(WSRequestHandler* req) {
* @category general * @category general
* @since 0.3 * @since 0.3
*/ */
HandlerResponse WSRequestHandler::HandleGetAuthRequired(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetAuthRequired(const RpcRequest& request) {
bool authRequired = GetConfig()->AuthRequired; bool authRequired = GetConfig()->AuthRequired;
OBSDataAutoRelease data = obs_data_create(); OBSDataAutoRelease data = obs_data_create();
@ -115,7 +115,7 @@ HandlerResponse WSRequestHandler::HandleGetAuthRequired(WSRequestHandler* req) {
config->Salt.toUtf8()); config->Salt.toUtf8());
} }
return req->SendOKResponse(data); return request.success(data);
} }
/** /**
@ -128,26 +128,26 @@ HandlerResponse WSRequestHandler::HandleGetAuthRequired(WSRequestHandler* req) {
* @category general * @category general
* @since 0.3 * @since 0.3
*/ */
HandlerResponse WSRequestHandler::HandleAuthenticate(WSRequestHandler* req) { RpcResponse WSRequestHandler::Authenticate(const RpcRequest& request) {
if (!req->hasField("auth")) { if (!request.hasField("auth")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
if (req->_connProperties.isAuthenticated()) { if (_connProperties.isAuthenticated()) {
return req->SendErrorResponse("already authenticated"); return request.failed("already authenticated");
} }
QString auth = obs_data_get_string(req->data, "auth"); QString auth = obs_data_get_string(request.parameters(), "auth");
if (auth.isEmpty()) { if (auth.isEmpty()) {
return req->SendErrorResponse("auth not specified!"); return request.failed("auth not specified!");
} }
if (GetConfig()->CheckAuth(auth) == false) { if (GetConfig()->CheckAuth(auth) == false) {
return req->SendErrorResponse("Authentication Failed."); return request.failed("Authentication Failed.");
} }
req->_connProperties.setAuthenticated(true); _connProperties.setAuthenticated(true);
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -160,17 +160,18 @@ HandlerResponse WSRequestHandler::HandleAuthenticate(WSRequestHandler* req) {
* @category general * @category general
* @since 4.3.0 * @since 4.3.0
*/ */
HandlerResponse WSRequestHandler::HandleSetHeartbeat(WSRequestHandler* req) { RpcResponse WSRequestHandler::SetHeartbeat(const RpcRequest& request) {
if (!req->hasField("enable")) { if (!request.hasField("enable")) {
return req->SendErrorResponse("Heartbeat <enable> parameter missing"); return request.failed("Heartbeat <enable> parameter missing");
} }
auto events = GetEventsSystem(); auto events = GetEventsSystem();
events->HeartbeatIsActive = obs_data_get_bool(req->data, "enable"); events->HeartbeatIsActive = obs_data_get_bool(request.parameters(), "enable");
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_bool(response, "enable", events->HeartbeatIsActive); obs_data_set_bool(response, "enable", events->HeartbeatIsActive);
return req->SendOKResponse(response);
return request.success(response);
} }
/** /**
@ -183,18 +184,19 @@ HandlerResponse WSRequestHandler::HandleSetHeartbeat(WSRequestHandler* req) {
* @category general * @category general
* @since 4.3.0 * @since 4.3.0
*/ */
HandlerResponse WSRequestHandler::HandleSetFilenameFormatting(WSRequestHandler* req) { RpcResponse WSRequestHandler::SetFilenameFormatting(const RpcRequest& request) {
if (!req->hasField("filename-formatting")) { if (!request.hasField("filename-formatting")) {
return req->SendErrorResponse("<filename-formatting> parameter missing"); return request.failed("<filename-formatting> parameter missing");
} }
QString filenameFormatting = obs_data_get_string(req->data, "filename-formatting"); QString filenameFormatting = obs_data_get_string(request.parameters(), "filename-formatting");
if (filenameFormatting.isEmpty()) { if (filenameFormatting.isEmpty()) {
return req->SendErrorResponse("invalid request parameters"); return request.failed("invalid request parameters");
} }
Utils::SetFilenameFormatting(filenameFormatting.toUtf8()); Utils::SetFilenameFormatting(filenameFormatting.toUtf8());
return req->SendOKResponse();
return request.success();
} }
/** /**
@ -207,10 +209,11 @@ HandlerResponse WSRequestHandler::HandleSetFilenameFormatting(WSRequestHandler*
* @category general * @category general
* @since 4.3.0 * @since 4.3.0
*/ */
HandlerResponse WSRequestHandler::HandleGetFilenameFormatting(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetFilenameFormatting(const RpcRequest& request) {
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "filename-formatting", Utils::GetFilenameFormatting()); obs_data_set_string(response, "filename-formatting", Utils::GetFilenameFormatting());
return req->SendOKResponse(response);
return request.success(response);
} }
/** /**
@ -223,12 +226,13 @@ HandlerResponse WSRequestHandler::HandleGetFilenameFormatting(WSRequestHandler*
* @category general * @category general
* @since 4.6.0 * @since 4.6.0
*/ */
HandlerResponse WSRequestHandler::HandleGetStats(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetStats(const RpcRequest& request) {
OBSDataAutoRelease stats = GetEventsSystem()->GetStats(); OBSDataAutoRelease stats = GetEventsSystem()->GetStats();
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_obj(response, "stats", stats); obs_data_set_obj(response, "stats", stats);
return req->SendOKResponse(response);
return request.success(response);
} }
/** /**
@ -242,26 +246,26 @@ HandlerResponse WSRequestHandler::HandleGetStats(WSRequestHandler* req) {
* @category general * @category general
* @since 4.7.0 * @since 4.7.0
*/ */
HandlerResponse WSRequestHandler::HandleBroadcastCustomMessage(WSRequestHandler* req) { RpcResponse WSRequestHandler::BroadcastCustomMessage(const RpcRequest& request) {
if (!req->hasField("realm") || !req->hasField("data")) { if (!request.hasField("realm") || !request.hasField("data")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
QString realm = obs_data_get_string(req->data, "realm"); QString realm = obs_data_get_string(request.parameters(), "realm");
OBSDataAutoRelease data = obs_data_get_obj(req->data, "data"); OBSDataAutoRelease data = obs_data_get_obj(request.parameters(), "data");
if (realm.isEmpty()) { if (realm.isEmpty()) {
return req->SendErrorResponse("realm not specified!"); return request.failed("realm not specified!");
} }
if (!data) { if (!data) {
return req->SendErrorResponse("data not specified!"); return request.failed("data not specified!");
} }
auto events = GetEventsSystem(); auto events = GetEventsSystem();
events->OnBroadcastCustomMessage(realm, data); events->OnBroadcastCustomMessage(realm, data);
return req->SendOKResponse(); return request.success();
} }
@ -283,9 +287,10 @@ HandlerResponse WSRequestHandler::HandleBroadcastCustomMessage(WSRequestHandler*
* @category general * @category general
* @since 4.6.0 * @since 4.6.0
*/ */
HandlerResponse WSRequestHandler::HandleGetVideoInfo(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetVideoInfo(const RpcRequest& request) {
obs_video_info ovi; obs_video_info ovi;
obs_get_video_info(&ovi); obs_get_video_info(&ovi);
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_int(response, "baseWidth", ovi.base_width); obs_data_set_int(response, "baseWidth", ovi.base_width);
obs_data_set_int(response, "baseHeight", ovi.base_height); obs_data_set_int(response, "baseHeight", ovi.base_height);
@ -296,5 +301,6 @@ HandlerResponse WSRequestHandler::HandleGetVideoInfo(WSRequestHandler* req) {
obs_data_set_string(response, "colorSpace", describe_color_space(ovi.colorspace)); obs_data_set_string(response, "colorSpace", describe_color_space(ovi.colorspace));
obs_data_set_string(response, "colorRange", describe_color_range(ovi.range)); obs_data_set_string(response, "colorRange", describe_color_range(ovi.range));
obs_data_set_string(response, "scaleType", describe_scale_type(ovi.scale_type)); obs_data_set_string(response, "scaleType", describe_scale_type(ovi.scale_type));
return req->SendOKResponse(response);
return request.success(response);
} }

View File

@ -1,3 +1,5 @@
#include <functional>
#include "WSRequestHandler.h" #include "WSRequestHandler.h"
/** /**
@ -57,16 +59,16 @@ obs_data_t* getOutputInfo(obs_output_t* output)
return data; return data;
} }
HandlerResponse findOutputOrFail(WSRequestHandler* req, std::function<HandlerResponse (obs_output_t*)> callback) RpcResponse findOutputOrFail(const RpcRequest& request, std::function<RpcResponse (obs_output_t*)> callback)
{ {
if (!req->hasField("outputName")) { if (!request.hasField("outputName")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
const char* outputName = obs_data_get_string(req->parameters(), "outputName"); const char* outputName = obs_data_get_string(request.parameters(), "outputName");
OBSOutputAutoRelease output = obs_get_output_by_name(outputName); OBSOutputAutoRelease output = obs_get_output_by_name(outputName);
if (!output) { if (!output) {
return req->SendErrorResponse("specified output doesn't exist"); return request.failed("specified output doesn't exist");
} }
return callback(output); return callback(output);
@ -82,7 +84,7 @@ HandlerResponse findOutputOrFail(WSRequestHandler* req, std::function<HandlerRes
* @category outputs * @category outputs
* @since 4.7.0 * @since 4.7.0
*/ */
HandlerResponse WSRequestHandler::HandleListOutputs(WSRequestHandler* req) RpcResponse WSRequestHandler::ListOutputs(const RpcRequest& request)
{ {
OBSDataArrayAutoRelease outputs = obs_data_array_create(); OBSDataArrayAutoRelease outputs = obs_data_array_create();
@ -97,7 +99,8 @@ HandlerResponse WSRequestHandler::HandleListOutputs(WSRequestHandler* req)
OBSDataAutoRelease fields = obs_data_create(); OBSDataAutoRelease fields = obs_data_create();
obs_data_set_array(fields, "outputs", outputs); obs_data_set_array(fields, "outputs", outputs);
return req->SendOKResponse(fields);
return request.success(fields);
} }
/** /**
@ -112,14 +115,14 @@ HandlerResponse WSRequestHandler::HandleListOutputs(WSRequestHandler* req)
* @category outputs * @category outputs
* @since 4.7.0 * @since 4.7.0
*/ */
HandlerResponse WSRequestHandler::HandleGetOutputInfo(WSRequestHandler* req) RpcResponse WSRequestHandler::GetOutputInfo(const RpcRequest& request)
{ {
return findOutputOrFail(req, [req](obs_output_t* output) { return findOutputOrFail(request, [request](obs_output_t* output) {
OBSDataAutoRelease outputInfo = getOutputInfo(output); OBSDataAutoRelease outputInfo = getOutputInfo(output);
OBSDataAutoRelease fields = obs_data_create(); OBSDataAutoRelease fields = obs_data_create();
obs_data_set_obj(fields, "outputInfo", outputInfo); obs_data_set_obj(fields, "outputInfo", outputInfo);
return req->SendOKResponse(fields); return request.success(fields);
}); });
} }
@ -133,21 +136,21 @@ HandlerResponse WSRequestHandler::HandleGetOutputInfo(WSRequestHandler* req)
* @category outputs * @category outputs
* @since 4.7.0 * @since 4.7.0
*/ */
HandlerResponse WSRequestHandler::HandleStartOutput(WSRequestHandler* req) RpcResponse WSRequestHandler::StartOutput(const RpcRequest& request)
{ {
return findOutputOrFail(req, [req](obs_output_t* output) { return findOutputOrFail(request, [request](obs_output_t* output) {
if (obs_output_active(output)) { if (obs_output_active(output)) {
return req->SendErrorResponse("output already active"); return request.failed("output already active");
} }
bool success = obs_output_start(output); bool success = obs_output_start(output);
if (!success) { if (!success) {
QString lastError = obs_output_get_last_error(output); QString lastError = obs_output_get_last_error(output);
QString errorMessage = QString("output start failed: %1").arg(lastError); QString errorMessage = QString("output start failed: %1").arg(lastError);
return req->SendErrorResponse(errorMessage); return request.failed(errorMessage);
} }
return req->SendOKResponse(); return request.success();
}); });
} }
@ -162,20 +165,20 @@ HandlerResponse WSRequestHandler::HandleStartOutput(WSRequestHandler* req)
* @category outputs * @category outputs
* @since 4.7.0 * @since 4.7.0
*/ */
HandlerResponse WSRequestHandler::HandleStopOutput(WSRequestHandler* req) RpcResponse WSRequestHandler::StopOutput(const RpcRequest& request)
{ {
return findOutputOrFail(req, [req](obs_output_t* output) { return findOutputOrFail(request, [request](obs_output_t* output) {
if (!obs_output_active(output)) { if (!obs_output_active(output)) {
return req->SendErrorResponse("output not active"); return request.failed("output not active");
} }
bool forceStop = obs_data_get_bool(req->data, "force"); bool forceStop = obs_data_get_bool(request.parameters(), "force");
if (forceStop) { if (forceStop) {
obs_output_force_stop(output); obs_output_force_stop(output);
} else { } else {
obs_output_stop(output); obs_output_stop(output);
} }
return req->SendOKResponse(); return request.success();
}); });
} }

View File

@ -12,19 +12,19 @@
* @category profiles * @category profiles
* @since 4.0.0 * @since 4.0.0
*/ */
HandlerResponse WSRequestHandler::HandleSetCurrentProfile(WSRequestHandler* req) { RpcResponse WSRequestHandler::SetCurrentProfile(const RpcRequest& request) {
if (!req->hasField("profile-name")) { if (!request.hasField("profile-name")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
QString profileName = obs_data_get_string(req->data, "profile-name"); QString profileName = obs_data_get_string(request.parameters(), "profile-name");
if (profileName.isEmpty()) { if (profileName.isEmpty()) {
return req->SendErrorResponse("invalid request parameters"); return request.failed("invalid request parameters");
} }
// TODO : check if profile exists // TODO : check if profile exists
obs_frontend_set_current_profile(profileName.toUtf8()); obs_frontend_set_current_profile(profileName.toUtf8());
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -37,12 +37,12 @@ HandlerResponse WSRequestHandler::HandleSetCurrentProfile(WSRequestHandler* req)
* @category profiles * @category profiles
* @since 4.0.0 * @since 4.0.0
*/ */
HandlerResponse WSRequestHandler::HandleGetCurrentProfile(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetCurrentProfile(const RpcRequest& request) {
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
char* currentProfile = obs_frontend_get_current_profile(); char* currentProfile = obs_frontend_get_current_profile();
obs_data_set_string(response, "profile-name", currentProfile); obs_data_set_string(response, "profile-name", currentProfile);
bfree(currentProfile); bfree(currentProfile);
return req->SendOKResponse(response); return request.success(response);
} }
/** /**
@ -55,7 +55,7 @@ HandlerResponse WSRequestHandler::HandleGetCurrentProfile(WSRequestHandler* req)
* @category profiles * @category profiles
* @since 4.0.0 * @since 4.0.0
*/ */
HandlerResponse WSRequestHandler::HandleListProfiles(WSRequestHandler* req) { RpcResponse WSRequestHandler::ListProfiles(const RpcRequest& request) {
char** profiles = obs_frontend_get_profiles(); char** profiles = obs_frontend_get_profiles();
OBSDataArrayAutoRelease list = Utils::StringListToArray(profiles, "profile-name"); OBSDataArrayAutoRelease list = Utils::StringListToArray(profiles, "profile-name");
bfree(profiles); bfree(profiles);
@ -63,5 +63,5 @@ HandlerResponse WSRequestHandler::HandleListProfiles(WSRequestHandler* req) {
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_array(response, "profiles", list); obs_data_set_array(response, "profiles", list);
return req->SendOKResponse(response); return request.success(response);
} }

View File

@ -1,16 +1,17 @@
#include "WSRequestHandler.h" #include "WSRequestHandler.h"
#include <functional>
#include <util/platform.h> #include <util/platform.h>
#include "Utils.h" #include "Utils.h"
HandlerResponse ifCanPause(WSRequestHandler* req, std::function<HandlerResponse()> callback) RpcResponse ifCanPause(const RpcRequest& request, std::function<RpcResponse()> callback)
{ {
if (!obs_frontend_recording_active()) { if (!obs_frontend_recording_active()) {
return req->SendErrorResponse("recording is not active"); return request.failed("recording is not active");
} }
if (!Utils::RecordingPauseSupported()) { if (!Utils::RecordingPauseSupported()) {
return req->SendErrorResponse("recording pauses are not available in this version of OBS Studio"); return request.failed("recording pauses are not available in this version of OBS Studio");
} }
return callback(); return callback();
@ -24,9 +25,9 @@ HandlerResponse ifCanPause(WSRequestHandler* req, std::function<HandlerResponse(
* @category recording * @category recording
* @since 0.3 * @since 0.3
*/ */
HandlerResponse WSRequestHandler::HandleStartStopRecording(WSRequestHandler* req) { RpcResponse WSRequestHandler::StartStopRecording(const RpcRequest& request) {
(obs_frontend_recording_active() ? obs_frontend_recording_stop() : obs_frontend_recording_start()); (obs_frontend_recording_active() ? obs_frontend_recording_stop() : obs_frontend_recording_start());
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -38,13 +39,13 @@ HandlerResponse WSRequestHandler::HandleStartStopRecording(WSRequestHandler* req
* @category recording * @category recording
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleStartRecording(WSRequestHandler* req) { RpcResponse WSRequestHandler::StartRecording(const RpcRequest& request) {
if (obs_frontend_recording_active()) { if (obs_frontend_recording_active()) {
return req->SendErrorResponse("recording already active"); return request.failed("recording already active");
} }
obs_frontend_recording_start(); obs_frontend_recording_start();
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -56,13 +57,13 @@ HandlerResponse WSRequestHandler::HandleStartRecording(WSRequestHandler* req) {
* @category recording * @category recording
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleStopRecording(WSRequestHandler* req) { RpcResponse WSRequestHandler::StopRecording(const RpcRequest& request) {
if (!obs_frontend_recording_active()) { if (!obs_frontend_recording_active()) {
return req->SendErrorResponse("recording not active"); return request.failed("recording not active");
} }
obs_frontend_recording_stop(); obs_frontend_recording_stop();
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -74,14 +75,14 @@ HandlerResponse WSRequestHandler::HandleStartRecording(WSRequestHandler* req) {
* @category recording * @category recording
* @since 4.7.0 * @since 4.7.0
*/ */
HandlerResponse WSRequestHandler::HandlePauseRecording(WSRequestHandler* req) { RpcResponse WSRequestHandler::PauseRecording(const RpcRequest& request) {
return ifCanPause(req, [req]() { return ifCanPause(request, [request]() {
if (Utils::RecordingPaused()) { if (Utils::RecordingPaused()) {
return req->SendErrorResponse("recording already paused"); return request.failed("recording already paused");
} }
Utils::PauseRecording(true); Utils::PauseRecording(true);
return req->SendOKResponse(); return request.success();
}); });
} }
@ -94,14 +95,14 @@ HandlerResponse WSRequestHandler::HandlePauseRecording(WSRequestHandler* req) {
* @category recording * @category recording
* @since 4.7.0 * @since 4.7.0
*/ */
HandlerResponse WSRequestHandler::HandleResumeRecording(WSRequestHandler* req) { RpcResponse WSRequestHandler::ResumeRecording(const RpcRequest& request) {
return ifCanPause(req, [req]() { return ifCanPause(request, [request]() {
if (!Utils::RecordingPaused()) { if (!Utils::RecordingPaused()) {
return req->SendErrorResponse("recording is not paused"); return request.failed("recording is not paused");
} }
Utils::PauseRecording(false); Utils::PauseRecording(false);
return req->SendOKResponse(); return request.success();
}); });
} }
@ -120,18 +121,18 @@ HandlerResponse WSRequestHandler::HandleResumeRecording(WSRequestHandler* req) {
* @category recording * @category recording
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleSetRecordingFolder(WSRequestHandler* req) { RpcResponse WSRequestHandler::SetRecordingFolder(const RpcRequest& request) {
if (!req->hasField("rec-folder")) { if (!request.hasField("rec-folder")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
const char* newRecFolder = obs_data_get_string(req->data, "rec-folder"); const char* newRecFolder = obs_data_get_string(request.parameters(), "rec-folder");
bool success = Utils::SetRecordingFolder(newRecFolder); bool success = Utils::SetRecordingFolder(newRecFolder);
if (!success) { if (!success) {
return req->SendErrorResponse("invalid request parameters"); return request.failed("invalid request parameters");
} }
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -144,11 +145,11 @@ HandlerResponse WSRequestHandler::HandleSetRecordingFolder(WSRequestHandler* req
* @category recording * @category recording
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleGetRecordingFolder(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetRecordingFolder(const RpcRequest& request) {
const char* recFolder = Utils::GetRecordingFolder(); const char* recFolder = Utils::GetRecordingFolder();
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "rec-folder", recFolder); obs_data_set_string(response, "rec-folder", recFolder);
return req->SendOKResponse(response); return request.success(response);
} }

View File

@ -10,13 +10,13 @@
* @category replay buffer * @category replay buffer
* @since 4.2.0 * @since 4.2.0
*/ */
HandlerResponse WSRequestHandler::HandleStartStopReplayBuffer(WSRequestHandler* req) { RpcResponse WSRequestHandler::StartStopReplayBuffer(const RpcRequest& request) {
if (obs_frontend_replay_buffer_active()) { if (obs_frontend_replay_buffer_active()) {
obs_frontend_replay_buffer_stop(); obs_frontend_replay_buffer_stop();
} else { } else {
Utils::StartReplayBuffer(); Utils::StartReplayBuffer();
} }
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -31,17 +31,17 @@ HandlerResponse WSRequestHandler::HandleStartStopReplayBuffer(WSRequestHandler*
* @category replay buffer * @category replay buffer
* @since 4.2.0 * @since 4.2.0
*/ */
HandlerResponse WSRequestHandler::HandleStartReplayBuffer(WSRequestHandler* req) { RpcResponse WSRequestHandler::StartReplayBuffer(const RpcRequest& request) {
if (!Utils::ReplayBufferEnabled()) { if (!Utils::ReplayBufferEnabled()) {
return req->SendErrorResponse("replay buffer disabled in settings"); return request.failed("replay buffer disabled in settings");
} }
if (obs_frontend_replay_buffer_active() == true) { if (obs_frontend_replay_buffer_active() == true) {
return req->SendErrorResponse("replay buffer already active"); return request.failed("replay buffer already active");
} }
Utils::StartReplayBuffer(); Utils::StartReplayBuffer();
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -53,12 +53,12 @@ HandlerResponse WSRequestHandler::HandleStartReplayBuffer(WSRequestHandler* req)
* @category replay buffer * @category replay buffer
* @since 4.2.0 * @since 4.2.0
*/ */
HandlerResponse WSRequestHandler::HandleStopReplayBuffer(WSRequestHandler* req) { RpcResponse WSRequestHandler::StopReplayBuffer(const RpcRequest& request) {
if (obs_frontend_replay_buffer_active() == true) { if (obs_frontend_replay_buffer_active() == true) {
obs_frontend_replay_buffer_stop(); obs_frontend_replay_buffer_stop();
return req->SendOKResponse(); return request.success();
} else { } else {
return req->SendErrorResponse("replay buffer not active"); return request.failed("replay buffer not active");
} }
} }
@ -72,9 +72,9 @@ HandlerResponse WSRequestHandler::HandleStopReplayBuffer(WSRequestHandler* req)
* @category replay buffer * @category replay buffer
* @since 4.2.0 * @since 4.2.0
*/ */
HandlerResponse WSRequestHandler::HandleSaveReplayBuffer(WSRequestHandler* req) { RpcResponse WSRequestHandler::SaveReplayBuffer(const RpcRequest& request) {
if (!obs_frontend_replay_buffer_active()) { if (!obs_frontend_replay_buffer_active()) {
return req->SendErrorResponse("replay buffer not active"); return request.failed("replay buffer not active");
} }
OBSOutputAutoRelease replayOutput = obs_frontend_get_replay_buffer_output(); OBSOutputAutoRelease replayOutput = obs_frontend_get_replay_buffer_output();
@ -84,5 +84,5 @@ HandlerResponse WSRequestHandler::HandleSaveReplayBuffer(WSRequestHandler* req)
proc_handler_call(ph, "save", &cd); proc_handler_call(ph, "save", &cd);
calldata_free(&cd); calldata_free(&cd);
return req->SendOKResponse(); return request.success();
} }

View File

@ -12,19 +12,19 @@
* @category scene collections * @category scene collections
* @since 4.0.0 * @since 4.0.0
*/ */
HandlerResponse WSRequestHandler::HandleSetCurrentSceneCollection(WSRequestHandler* req) { RpcResponse WSRequestHandler::SetCurrentSceneCollection(const RpcRequest& request) {
if (!req->hasField("sc-name")) { if (!request.hasField("sc-name")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
QString sceneCollection = obs_data_get_string(req->data, "sc-name"); QString sceneCollection = obs_data_get_string(request.parameters(), "sc-name");
if (sceneCollection.isEmpty()) { if (sceneCollection.isEmpty()) {
return req->SendErrorResponse("invalid request parameters"); return request.failed("invalid request parameters");
} }
// TODO : Check if specified profile exists and if changing is allowed // TODO : Check if specified profile exists and if changing is allowed
obs_frontend_set_current_scene_collection(sceneCollection.toUtf8()); obs_frontend_set_current_scene_collection(sceneCollection.toUtf8());
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -37,14 +37,14 @@ HandlerResponse WSRequestHandler::HandleSetCurrentSceneCollection(WSRequestHandl
* @category scene collections * @category scene collections
* @since 4.0.0 * @since 4.0.0
*/ */
HandlerResponse WSRequestHandler::HandleGetCurrentSceneCollection(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetCurrentSceneCollection(const RpcRequest& request) {
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
char* sceneCollection = obs_frontend_get_current_scene_collection(); char* sceneCollection = obs_frontend_get_current_scene_collection();
obs_data_set_string(response, "sc-name", sceneCollection); obs_data_set_string(response, "sc-name", sceneCollection);
bfree(sceneCollection); bfree(sceneCollection);
return req->SendOKResponse(response); return request.success(response);
} }
/** /**
@ -57,7 +57,7 @@ HandlerResponse WSRequestHandler::HandleGetCurrentSceneCollection(WSRequestHandl
* @category scene collections * @category scene collections
* @since 4.0.0 * @since 4.0.0
*/ */
HandlerResponse WSRequestHandler::HandleListSceneCollections(WSRequestHandler* req) { RpcResponse WSRequestHandler::ListSceneCollections(const RpcRequest& request) {
char** sceneCollections = obs_frontend_get_scene_collections(); char** sceneCollections = obs_frontend_get_scene_collections();
OBSDataArrayAutoRelease list = OBSDataArrayAutoRelease list =
Utils::StringListToArray(sceneCollections, "sc-name"); Utils::StringListToArray(sceneCollections, "sc-name");
@ -66,5 +66,5 @@ HandlerResponse WSRequestHandler::HandleListSceneCollections(WSRequestHandler* r
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_array(response, "scene-collections", list); obs_data_set_array(response, "scene-collections", list);
return req->SendOKResponse(response); return request.success(response);
} }

View File

@ -38,31 +38,31 @@
* @category scene items * @category scene items
* @since 4.3.0 * @since 4.3.0
*/ */
HandlerResponse WSRequestHandler::HandleGetSceneItemProperties(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetSceneItemProperties(const RpcRequest& request) {
if (!req->hasField("item")) { if (!request.hasField("item")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
QString itemName = obs_data_get_string(req->data, "item"); QString itemName = obs_data_get_string(request.parameters(), "item");
if (itemName.isEmpty()) { if (itemName.isEmpty()) {
return req->SendErrorResponse("invalid request parameters"); return request.failed("invalid request parameters");
} }
QString sceneName = obs_data_get_string(req->data, "scene-name"); QString sceneName = obs_data_get_string(request.parameters(), "scene-name");
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
if (!scene) { if (!scene) {
return req->SendErrorResponse("requested scene doesn't exist"); return request.failed("requested scene doesn't exist");
} }
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName); OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName);
if (!sceneItem) { if (!sceneItem) {
return req->SendErrorResponse("specified scene item doesn't exist"); return request.failed("specified scene item doesn't exist");
} }
OBSDataAutoRelease data = Utils::GetSceneItemPropertiesData(sceneItem); OBSDataAutoRelease data = Utils::GetSceneItemPropertiesData(sceneItem);
obs_data_set_string(data, "name", itemName.toUtf8()); obs_data_set_string(data, "name", itemName.toUtf8());
return req->SendOKResponse(data); return request.success(data);
} }
/** /**
@ -93,38 +93,38 @@ HandlerResponse WSRequestHandler::HandleGetSceneItemProperties(WSRequestHandler*
* @category scene items * @category scene items
* @since 4.3.0 * @since 4.3.0
*/ */
HandlerResponse WSRequestHandler::HandleSetSceneItemProperties(WSRequestHandler* req) { RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request) {
if (!req->hasField("item")) { if (!request.hasField("item")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
QString itemName = obs_data_get_string(req->data, "item"); QString itemName = obs_data_get_string(request.parameters(), "item");
if (itemName.isEmpty()) { if (itemName.isEmpty()) {
return req->SendErrorResponse("invalid request parameters"); return request.failed("invalid request parameters");
} }
QString sceneName = obs_data_get_string(req->data, "scene-name"); QString sceneName = obs_data_get_string(request.parameters(), "scene-name");
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
if (!scene) { if (!scene) {
return req->SendErrorResponse("requested scene doesn't exist"); return request.failed("requested scene doesn't exist");
} }
OBSSceneItemAutoRelease sceneItem = OBSSceneItemAutoRelease sceneItem =
Utils::GetSceneItemFromName(scene, itemName); Utils::GetSceneItemFromName(scene, itemName);
if (!sceneItem) { if (!sceneItem) {
return req->SendErrorResponse("specified scene item doesn't exist"); return request.failed("specified scene item doesn't exist");
} }
bool badRequest = false; bool badRequest = false;
OBSDataAutoRelease errorMessage = obs_data_create(); OBSDataAutoRelease errorData = obs_data_create();
obs_sceneitem_defer_update_begin(sceneItem); obs_sceneitem_defer_update_begin(sceneItem);
if (req->hasField("position")) { if (request.hasField("position")) {
vec2 oldPosition; vec2 oldPosition;
OBSDataAutoRelease positionError = obs_data_create(); OBSDataAutoRelease positionError = obs_data_create();
obs_sceneitem_get_pos(sceneItem, &oldPosition); obs_sceneitem_get_pos(sceneItem, &oldPosition);
OBSDataAutoRelease reqPosition = obs_data_get_obj(req->data, "position"); OBSDataAutoRelease reqPosition = obs_data_get_obj(request.parameters(), "position");
vec2 newPosition = oldPosition; vec2 newPosition = oldPosition;
if (obs_data_has_user_value(reqPosition, "x")) { if (obs_data_has_user_value(reqPosition, "x")) {
newPosition.x = obs_data_get_int(reqPosition, "x"); newPosition.x = obs_data_get_int(reqPosition, "x");
@ -140,20 +140,20 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemProperties(WSRequestHandler*
else { else {
badRequest = true; badRequest = true;
obs_data_set_string(positionError, "alignment", "invalid"); obs_data_set_string(positionError, "alignment", "invalid");
obs_data_set_obj(errorMessage, "position", positionError); obs_data_set_obj(errorData, "position", positionError);
} }
} }
obs_sceneitem_set_pos(sceneItem, &newPosition); obs_sceneitem_set_pos(sceneItem, &newPosition);
} }
if (req->hasField("rotation")) { if (request.hasField("rotation")) {
obs_sceneitem_set_rot(sceneItem, (float)obs_data_get_double(req->data, "rotation")); obs_sceneitem_set_rot(sceneItem, (float)obs_data_get_double(request.parameters(), "rotation"));
} }
if (req->hasField("scale")) { if (request.hasField("scale")) {
vec2 oldScale; vec2 oldScale;
obs_sceneitem_get_scale(sceneItem, &oldScale); obs_sceneitem_get_scale(sceneItem, &oldScale);
OBSDataAutoRelease reqScale = obs_data_get_obj(req->data, "scale"); OBSDataAutoRelease reqScale = obs_data_get_obj(request.parameters(), "scale");
vec2 newScale = oldScale; vec2 newScale = oldScale;
if (obs_data_has_user_value(reqScale, "x")) { if (obs_data_has_user_value(reqScale, "x")) {
newScale.x = obs_data_get_double(reqScale, "x"); newScale.x = obs_data_get_double(reqScale, "x");
@ -164,10 +164,10 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemProperties(WSRequestHandler*
obs_sceneitem_set_scale(sceneItem, &newScale); obs_sceneitem_set_scale(sceneItem, &newScale);
} }
if (req->hasField("crop")) { if (request.hasField("crop")) {
obs_sceneitem_crop oldCrop; obs_sceneitem_crop oldCrop;
obs_sceneitem_get_crop(sceneItem, &oldCrop); obs_sceneitem_get_crop(sceneItem, &oldCrop);
OBSDataAutoRelease reqCrop = obs_data_get_obj(req->data, "crop"); OBSDataAutoRelease reqCrop = obs_data_get_obj(request.parameters(), "crop");
obs_sceneitem_crop newCrop = oldCrop; obs_sceneitem_crop newCrop = oldCrop;
if (obs_data_has_user_value(reqCrop, "top")) { if (obs_data_has_user_value(reqCrop, "top")) {
newCrop.top = obs_data_get_int(reqCrop, "top"); newCrop.top = obs_data_get_int(reqCrop, "top");
@ -184,18 +184,18 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemProperties(WSRequestHandler*
obs_sceneitem_set_crop(sceneItem, &newCrop); obs_sceneitem_set_crop(sceneItem, &newCrop);
} }
if (req->hasField("visible")) { if (request.hasField("visible")) {
obs_sceneitem_set_visible(sceneItem, obs_data_get_bool(req->data, "visible")); obs_sceneitem_set_visible(sceneItem, obs_data_get_bool(request.parameters(), "visible"));
} }
if (req->hasField("locked")) { if (request.hasField("locked")) {
obs_sceneitem_set_locked(sceneItem, obs_data_get_bool(req->data, "locked")); obs_sceneitem_set_locked(sceneItem, obs_data_get_bool(request.parameters(), "locked"));
} }
if (req->hasField("bounds")) { if (request.hasField("bounds")) {
bool badBounds = false; bool badBounds = false;
OBSDataAutoRelease boundsError = obs_data_create(); OBSDataAutoRelease boundsError = obs_data_create();
OBSDataAutoRelease reqBounds = obs_data_get_obj(req->data, "bounds"); OBSDataAutoRelease reqBounds = obs_data_get_obj(request.parameters(), "bounds");
if (obs_data_has_user_value(reqBounds, "type")) { if (obs_data_has_user_value(reqBounds, "type")) {
QString newBoundsType = obs_data_get_string(reqBounds, "type"); QString newBoundsType = obs_data_get_string(reqBounds, "type");
if (newBoundsType == "OBS_BOUNDS_NONE") { if (newBoundsType == "OBS_BOUNDS_NONE") {
@ -245,17 +245,17 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemProperties(WSRequestHandler*
} }
} }
if (badBounds) { if (badBounds) {
obs_data_set_obj(errorMessage, "bounds", boundsError); obs_data_set_obj(errorData, "bounds", boundsError);
} }
} }
obs_sceneitem_defer_update_end(sceneItem); obs_sceneitem_defer_update_end(sceneItem);
if (badRequest) { if (badRequest) {
return req->SendErrorResponse(errorMessage); return request.failed("error", errorData);
} }
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -269,27 +269,27 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemProperties(WSRequestHandler*
* @category scene items * @category scene items
* @since 4.2.0 * @since 4.2.0
*/ */
HandlerResponse WSRequestHandler::HandleResetSceneItem(WSRequestHandler* req) { RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) {
// TODO: remove this request, or refactor it to ResetSource // TODO: remove this request, or refactor it to ResetSource
if (!req->hasField("item")) { if (!request.hasField("item")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
const char* itemName = obs_data_get_string(req->data, "item"); const char* itemName = obs_data_get_string(request.parameters(), "item");
if (!itemName) { if (!itemName) {
return req->SendErrorResponse("invalid request parameters"); return request.failed("invalid request parameters");
} }
const char* sceneName = obs_data_get_string(req->data, "scene-name"); const char* sceneName = obs_data_get_string(request.parameters(), "scene-name");
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
if (!scene) { if (!scene) {
return req->SendErrorResponse("requested scene doesn't exist"); return request.failed("requested scene doesn't exist");
} }
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName); OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName);
if (!sceneItem) { if (!sceneItem) {
return req->SendErrorResponse("specified scene item doesn't exist"); return request.failed("specified scene item doesn't exist");
} }
OBSSource sceneItemSource = obs_sceneitem_get_source(sceneItem); OBSSource sceneItemSource = obs_sceneitem_get_source(sceneItem);
@ -297,7 +297,7 @@ HandlerResponse WSRequestHandler::HandleResetSceneItem(WSRequestHandler* req) {
OBSDataAutoRelease settings = obs_source_get_settings(sceneItemSource); OBSDataAutoRelease settings = obs_source_get_settings(sceneItemSource);
obs_source_update(sceneItemSource, settings); obs_source_update(sceneItemSource, settings);
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -313,34 +313,34 @@ HandlerResponse WSRequestHandler::HandleResetSceneItem(WSRequestHandler* req) {
* @since 0.3 * @since 0.3
* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties. * @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties.
*/ */
HandlerResponse WSRequestHandler::HandleSetSceneItemRender(WSRequestHandler* req) { RpcResponse WSRequestHandler::SetSceneItemRender(const RpcRequest& request) {
if (!req->hasField("source") || if (!request.hasField("source") ||
!req->hasField("render")) !request.hasField("render"))
{ {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
const char* itemName = obs_data_get_string(req->data, "source"); const char* itemName = obs_data_get_string(request.parameters(), "source");
bool isVisible = obs_data_get_bool(req->data, "render"); bool isVisible = obs_data_get_bool(request.parameters(), "render");
if (!itemName) { if (!itemName) {
return req->SendErrorResponse("invalid request parameters"); return request.failed("invalid request parameters");
} }
const char* sceneName = obs_data_get_string(req->data, "scene-name"); const char* sceneName = obs_data_get_string(request.parameters(), "scene-name");
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
if (!scene) { if (!scene) {
return req->SendErrorResponse("requested scene doesn't exist"); return request.failed("requested scene doesn't exist");
} }
OBSSceneItemAutoRelease sceneItem = OBSSceneItemAutoRelease sceneItem =
Utils::GetSceneItemFromName(scene, itemName); Utils::GetSceneItemFromName(scene, itemName);
if (!sceneItem) { if (!sceneItem) {
return req->SendErrorResponse("specified scene item doesn't exist"); return request.failed("specified scene item doesn't exist");
} }
obs_sceneitem_set_visible(sceneItem, isVisible); obs_sceneitem_set_visible(sceneItem, isVisible);
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -358,34 +358,34 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemRender(WSRequestHandler* req
* @since 4.0.0 * @since 4.0.0
* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties. * @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties.
*/ */
HandlerResponse WSRequestHandler::HandleSetSceneItemPosition(WSRequestHandler* req) { RpcResponse WSRequestHandler::SetSceneItemPosition(const RpcRequest& request) {
if (!req->hasField("item") || if (!request.hasField("item") ||
!req->hasField("x") || !req->hasField("y")) { !request.hasField("x") || !request.hasField("y")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
QString itemName = obs_data_get_string(req->data, "item"); QString itemName = obs_data_get_string(request.parameters(), "item");
if (itemName.isEmpty()) { if (itemName.isEmpty()) {
return req->SendErrorResponse("invalid request parameters"); return request.failed("invalid request parameters");
} }
QString sceneName = obs_data_get_string(req->data, "scene-name"); QString sceneName = obs_data_get_string(request.parameters(), "scene-name");
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
if (!scene) { if (!scene) {
return req->SendErrorResponse("requested scene could not be found"); return request.failed("requested scene could not be found");
} }
OBSSceneItem sceneItem = Utils::GetSceneItemFromName(scene, itemName); OBSSceneItem sceneItem = Utils::GetSceneItemFromName(scene, itemName);
if (!sceneItem) { if (!sceneItem) {
return req->SendErrorResponse("specified scene item doesn't exist"); return request.failed("specified scene item doesn't exist");
} }
vec2 item_position = { 0 }; vec2 item_position = { 0 };
item_position.x = obs_data_get_double(req->data, "x"); item_position.x = obs_data_get_double(request.parameters(), "x");
item_position.y = obs_data_get_double(req->data, "y"); item_position.y = obs_data_get_double(request.parameters(), "y");
obs_sceneitem_set_pos(sceneItem, &item_position); obs_sceneitem_set_pos(sceneItem, &item_position);
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -403,34 +403,34 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemPosition(WSRequestHandler* r
* @since 4.0.0 * @since 4.0.0
* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties. * @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties.
*/ */
HandlerResponse WSRequestHandler::HandleSetSceneItemTransform(WSRequestHandler* req) { RpcResponse WSRequestHandler::SetSceneItemTransform(const RpcRequest& request) {
if (!req->hasField("item") || if (!request.hasField("item") ||
!req->hasField("x-scale") || !request.hasField("x-scale") ||
!req->hasField("y-scale") || !request.hasField("y-scale") ||
!req->hasField("rotation")) !request.hasField("rotation"))
{ {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
QString itemName = obs_data_get_string(req->data, "item"); QString itemName = obs_data_get_string(request.parameters(), "item");
if (itemName.isEmpty()) { if (itemName.isEmpty()) {
return req->SendErrorResponse("invalid request parameters"); return request.failed("invalid request parameters");
} }
QString sceneName = obs_data_get_string(req->data, "scene-name"); QString sceneName = obs_data_get_string(request.parameters(), "scene-name");
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
if (!scene) { if (!scene) {
return req->SendErrorResponse("requested scene doesn't exist"); return request.failed("requested scene doesn't exist");
} }
vec2 scale; vec2 scale;
scale.x = obs_data_get_double(req->data, "x-scale"); scale.x = obs_data_get_double(request.parameters(), "x-scale");
scale.y = obs_data_get_double(req->data, "y-scale"); scale.y = obs_data_get_double(request.parameters(), "y-scale");
float rotation = obs_data_get_double(req->data, "rotation"); float rotation = obs_data_get_double(request.parameters(), "rotation");
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName); OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName);
if (!sceneItem) { if (!sceneItem) {
return req->SendErrorResponse("specified scene item doesn't exist"); return request.failed("specified scene item doesn't exist");
} }
obs_sceneitem_defer_update_begin(sceneItem); obs_sceneitem_defer_update_begin(sceneItem);
@ -440,7 +440,7 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemTransform(WSRequestHandler*
obs_sceneitem_defer_update_end(sceneItem); obs_sceneitem_defer_update_end(sceneItem);
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -459,36 +459,36 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemTransform(WSRequestHandler*
* @since 4.1.0 * @since 4.1.0
* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties. * @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties.
*/ */
HandlerResponse WSRequestHandler::HandleSetSceneItemCrop(WSRequestHandler* req) { RpcResponse WSRequestHandler::SetSceneItemCrop(const RpcRequest& request) {
if (!req->hasField("item")) { if (!request.hasField("item")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
QString itemName = obs_data_get_string(req->data, "item"); QString itemName = obs_data_get_string(request.parameters(), "item");
if (itemName.isEmpty()) { if (itemName.isEmpty()) {
return req->SendErrorResponse("invalid request parameters"); return request.failed("invalid request parameters");
} }
QString sceneName = obs_data_get_string(req->data, "scene-name"); QString sceneName = obs_data_get_string(request.parameters(), "scene-name");
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
if (!scene) { if (!scene) {
return req->SendErrorResponse("requested scene doesn't exist"); return request.failed("requested scene doesn't exist");
} }
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName); OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName);
if (!sceneItem) { if (!sceneItem) {
return req->SendErrorResponse("specified scene item doesn't exist"); return request.failed("specified scene item doesn't exist");
} }
struct obs_sceneitem_crop crop = { 0 }; struct obs_sceneitem_crop crop = { 0 };
crop.top = obs_data_get_int(req->data, "top"); crop.top = obs_data_get_int(request.parameters(), "top");
crop.bottom = obs_data_get_int(req->data, "bottom"); crop.bottom = obs_data_get_int(request.parameters(), "bottom");
crop.left = obs_data_get_int(req->data, "left"); crop.left = obs_data_get_int(request.parameters(), "left");
crop.right = obs_data_get_int(req->data, "right"); crop.right = obs_data_get_int(request.parameters(), "right");
obs_sceneitem_set_crop(sceneItem, &crop); obs_sceneitem_set_crop(sceneItem, &crop);
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -504,38 +504,26 @@ HandlerResponse WSRequestHandler::HandleSetSceneItemCrop(WSRequestHandler* req)
* @category scene items * @category scene items
* @since 4.5.0 * @since 4.5.0
*/ */
HandlerResponse WSRequestHandler::HandleDeleteSceneItem(WSRequestHandler* req) { RpcResponse WSRequestHandler::DeleteSceneItem(const RpcRequest& request) {
if (!req->hasField("item")) { if (!request.hasField("item")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
const char* sceneName = obs_data_get_string(req->data, "scene"); const char* sceneName = obs_data_get_string(request.parameters(), "scene");
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
if (!scene) { if (!scene) {
return req->SendErrorResponse("requested scene doesn't exist"); return request.failed("requested scene doesn't exist");
} }
OBSDataAutoRelease item = obs_data_get_obj(req->data, "item"); OBSDataAutoRelease item = obs_data_get_obj(request.parameters(), "item");
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromItem(scene, item); OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromItem(scene, item);
if (!sceneItem) { if (!sceneItem) {
return req->SendErrorResponse("item with id/name combination not found in specified scene"); return request.failed("item with id/name combination not found in specified scene");
} }
obs_sceneitem_remove(sceneItem); obs_sceneitem_remove(sceneItem);
return req->SendOKResponse(); return request.success();
}
struct DuplicateSceneItemData {
obs_sceneitem_t *referenceItem;
obs_source_t *fromSource;
obs_sceneitem_t *newItem;
};
static void DuplicateSceneItem(void *_data, obs_scene_t *scene) {
DuplicateSceneItemData *data = (DuplicateSceneItemData *)_data;
data->newItem = obs_scene_add(scene, data->fromSource);
obs_sceneitem_set_visible(data->newItem, obs_sceneitem_visible(data->referenceItem));
} }
/** /**
@ -557,27 +545,33 @@ static void DuplicateSceneItem(void *_data, obs_scene_t *scene) {
* @category scene items * @category scene items
* @since 4.5.0 * @since 4.5.0
*/ */
HandlerResponse WSRequestHandler::HandleDuplicateSceneItem(WSRequestHandler* req) { RpcResponse WSRequestHandler::DuplicateSceneItem(const RpcRequest& request) {
if (!req->hasField("item")) { struct DuplicateSceneItemData {
return req->SendErrorResponse("missing request parameters"); obs_sceneitem_t *referenceItem;
obs_source_t *fromSource;
obs_sceneitem_t *newItem;
};
if (!request.hasField("item")) {
return request.failed("missing request parameters");
} }
const char* fromSceneName = obs_data_get_string(req->data, "fromScene"); const char* fromSceneName = obs_data_get_string(request.parameters(), "fromScene");
OBSScene fromScene = Utils::GetSceneFromNameOrCurrent(fromSceneName); OBSScene fromScene = Utils::GetSceneFromNameOrCurrent(fromSceneName);
if (!fromScene) { if (!fromScene) {
return req->SendErrorResponse("requested fromScene doesn't exist"); return request.failed("requested fromScene doesn't exist");
} }
const char* toSceneName = obs_data_get_string(req->data, "toScene"); const char* toSceneName = obs_data_get_string(request.parameters(), "toScene");
OBSScene toScene = Utils::GetSceneFromNameOrCurrent(toSceneName); OBSScene toScene = Utils::GetSceneFromNameOrCurrent(toSceneName);
if (!toScene) { if (!toScene) {
return req->SendErrorResponse("requested toScene doesn't exist"); return request.failed("requested toScene doesn't exist");
} }
OBSDataAutoRelease item = obs_data_get_obj(req->data, "item"); OBSDataAutoRelease item = obs_data_get_obj(request.parameters(), "item");
OBSSceneItemAutoRelease referenceItem = Utils::GetSceneItemFromItem(fromScene, item); OBSSceneItemAutoRelease referenceItem = Utils::GetSceneItemFromItem(fromScene, item);
if (!referenceItem) { if (!referenceItem) {
return req->SendErrorResponse("item with id/name combination not found in specified scene"); return request.failed("item with id/name combination not found in specified scene");
} }
DuplicateSceneItemData data; DuplicateSceneItemData data;
@ -585,12 +579,16 @@ HandlerResponse WSRequestHandler::HandleDuplicateSceneItem(WSRequestHandler* req
data.referenceItem = referenceItem; data.referenceItem = referenceItem;
obs_enter_graphics(); obs_enter_graphics();
obs_scene_atomic_update(toScene, DuplicateSceneItem, &data); obs_scene_atomic_update(toScene, [](void *_data, obs_scene_t *scene) {
auto data = reinterpret_cast<DuplicateSceneItemData*>(_data);
data->newItem = obs_scene_add(scene, data->fromSource);
obs_sceneitem_set_visible(data->newItem, obs_sceneitem_visible(data->referenceItem));
}, &data);
obs_leave_graphics(); obs_leave_graphics();
obs_sceneitem_t *newItem = data.newItem; obs_sceneitem_t *newItem = data.newItem;
if (!newItem) { if (!newItem) {
return req->SendErrorResponse("Error duplicating scene item"); return request.failed("Error duplicating scene item");
} }
OBSDataAutoRelease itemData = obs_data_create(); OBSDataAutoRelease itemData = obs_data_create();
@ -601,5 +599,5 @@ HandlerResponse WSRequestHandler::HandleDuplicateSceneItem(WSRequestHandler* req
obs_data_set_obj(responseData, "item", itemData); obs_data_set_obj(responseData, "item", itemData);
obs_data_set_string(responseData, "scene", obs_source_get_name(obs_scene_get_source(toScene))); obs_data_set_string(responseData, "scene", obs_source_get_name(obs_scene_get_source(toScene)));
return req->SendOKResponse(responseData); return request.success(responseData);
} }

View File

@ -18,19 +18,19 @@
* @category scenes * @category scenes
* @since 0.3 * @since 0.3
*/ */
HandlerResponse WSRequestHandler::HandleSetCurrentScene(WSRequestHandler* req) { RpcResponse WSRequestHandler::SetCurrentScene(const RpcRequest& request) {
if (!req->hasField("scene-name")) { if (!request.hasField("scene-name")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
const char* sceneName = obs_data_get_string(req->data, "scene-name"); const char* sceneName = obs_data_get_string(request.parameters(), "scene-name");
OBSSourceAutoRelease source = obs_get_source_by_name(sceneName); OBSSourceAutoRelease source = obs_get_source_by_name(sceneName);
if (source) { if (source) {
obs_frontend_set_current_scene(source); obs_frontend_set_current_scene(source);
return req->SendOKResponse(); return request.success();
} else { } else {
return req->SendErrorResponse("requested scene does not exist"); return request.failed("requested scene does not exist");
} }
} }
@ -45,7 +45,7 @@ HandlerResponse WSRequestHandler::HandleSetCurrentScene(WSRequestHandler* req) {
* @category scenes * @category scenes
* @since 0.3 * @since 0.3
*/ */
HandlerResponse WSRequestHandler::HandleGetCurrentScene(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetCurrentScene(const RpcRequest& request) {
OBSSourceAutoRelease currentScene = obs_frontend_get_current_scene(); OBSSourceAutoRelease currentScene = obs_frontend_get_current_scene();
OBSDataArrayAutoRelease sceneItems = Utils::GetSceneItems(currentScene); OBSDataArrayAutoRelease sceneItems = Utils::GetSceneItems(currentScene);
@ -53,7 +53,7 @@ HandlerResponse WSRequestHandler::HandleGetCurrentScene(WSRequestHandler* req) {
obs_data_set_string(data, "name", obs_source_get_name(currentScene)); obs_data_set_string(data, "name", obs_source_get_name(currentScene));
obs_data_set_array(data, "sources", sceneItems); obs_data_set_array(data, "sources", sceneItems);
return req->SendOKResponse(data); return request.success(data);
} }
/** /**
@ -67,7 +67,7 @@ HandlerResponse WSRequestHandler::HandleGetCurrentScene(WSRequestHandler* req) {
* @category scenes * @category scenes
* @since 0.3 * @since 0.3
*/ */
HandlerResponse WSRequestHandler::HandleGetSceneList(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetSceneList(const RpcRequest& request) {
OBSSourceAutoRelease currentScene = obs_frontend_get_current_scene(); OBSSourceAutoRelease currentScene = obs_frontend_get_current_scene();
OBSDataArrayAutoRelease scenes = Utils::GetScenes(); OBSDataArrayAutoRelease scenes = Utils::GetScenes();
@ -76,7 +76,7 @@ HandlerResponse WSRequestHandler::HandleGetSceneList(WSRequestHandler* req) {
obs_source_get_name(currentScene)); obs_source_get_name(currentScene));
obs_data_set_array(data, "scenes", scenes); obs_data_set_array(data, "scenes", scenes);
return req->SendOKResponse(data); return request.success(data);
} }
/** /**
@ -92,16 +92,16 @@ HandlerResponse WSRequestHandler::HandleGetSceneList(WSRequestHandler* req) {
* @category scenes * @category scenes
* @since 4.5.0 * @since 4.5.0
*/ */
HandlerResponse WSRequestHandler::HandleReorderSceneItems(WSRequestHandler* req) { RpcResponse WSRequestHandler::ReorderSceneItems(const RpcRequest& request) {
QString sceneName = obs_data_get_string(req->data, "scene"); QString sceneName = obs_data_get_string(request.parameters(), "scene");
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
if (!scene) { if (!scene) {
return req->SendErrorResponse("requested scene doesn't exist"); return request.failed("requested scene doesn't exist");
} }
OBSDataArrayAutoRelease items = obs_data_get_array(req->data, "items"); OBSDataArrayAutoRelease items = obs_data_get_array(request.parameters(), "items");
if (!items) { if (!items) {
return req->SendErrorResponse("sceneItem order not specified"); return request.failed("sceneItem order not specified");
} }
struct reorder_context { struct reorder_context {
@ -143,8 +143,8 @@ HandlerResponse WSRequestHandler::HandleReorderSceneItems(WSRequestHandler* req)
}, &ctx); }, &ctx);
if (!ctx.success) { if (!ctx.success) {
return req->SendErrorResponse(ctx.errorMessage); return request.failed(ctx.errorMessage);
} }
return req->SendOKResponse(); return request.success();
} }

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,7 @@
* @category streaming * @category streaming
* @since 0.3 * @since 0.3
*/ */
HandlerResponse WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetStreamingStatus(const RpcRequest& request) {
auto events = GetEventsSystem(); auto events = GetEventsSystem();
OBSDataAutoRelease data = obs_data_create(); OBSDataAutoRelease data = obs_data_create();
@ -39,7 +39,7 @@ HandlerResponse WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler* req
obs_data_set_string(data, "rec-timecode", recordingTimecode.toUtf8().constData()); obs_data_set_string(data, "rec-timecode", recordingTimecode.toUtf8().constData());
} }
return req->SendOKResponse(data); return request.success(data);
} }
/** /**
@ -50,11 +50,11 @@ HandlerResponse WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler* req
* @category streaming * @category streaming
* @since 0.3 * @since 0.3
*/ */
HandlerResponse WSRequestHandler::HandleStartStopStreaming(WSRequestHandler* req) { RpcResponse WSRequestHandler::StartStopStreaming(const RpcRequest& request) {
if (obs_frontend_streaming_active()) if (obs_frontend_streaming_active())
return HandleStopStreaming(req); return StopStreaming(request);
else else
return HandleStartStreaming(req); return StartStreaming(request);
} }
/** /**
@ -76,15 +76,15 @@ HandlerResponse WSRequestHandler::HandleStartStopStreaming(WSRequestHandler* req
* @category streaming * @category streaming
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleStartStreaming(WSRequestHandler* req) { RpcResponse WSRequestHandler::StartStreaming(const RpcRequest& request) {
if (obs_frontend_streaming_active() == false) { if (obs_frontend_streaming_active() == false) {
OBSService configuredService = obs_frontend_get_streaming_service(); OBSService configuredService = obs_frontend_get_streaming_service();
OBSService newService = nullptr; OBSService newService = nullptr;
// TODO: fix service memory leak // TODO: fix service memory leak
if (req->hasField("stream")) { if (request.hasField("stream")) {
OBSDataAutoRelease streamData = obs_data_get_obj(req->data, "stream"); OBSDataAutoRelease streamData = obs_data_get_obj(request.parameters(), "stream");
OBSDataAutoRelease newSettings = obs_data_get_obj(streamData, "settings"); OBSDataAutoRelease newSettings = obs_data_get_obj(streamData, "settings");
OBSDataAutoRelease newMetadata = obs_data_get_obj(streamData, "metadata"); OBSDataAutoRelease newMetadata = obs_data_get_obj(streamData, "metadata");
@ -157,9 +157,9 @@ HandlerResponse WSRequestHandler::HandleStartStreaming(WSRequestHandler* req) {
obs_frontend_set_streaming_service(configuredService); obs_frontend_set_streaming_service(configuredService);
} }
return req->SendOKResponse(); return request.success();
} else { } else {
return req->SendErrorResponse("streaming already active"); return request.failed("streaming already active");
} }
} }
@ -172,12 +172,12 @@ HandlerResponse WSRequestHandler::HandleStartStreaming(WSRequestHandler* req) {
* @category streaming * @category streaming
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleStopStreaming(WSRequestHandler* req) { RpcResponse WSRequestHandler::StopStreaming(const RpcRequest& request) {
if (obs_frontend_streaming_active() == true) { if (obs_frontend_streaming_active() == true) {
obs_frontend_streaming_stop(); obs_frontend_streaming_stop();
return req->SendOKResponse(); return request.success();
} else { } else {
return req->SendErrorResponse("streaming not active"); return request.failed("streaming not active");
} }
} }
@ -198,16 +198,16 @@ HandlerResponse WSRequestHandler::HandleStopStreaming(WSRequestHandler* req) {
* @category streaming * @category streaming
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleSetStreamSettings(WSRequestHandler* req) { RpcResponse WSRequestHandler::SetStreamSettings(const RpcRequest& request) {
OBSService service = obs_frontend_get_streaming_service(); OBSService service = obs_frontend_get_streaming_service();
OBSDataAutoRelease requestSettings = obs_data_get_obj(req->data, "settings"); OBSDataAutoRelease requestSettings = obs_data_get_obj(request.parameters(), "settings");
if (!requestSettings) { if (!requestSettings) {
return req->SendErrorResponse("'settings' are required'"); return request.failed("'settings' are required'");
} }
QString serviceType = obs_service_get_type(service); QString serviceType = obs_service_get_type(service);
QString requestedType = obs_data_get_string(req->data, "type"); QString requestedType = obs_data_get_string(request.parameters(), "type");
if (requestedType != nullptr && requestedType != serviceType) { if (requestedType != nullptr && requestedType != serviceType) {
OBSDataAutoRelease hotkeys = obs_hotkeys_save_service(service); OBSDataAutoRelease hotkeys = obs_hotkeys_save_service(service);
@ -231,7 +231,7 @@ HandlerResponse WSRequestHandler::HandleSetStreamSettings(WSRequestHandler* req)
} }
//if save is specified we should immediately save the streaming service //if save is specified we should immediately save the streaming service
if (obs_data_get_bool(req->data, "save")) { if (obs_data_get_bool(request.parameters(), "save")) {
obs_frontend_save_streaming_service(); obs_frontend_save_streaming_service();
} }
@ -241,7 +241,7 @@ HandlerResponse WSRequestHandler::HandleSetStreamSettings(WSRequestHandler* req)
obs_data_set_string(response, "type", requestedType.toUtf8()); obs_data_set_string(response, "type", requestedType.toUtf8());
obs_data_set_obj(response, "settings", serviceSettings); obs_data_set_obj(response, "settings", serviceSettings);
return req->SendOKResponse(response); return request.success(response);
} }
/** /**
@ -260,7 +260,7 @@ HandlerResponse WSRequestHandler::HandleSetStreamSettings(WSRequestHandler* req)
* @category streaming * @category streaming
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleGetStreamSettings(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetStreamSettings(const RpcRequest& request) {
OBSService service = obs_frontend_get_streaming_service(); OBSService service = obs_frontend_get_streaming_service();
const char* serviceType = obs_service_get_type(service); const char* serviceType = obs_service_get_type(service);
@ -270,7 +270,7 @@ HandlerResponse WSRequestHandler::HandleGetStreamSettings(WSRequestHandler* req)
obs_data_set_string(response, "type", serviceType); obs_data_set_string(response, "type", serviceType);
obs_data_set_obj(response, "settings", settings); obs_data_set_obj(response, "settings", settings);
return req->SendOKResponse(response); return request.success(response);
} }
/** /**
@ -281,9 +281,9 @@ HandlerResponse WSRequestHandler::HandleGetStreamSettings(WSRequestHandler* req)
* @category streaming * @category streaming
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleSaveStreamSettings(WSRequestHandler* req) { RpcResponse WSRequestHandler::SaveStreamSettings(const RpcRequest& request) {
obs_frontend_save_streaming_service(); obs_frontend_save_streaming_service();
return req->SendOKResponse(); return request.success();
} }
@ -299,19 +299,19 @@ HandlerResponse WSRequestHandler::HandleSaveStreamSettings(WSRequestHandler* req
* @since 4.6.0 * @since 4.6.0
*/ */
#if BUILD_CAPTIONS #if BUILD_CAPTIONS
HandlerResponse WSRequestHandler::HandleSendCaptions(WSRequestHandler* req) { RpcResponse WSRequestHandler::SendCaptions(const RpcRequest& request) {
if (!req->hasField("text")) { if (!request.hasField("text")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
OBSOutputAutoRelease output = obs_frontend_get_streaming_output(); OBSOutputAutoRelease output = obs_frontend_get_streaming_output();
if (output) { if (output) {
const char* caption = obs_data_get_string(req->data, "text"); const char* caption = obs_data_get_string(request.parameters(), "text");
// Send caption text with immediately (0 second delay) // Send caption text with immediately (0 second delay)
obs_output_output_caption_text2(output, caption, 0.0); obs_output_output_caption_text2(output, caption, 0.0);
} }
return req->SendOKResponse(); return request.success();
} }
#endif #endif

View File

@ -12,13 +12,13 @@
* @category studio mode * @category studio mode
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleGetStudioModeStatus(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetStudioModeStatus(const RpcRequest& request) {
bool previewActive = obs_frontend_preview_program_mode_active(); bool previewActive = obs_frontend_preview_program_mode_active();
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_bool(response, "studio-mode", previewActive); obs_data_set_bool(response, "studio-mode", previewActive);
return req->SendOKResponse(response); return request.success(response);
} }
/** /**
@ -33,9 +33,9 @@ HandlerResponse WSRequestHandler::HandleGetStudioModeStatus(WSRequestHandler* re
* @category studio mode * @category studio mode
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleGetPreviewScene(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetPreviewScene(const RpcRequest& request) {
if (!obs_frontend_preview_program_mode_active()) { if (!obs_frontend_preview_program_mode_active()) {
return req->SendErrorResponse("studio mode not enabled"); return request.failed("studio mode not enabled");
} }
OBSSourceAutoRelease scene = obs_frontend_get_current_preview_scene(); OBSSourceAutoRelease scene = obs_frontend_get_current_preview_scene();
@ -45,7 +45,7 @@ HandlerResponse WSRequestHandler::HandleGetPreviewScene(WSRequestHandler* req) {
obs_data_set_string(data, "name", obs_source_get_name(scene)); obs_data_set_string(data, "name", obs_source_get_name(scene));
obs_data_set_array(data, "sources", sceneItems); obs_data_set_array(data, "sources", sceneItems);
return req->SendOKResponse(data); return request.success(data);
} }
/** /**
@ -59,23 +59,23 @@ HandlerResponse WSRequestHandler::HandleGetPreviewScene(WSRequestHandler* req) {
* @category studio mode * @category studio mode
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleSetPreviewScene(WSRequestHandler* req) { RpcResponse WSRequestHandler::SetPreviewScene(const RpcRequest& request) {
if (!obs_frontend_preview_program_mode_active()) { if (!obs_frontend_preview_program_mode_active()) {
return req->SendErrorResponse("studio mode not enabled"); return request.failed("studio mode not enabled");
} }
if (!req->hasField("scene-name")) { if (!request.hasField("scene-name")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
const char* scene_name = obs_data_get_string(req->data, "scene-name"); const char* scene_name = obs_data_get_string(request.parameters(), "scene-name");
OBSScene scene = Utils::GetSceneFromNameOrCurrent(scene_name); OBSScene scene = Utils::GetSceneFromNameOrCurrent(scene_name);
if (!scene) { if (!scene) {
return req->SendErrorResponse("specified scene doesn't exist"); return request.failed("specified scene doesn't exist");
} }
obs_frontend_set_current_preview_scene(obs_scene_get_source(scene)); obs_frontend_set_current_preview_scene(obs_scene_get_source(scene));
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -91,25 +91,25 @@ HandlerResponse WSRequestHandler::HandleSetPreviewScene(WSRequestHandler* req) {
* @category studio mode * @category studio mode
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleTransitionToProgram(WSRequestHandler* req) { RpcResponse WSRequestHandler::TransitionToProgram(const RpcRequest& request) {
if (!obs_frontend_preview_program_mode_active()) { if (!obs_frontend_preview_program_mode_active()) {
return req->SendErrorResponse("studio mode not enabled"); return request.failed("studio mode not enabled");
} }
if (req->hasField("with-transition")) { if (request.hasField("with-transition")) {
OBSDataAutoRelease transitionInfo = OBSDataAutoRelease transitionInfo =
obs_data_get_obj(req->data, "with-transition"); obs_data_get_obj(request.parameters(), "with-transition");
if (obs_data_has_user_value(transitionInfo, "name")) { if (obs_data_has_user_value(transitionInfo, "name")) {
QString transitionName = QString transitionName =
obs_data_get_string(transitionInfo, "name"); obs_data_get_string(transitionInfo, "name");
if (transitionName.isEmpty()) { if (transitionName.isEmpty()) {
return req->SendErrorResponse("invalid request parameters"); return request.failed("invalid request parameters");
} }
bool success = Utils::SetTransitionByName(transitionName); bool success = Utils::SetTransitionByName(transitionName);
if (!success) { if (!success) {
return req->SendErrorResponse("specified transition doesn't exist"); return request.failed("specified transition doesn't exist");
} }
} }
@ -121,7 +121,7 @@ HandlerResponse WSRequestHandler::HandleTransitionToProgram(WSRequestHandler* re
} }
obs_frontend_preview_program_trigger_transition(); obs_frontend_preview_program_trigger_transition();
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -132,9 +132,9 @@ HandlerResponse WSRequestHandler::HandleTransitionToProgram(WSRequestHandler* re
* @category studio mode * @category studio mode
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleEnableStudioMode(WSRequestHandler* req) { RpcResponse WSRequestHandler::EnableStudioMode(const RpcRequest& request) {
obs_frontend_set_preview_program_mode(true); obs_frontend_set_preview_program_mode(true);
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -145,9 +145,9 @@ HandlerResponse WSRequestHandler::HandleEnableStudioMode(WSRequestHandler* req)
* @category studio mode * @category studio mode
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleDisableStudioMode(WSRequestHandler* req) { RpcResponse WSRequestHandler::DisableStudioMode(const RpcRequest& request) {
obs_frontend_set_preview_program_mode(false); obs_frontend_set_preview_program_mode(false);
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -158,8 +158,8 @@ HandlerResponse WSRequestHandler::HandleDisableStudioMode(WSRequestHandler* req)
* @category studio mode * @category studio mode
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleToggleStudioMode(WSRequestHandler* req) { RpcResponse WSRequestHandler::ToggleStudioMode(const RpcRequest& request) {
bool previewProgramMode = obs_frontend_preview_program_mode_active(); bool previewProgramMode = obs_frontend_preview_program_mode_active();
obs_frontend_set_preview_program_mode(!previewProgramMode); obs_frontend_set_preview_program_mode(!previewProgramMode);
return req->SendOKResponse(); return request.success();
} }

View File

@ -14,7 +14,7 @@
* @category transitions * @category transitions
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleGetTransitionList(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetTransitionList(const RpcRequest& request) {
OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition(); OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition();
obs_frontend_source_list transitionList = {}; obs_frontend_source_list transitionList = {};
obs_frontend_get_transitions(&transitionList); obs_frontend_get_transitions(&transitionList);
@ -34,7 +34,7 @@ HandlerResponse WSRequestHandler::HandleGetTransitionList(WSRequestHandler* req)
obs_source_get_name(currentTransition)); obs_source_get_name(currentTransition));
obs_data_set_array(response, "transitions", transitions); obs_data_set_array(response, "transitions", transitions);
return req->SendOKResponse(response); return request.success(response);
} }
/** /**
@ -48,7 +48,7 @@ HandlerResponse WSRequestHandler::HandleGetTransitionList(WSRequestHandler* req)
* @category transitions * @category transitions
* @since 0.3 * @since 0.3
*/ */
HandlerResponse WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetCurrentTransition(const RpcRequest& request) {
OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition(); OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition();
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
@ -58,7 +58,7 @@ HandlerResponse WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler* r
if (!obs_transition_fixed(currentTransition)) if (!obs_transition_fixed(currentTransition))
obs_data_set_int(response, "duration", obs_frontend_get_transition_duration()); obs_data_set_int(response, "duration", obs_frontend_get_transition_duration());
return req->SendOKResponse(response); return request.success(response);
} }
/** /**
@ -71,18 +71,18 @@ HandlerResponse WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler* r
* @category transitions * @category transitions
* @since 0.3 * @since 0.3
*/ */
HandlerResponse WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler* req) { RpcResponse WSRequestHandler::SetCurrentTransition(const RpcRequest& request) {
if (!req->hasField("transition-name")) { if (!request.hasField("transition-name")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
QString name = obs_data_get_string(req->data, "transition-name"); QString name = obs_data_get_string(request.parameters(), "transition-name");
bool success = Utils::SetTransitionByName(name); bool success = Utils::SetTransitionByName(name);
if (!success) { if (!success) {
return req->SendErrorResponse("requested transition does not exist"); return request.failed("requested transition does not exist");
} }
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -95,14 +95,14 @@ HandlerResponse WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler* r
* @category transitions * @category transitions
* @since 4.0.0 * @since 4.0.0
*/ */
HandlerResponse WSRequestHandler::HandleSetTransitionDuration(WSRequestHandler* req) { RpcResponse WSRequestHandler::SetTransitionDuration(const RpcRequest& request) {
if (!req->hasField("duration")) { if (!request.hasField("duration")) {
return req->SendErrorResponse("missing request parameters"); return request.failed("missing request parameters");
} }
int ms = obs_data_get_int(req->data, "duration"); int ms = obs_data_get_int(request.parameters(), "duration");
obs_frontend_set_transition_duration(ms); obs_frontend_set_transition_duration(ms);
return req->SendOKResponse(); return request.success();
} }
/** /**
@ -115,8 +115,8 @@ HandlerResponse WSRequestHandler::HandleSetTransitionDuration(WSRequestHandler*
* @category transitions * @category transitions
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleGetTransitionDuration(WSRequestHandler* req) { RpcResponse WSRequestHandler::GetTransitionDuration(const RpcRequest& request) {
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_int(response, "transition-duration", obs_frontend_get_transition_duration()); obs_data_set_int(response, "transition-duration", obs_frontend_get_transition_duration());
return req->SendOKResponse(response); return request.success(response);
} }

View File

@ -31,6 +31,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "obs-websocket.h" #include "obs-websocket.h"
#include "Config.h" #include "Config.h"
#include "Utils.h" #include "Utils.h"
#include "protocol/OBSRemoteProtocol.h"
QT_USE_NAMESPACE QT_USE_NAMESPACE
@ -124,8 +125,15 @@ void WSServer::stop()
blog(LOG_INFO, "server stopped successfully"); blog(LOG_INFO, "server stopped successfully");
} }
void WSServer::broadcast(std::string message) void WSServer::broadcast(const RpcEvent& event)
{ {
OBSRemoteProtocol protocol;
std::string message = protocol.encodeEvent(event);
if (GetConfig()->DebugEnabled) {
blog(LOG_INFO, "Update << '%s'", message.c_str());
}
QMutexLocker locker(&_clMutex); QMutexLocker locker(&_clMutex);
for (connection_hdl hdl : _connections) { for (connection_hdl hdl : _connections) {
if (GetConfig()->AuthRequired) { if (GetConfig()->AuthRequired) {
@ -171,8 +179,17 @@ void WSServer::onMessage(connection_hdl hdl, server::message_ptr message)
ConnectionProperties& connProperties = _connectionProperties[hdl]; ConnectionProperties& connProperties = _connectionProperties[hdl];
locker.unlock(); locker.unlock();
WSRequestHandler handler(connProperties); if (GetConfig()->DebugEnabled) {
std::string response = handler.processIncomingMessage(payload); blog(LOG_INFO, "Request >> '%s'", payload.c_str());
}
WSRequestHandler requestHandler(connProperties);
OBSRemoteProtocol protocol;
std::string response = protocol.processMessage(requestHandler, payload);
if (GetConfig()->DebugEnabled) {
blog(LOG_INFO, "Response << '%s'", response.c_str());
}
websocketpp::lib::error_code errorCode; websocketpp::lib::error_code errorCode;
_server.send(hdl, response, websocketpp::frame::opcode::text, errorCode); _server.send(hdl, response, websocketpp::frame::opcode::text, errorCode);

View File

@ -30,8 +30,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <websocketpp/server.hpp> #include <websocketpp/server.hpp>
#include "ConnectionProperties.h" #include "ConnectionProperties.h"
#include "WSRequestHandler.h" #include "WSRequestHandler.h"
#include "rpc/RpcEvent.h"
using websocketpp::connection_hdl; using websocketpp::connection_hdl;
@ -46,7 +46,7 @@ public:
virtual ~WSServer(); virtual ~WSServer();
void start(quint16 port); void start(quint16 port);
void stop(); void stop();
void broadcast(std::string message); void broadcast(const RpcEvent& event);
QThreadPool* threadPool() { QThreadPool* threadPool() {
return &_threadPool; return &_threadPool;
} }

View File

@ -0,0 +1,117 @@
/*
obs-websocket
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
*/
#include <inttypes.h>
#include "OBSRemoteProtocol.h"
#include "../WSRequestHandler.h"
#include "../rpc/RpcEvent.h"
#include "../Utils.h"
std::string OBSRemoteProtocol::processMessage(WSRequestHandler& requestHandler, std::string message)
{
std::string msgContainer(message);
const char* msg = msgContainer.c_str();
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");
}
if (!obs_data_has_user_value(data, "request-type") || !obs_data_has_user_value(data, "message-id")) {
return errorResponse(QString::Null(), "missing request parameters");
}
QString methodName = obs_data_get_string(data, "request-type");
QString messageId = obs_data_get_string(data, "message-id");
OBSDataAutoRelease params = obs_data_create();
obs_data_apply(params, data);
obs_data_unset_user_value(params, "request-type");
obs_data_unset_user_value(params, "message-id");
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();
}
std::string OBSRemoteProtocol::encodeEvent(const RpcEvent& event)
{
OBSDataAutoRelease eventData = obs_data_create();
QString updateType = event.updateType();
obs_data_set_string(eventData, "update-type", updateType.toUtf8().constData());
if (obs_frontend_streaming_active()) {
QString streamingTimecode = Utils::nsToTimestamp(event.streamTime());
obs_data_set_string(eventData, "stream-timecode", streamingTimecode.toUtf8().constData());
}
if (obs_frontend_recording_active()) {
QString recordingTimecode = Utils::nsToTimestamp(event.recordingTime());
obs_data_set_string(eventData, "rec-timecode", recordingTimecode.toUtf8().constData());
}
OBSData additionalFields = event.additionalFields();
if (additionalFields) {
obs_data_apply(eventData, additionalFields);
}
return std::string(obs_data_get_json(eventData));
}
std::string OBSRemoteProtocol::buildResponse(QString messageId, QString status, obs_data_t* fields)
{
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());
if (fields) {
obs_data_apply(response, fields);
}
std::string responseString = obs_data_get_json(response);
return responseString;
}
std::string OBSRemoteProtocol::successResponse(QString messageId, obs_data_t* fields)
{
return buildResponse(messageId, "ok", fields);
}
std::string OBSRemoteProtocol::errorResponse(QString messageId, QString 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());
return buildResponse(messageId, "error", fields);
}

View File

@ -0,0 +1,38 @@
/*
obs-websocket
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
*/
#pragma once
#include <string>
#include <obs-data.h>
#include <QtCore/QString>
class WSRequestHandler;
class RpcEvent;
class OBSRemoteProtocol
{
public:
std::string processMessage(WSRequestHandler& requestHandler, std::string message);
std::string encodeEvent(const RpcEvent& event);
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);
};

35
src/rpc/RpcEvent.cpp Normal file
View File

@ -0,0 +1,35 @@
/*
obs-websocket
Copyright (C) 2016-2020 Stéphane Lepin <stephane.lepin@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
*/
#include "RpcEvent.h"
RpcEvent::RpcEvent(
const QString& updateType,
uint64_t streamTime, uint64_t recordingTime,
obs_data_t* additionalFields
) :
_updateType(updateType),
_streamTime(streamTime),
_recordingTime(recordingTime),
_additionalFields(nullptr)
{
if (additionalFields) {
_additionalFields = obs_data_create();
obs_data_apply(_additionalFields, additionalFields);
}
}

60
src/rpc/RpcEvent.h Normal file
View File

@ -0,0 +1,60 @@
/*
obs-websocket
Copyright (C) 2016-2020 Stéphane Lepin <stephane.lepin@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
*/
#pragma once
#include <obs-data.h>
#include <QtCore/QString>
#include "../obs-websocket.h"
class RpcEvent
{
public:
explicit RpcEvent(
const QString& updateType,
uint64_t streamTime, uint64_t recordingTime,
obs_data_t* additionalFields = nullptr
);
const QString& updateType() const
{
return _updateType;
}
const uint64_t streamTime() const
{
return _streamTime;
}
const uint64_t recordingTime() const
{
return _recordingTime;
}
const OBSData additionalFields() const
{
return OBSData(_additionalFields);
}
private:
QString _updateType;
uint64_t _streamTime;
uint64_t _recordingTime;
OBSDataAutoRelease _additionalFields;
};

104
src/rpc/RpcRequest.cpp Normal file
View File

@ -0,0 +1,104 @@
/*
obs-websocket
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
*/
#include "RpcRequest.h"
#include "RpcResponse.h"
RpcRequest::RpcRequest(const QString& messageId, const QString& methodName, obs_data_t* params) :
_messageId(messageId),
_methodName(methodName),
_parameters(nullptr)
{
if (params) {
_parameters = obs_data_create();
obs_data_apply(_parameters, params);
}
}
const RpcResponse RpcRequest::success(obs_data_t* additionalFields) const
{
return RpcResponse::ok(*this, additionalFields);
}
const RpcResponse RpcRequest::failed(const QString& errorMessage, obs_data_t* additionalFields) const
{
return RpcResponse::fail(*this, errorMessage, additionalFields);
}
const bool RpcRequest::hasField(QString name, obs_data_type expectedFieldType, obs_data_number_type expectedNumberType) const
{
if (!_parameters || name.isEmpty() || name.isNull()) {
return false;
}
OBSDataItemAutoRelease dataItem = obs_data_item_byname(_parameters, name.toUtf8());
if (!dataItem) {
return false;
}
if (expectedFieldType != OBS_DATA_NULL) {
obs_data_type fieldType = obs_data_item_gettype(dataItem);
if (fieldType != expectedFieldType) {
return false;
}
if (fieldType == OBS_DATA_NUMBER && expectedNumberType != OBS_DATA_NUM_INVALID) {
obs_data_number_type numberType = obs_data_item_numtype(dataItem);
if (numberType != expectedNumberType) {
return false;
}
}
}
return true;
}
const bool RpcRequest::hasBool(QString fieldName) const
{
return this->hasField(fieldName, OBS_DATA_BOOLEAN);
}
const bool RpcRequest::hasString(QString fieldName) const
{
return this->hasField(fieldName, OBS_DATA_STRING);
}
const bool RpcRequest::hasNumber(QString fieldName, obs_data_number_type expectedNumberType) const
{
return this->hasField(fieldName, OBS_DATA_NUMBER, expectedNumberType);
}
const bool RpcRequest::hasInteger(QString fieldName) const
{
return this->hasNumber(fieldName, OBS_DATA_NUM_INT);
}
const bool RpcRequest::hasDouble(QString fieldName) const
{
return this->hasNumber(fieldName, OBS_DATA_NUM_DOUBLE);
}
const bool RpcRequest::hasArray(QString fieldName) const
{
return this->hasField(fieldName, OBS_DATA_ARRAY);
}
const bool RpcRequest::hasObject(QString fieldName) const
{
return this->hasField(fieldName, OBS_DATA_OBJECT);
}

65
src/rpc/RpcRequest.h Normal file
View File

@ -0,0 +1,65 @@
/*
obs-websocket
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
*/
#pragma once
#include <obs-data.h>
#include <QtCore/QString>
#include "../obs-websocket.h"
// forward declarations
class RpcResponse;
class RpcRequest
{
public:
explicit RpcRequest(const QString& messageId, const QString& methodName, obs_data_t* params);
const QString& messageId() const
{
return _messageId;
}
const QString& methodName() const
{
return _methodName;
}
const OBSData parameters() const
{
return OBSData(_parameters);
}
const RpcResponse success(obs_data_t* additionalFields = nullptr) const;
const RpcResponse failed(const QString& errorMessage, obs_data_t* additionalFields = nullptr) const;
const bool hasField(QString fieldName, obs_data_type expectedFieldType = OBS_DATA_NULL,
obs_data_number_type expectedNumberType = OBS_DATA_NUM_INVALID) const;
const bool hasBool(QString fieldName) const;
const bool hasString(QString fieldName) const;
const bool hasNumber(QString fieldName, obs_data_number_type expectedNumberType = OBS_DATA_NUM_INVALID) const;
const bool hasInteger(QString fieldName) const;
const bool hasDouble(QString fieldName) const;
const bool hasArray(QString fieldName) const;
const bool hasObject(QString fieldName) const;
private:
const QString _messageId;
const QString _methodName;
OBSDataAutoRelease _parameters;
};

48
src/rpc/RpcResponse.cpp Normal file
View File

@ -0,0 +1,48 @@
/*
obs-websocket
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
*/
#include "RpcResponse.h"
#include "RpcRequest.h"
RpcResponse::RpcResponse(
Status status, const QString& messageId,
const QString& methodName, obs_data_t* additionalFields
) :
_status(status),
_messageId(messageId),
_methodName(methodName),
_additionalFields(nullptr)
{
if (additionalFields) {
_additionalFields = obs_data_create();
obs_data_apply(_additionalFields, additionalFields);
}
}
const RpcResponse RpcResponse::ok(const RpcRequest& request, obs_data_t* additionalFields)
{
RpcResponse response(Status::Ok, request.messageId(), request.methodName(), additionalFields);
return response;
}
const RpcResponse RpcResponse::fail(const RpcRequest& request, const QString& errorMessage, obs_data_t* additionalFields)
{
RpcResponse response(Status::Error, request.messageId(), request.methodName(), additionalFields);
response._errorMessage = errorMessage;
return response;
}

70
src/rpc/RpcResponse.h Normal file
View File

@ -0,0 +1,70 @@
/*
obs-websocket
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
*/
#pragma once
#include <obs-data.h>
#include <QtCore/QString>
#include "../obs-websocket.h"
class RpcRequest;
class RpcResponse
{
public:
enum Status { Unknown, Ok, Error };
static RpcResponse ofRequest(const RpcRequest& request);
static const RpcResponse ok(const RpcRequest& request, obs_data_t* additionalFields = nullptr);
static const RpcResponse fail(
const RpcRequest& request, const QString& errorMessage,
obs_data_t* additionalFields = nullptr
);
Status status() {
return _status;
}
const QString& messageId() const {
return _messageId;
}
const QString& methodName() const {
return _methodName;
}
const QString& errorMessage() const {
return _errorMessage;
}
const OBSData additionalFields() const {
return OBSData(_additionalFields);
}
private:
explicit RpcResponse(
Status status,
const QString& messageId, const QString& methodName,
obs_data_t* additionalFields = nullptr
);
const Status _status;
const QString _messageId;
const QString _methodName;
QString _errorMessage;
OBSDataAutoRelease _additionalFields;
};