From 807a1501b700337d9e33c4c1c98ac46b6a345894 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Mon, 3 May 2021 13:31:22 -0700 Subject: [PATCH] base: Refactor request stuff and finish more logic --- CMakeLists.txt | 4 + src/WebSocketProtocol.cpp | 38 ++--- src/WebSocketProtocol.h | 1 - src/WebSocketServer.cpp | 6 +- src/WebSocketServer.h | 8 +- src/WebSocketSession.cpp | 25 +++- src/WebSocketSession.h | 9 ++ src/plugin-macros.generated.h | 5 +- src/requesthandler/RequestHandler.cpp | 30 ++-- src/requesthandler/RequestHandler.h | 139 ++---------------- src/requesthandler/RequestHandler_General.cpp | 9 ++ src/requesthandler/rpc/Request.cpp | 11 ++ src/requesthandler/rpc/Request.h | 16 +- src/requesthandler/rpc/RequestResult.cpp | 18 +++ src/requesthandler/rpc/RequestResult.h | 14 ++ src/requesthandler/rpc/RequestStatus.h | 106 +++++++++++++ 16 files changed, 250 insertions(+), 189 deletions(-) create mode 100644 src/requesthandler/RequestHandler_General.cpp create mode 100644 src/requesthandler/rpc/RequestResult.cpp create mode 100644 src/requesthandler/rpc/RequestResult.h create mode 100644 src/requesthandler/rpc/RequestStatus.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f8a4e340..f9e0a9ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,9 @@ set(obs-websocket_SOURCES src/WebSocketProtocol.cpp src/WebSocketSession.cpp src/requesthandler/RequestHandler.cpp + src/requesthandler/RequestHandler_General.cpp src/requesthandler/rpc/Request.cpp + src/requesthandler/rpc/RequestResult.cpp src/forms/SettingsDialog.cpp src/utils/Json.cpp src/utils/Crypto.cpp @@ -89,6 +91,8 @@ set(obs-websocket_HEADERS src/WebSocketSession.h src/requesthandler/RequestHandler.h src/requesthandler/rpc/Request.h + src/requesthandler/rpc/RequestResult.h + src/requesthandler/rpc/RequestStatus.h src/forms/SettingsDialog.h src/utils/Utils.h) diff --git a/src/WebSocketProtocol.cpp b/src/WebSocketProtocol.cpp index 3a04ba82..1cf40bef 100644 --- a/src/WebSocketProtocol.cpp +++ b/src/WebSocketProtocol.cpp @@ -1,7 +1,8 @@ #include "WebSocketProtocol.h" -#include "obs-websocket.h" -#include "utils/Utils.h" +#include "requesthandler/RequestHandler.h" +#include "requesthandler/rpc/RequestStatus.h" +#include "utils/Utils.h" #include "plugin-macros.generated.h" bool IsSupportedRpcVersion(uint8_t requestedVersion) @@ -98,25 +99,22 @@ WebSocketProtocol::ProcessResult WebSocketProtocol::ProcessMessage(SessionPtr se return ret; } - auto requestHandler = RequestHandler(session); - RequestHandler::RequestResult result; - if (incomingMessage.contains("requestData")) { - result = requestHandler.ProcessRequest(incomingMessage["requestType"], incomingMessage["requestData"]); - } else { - result = requestHandler.ProcessRequest(incomingMessage["requestType"]); - } + RequestHandler requestHandler; + Request request(session->RpcVersion(), session->IgnoreNonFatalRequestChecks(), incomingMessage["requestType"], incomingMessage["requestData"]); + + RequestResult requestResult = requestHandler.ProcessRequest(request); ret.result["messageType"] = "RequestResponse"; ret.result["requestType"] = incomingMessage["requestType"]; ret.result["requestId"] = incomingMessage["requestId"]; ret.result["requestStatus"] = { - {"result", result.statusCode == RequestHandler::RequestStatus::Success}, - {"code", result.statusCode} + {"result", requestResult.StatusCode == RequestStatus::Success}, + {"code", requestResult.StatusCode} }; - if (result.comment != "") - ret.result["requestStatus"]["comment"] = result.comment; - if (!result.responseData.is_null()) - ret.result["responseData"] = result.responseData; + if (!requestResult.Comment.empty()) + ret.result["requestStatus"]["comment"] = requestResult.Comment; + if (requestResult.ResponseData.is_object()) + ret.result["responseData"] = requestResult.ResponseData; return ret; } else if (messageType == "RequestBatch") { @@ -131,19 +129,13 @@ WebSocketProtocol::ProcessResult WebSocketProtocol::ProcessMessage(SessionPtr se return ret; } - auto webSocketServer = GetWebSocketServer(); - if (!webSocketServer) { - blog(LOG_ERROR, "[WebSocketProtocol::ProcessMessage] Unable to fetch websocket server instance!"); - return ret; - } - - if (webSocketServer->AuthenticationRequired) { + if (session->AuthenticationRequired()) { if (!incomingMessage.contains("authentication")) { ret.closeCode = WebSocketServer::WebSocketCloseCode::InvalidIdentifyParameter; ret.closeReason = "Your `Identify` payload is missing an `authentication` string, however authentication is required."; return ret; } - if (!Utils::Crypto::CheckAuthenticationString(webSocketServer->AuthenticationSecret, session->Challenge(), incomingMessage["authentication"])) { + if (!Utils::Crypto::CheckAuthenticationString(session->Secret(), session->Challenge(), incomingMessage["authentication"])) { ret.closeCode = WebSocketServer::WebSocketCloseCode::AuthenticationFailed; ret.closeReason = "Authentication failed."; return ret; diff --git a/src/WebSocketProtocol.h b/src/WebSocketProtocol.h index 739a32ee..2b57bc31 100644 --- a/src/WebSocketProtocol.h +++ b/src/WebSocketProtocol.h @@ -6,7 +6,6 @@ #include "WebSocketServer.h" #include "WebSocketSession.h" -#include "requesthandler/RequestHandler.h" namespace WebSocketProtocol { const std::vector SupportedRpcVersions{ diff --git a/src/WebSocketServer.cpp b/src/WebSocketServer.cpp index 26a6ca8c..585c7757 100644 --- a/src/WebSocketServer.cpp +++ b/src/WebSocketServer.cpp @@ -4,11 +4,11 @@ #include #include +#include "obs-websocket.h" + #include "WebSocketServer.h" #include "WebSocketProtocol.h" -#include "obs-websocket.h" #include "Config.h" -#include "utils/Utils.h" #include "plugin-macros.generated.h" @@ -260,6 +260,7 @@ void WebSocketServer::onOpen(websocketpp::connection_hdl hdl) // Configure session details session->SetRemoteAddress(conn->get_remote_endpoint()); session->SetConnectedAt(QDateTime::currentSecsSinceEpoch()); + session->SetAuthenticationRequired(AuthenticationRequired); std::string contentType = conn->get_request_header("Content-Type"); if (contentType == "") { ; @@ -280,6 +281,7 @@ void WebSocketServer::onOpen(websocketpp::connection_hdl hdl) helloMessage["availableRequests"] = WebSocketProtocol::GetRequestList(); helloMessage["availableEvents"] = WebSocketProtocol::GetEventList(); if (AuthenticationRequired) { + session->SetSecret(AuthenticationSecret); std::string sessionChallenge = Utils::Crypto::GenerateSalt(); session->SetChallenge(sessionChallenge); helloMessage["authentication"] = {}; diff --git a/src/WebSocketServer.h b/src/WebSocketServer.h index da00fb39..da25a484 100644 --- a/src/WebSocketServer.h +++ b/src/WebSocketServer.h @@ -5,20 +5,18 @@ #include #include -#include +#include "utils/Utils.h" #include #include #include "WebSocketSession.h" -using json = nlohmann::json; - class WebSocketServer : QObject { Q_OBJECT public: - enum WebSocketCloseCode: uint16_t { + enum WebSocketCloseCode { // Internal only DontClose = 0, // Reserved @@ -45,7 +43,7 @@ class WebSocketServer : QObject InvalidContentType = 4010, }; - enum WebSocketEncoding: uint8_t { + enum WebSocketEncoding { Json, MsgPack }; diff --git a/src/WebSocketSession.cpp b/src/WebSocketSession.cpp index 8ebbe4c0..b2fa46be 100644 --- a/src/WebSocketSession.cpp +++ b/src/WebSocketSession.cpp @@ -1,5 +1,3 @@ -#include - #include "WebSocketSession.h" #include "plugin-macros.generated.h" @@ -72,6 +70,29 @@ void WebSocketSession::SetEncoding(uint8_t encoding) _encoding.store(encoding); } +bool WebSocketSession::AuthenticationRequired() +{ + return _authenticationRequired.load(); +} + +void WebSocketSession::SetAuthenticationRequired(bool required) +{ + _authenticationRequired.store(required); +} + +std::string WebSocketSession::Secret() +{ + std::lock_guard lock(_secretMutex); + std::string ret(_secret); + return ret; +} + +void WebSocketSession::SetSecret(std::string secret) +{ + std::lock_guard lock(_secretMutex); + _secret = secret; +} + std::string WebSocketSession::Challenge() { std::lock_guard lock(_challengeMutex); diff --git a/src/WebSocketSession.h b/src/WebSocketSession.h index 2ec918b7..8fe9ab9d 100644 --- a/src/WebSocketSession.h +++ b/src/WebSocketSession.h @@ -28,6 +28,12 @@ class WebSocketSession uint8_t Encoding(); void SetEncoding(uint8_t encoding); + bool AuthenticationRequired(); + void SetAuthenticationRequired(bool required); + + std::string Secret(); + void SetSecret(std::string secret); + std::string Challenge(); void SetChallenge(std::string challenge); @@ -55,6 +61,9 @@ class WebSocketSession std::atomic _incomingMessages; std::atomic _outgoingMessages; std::atomic _encoding; + std::atomic _authenticationRequired; + std::mutex _secretMutex; + std::string _secret; std::mutex _challengeMutex; std::string _challenge; std::atomic _rpcVersion; diff --git a/src/plugin-macros.generated.h b/src/plugin-macros.generated.h index 3e776c01..7a4bcaf6 100644 --- a/src/plugin-macros.generated.h +++ b/src/plugin-macros.generated.h @@ -18,10 +18,11 @@ with this program. If not, see #pragma once +#include +#define blog(level, msg, ...) blog(level, "[obs-websocket] " msg, ##__VA_ARGS__) + #define OBS_WEBSOCKET_VERSION "5.0.0" #define OBS_WEBSOCKET_RPC_VERSION 1 #define QT_TO_UTF8(str) str.toUtf8().constData() - -#define blog(level, msg, ...) blog(level, "[obs-websocket] " msg, ##__VA_ARGS__) diff --git a/src/requesthandler/RequestHandler.cpp b/src/requesthandler/RequestHandler.cpp index 844fc407..a2a2b535 100644 --- a/src/requesthandler/RequestHandler.cpp +++ b/src/requesthandler/RequestHandler.cpp @@ -1,27 +1,23 @@ -#include -#include - #include "RequestHandler.h" #include "../plugin-macros.generated.h" -RequestHandler::RequestHandler(bool ignoreNonFatalRequestChecks, uint8_t rpcVersion) : - _ignoreNonFatalRequestChecks(ignoreNonFatalRequestChecks), - _rpcVersion(rpcVersion) +const std::map RequestHandler::_handlerMap { -} + {"GetVersion", &RequestHandler::GetVersion}, +}; -RequestHandler::RequestHandler(SessionPtr session) : - _ignoreNonFatalRequestChecks(session->IgnoreNonFatalRequestChecks()), - _rpcVersion(session->RpcVersion()) +RequestResult RequestHandler::ProcessRequest(const Request& request) { -} + if (!request.RequestData.is_null() && !request.RequestData.is_object()) + return RequestResult::Error(RequestStatus::InvalidRequestParameterDataType, "Your request data is not an object."); -RequestHandler::RequestResult RequestHandler::ProcessRequest(std::string requestType, json requestData) -{ - RequestHandler::RequestResult ret; + RequestMethodHandler handler; + try { + handler = _handlerMap.at(request.RequestType); + } catch (const std::out_of_range& oor) { + return RequestResult::Error(RequestStatus::UnknownRequestType, "Your request type is not valid."); + } - ret.statusCode = RequestHandler::RequestStatus::Success; - - return ret; + return std::bind(handler, this, std::placeholders::_1)(request); } \ No newline at end of file diff --git a/src/requesthandler/RequestHandler.h b/src/requesthandler/RequestHandler.h index 2cbcd2ec..7024cdc9 100644 --- a/src/requesthandler/RequestHandler.h +++ b/src/requesthandler/RequestHandler.h @@ -1,139 +1,20 @@ #pragma once -#include +#include #include "rpc/Request.h" -#include "../WebSocketSession.h" +#include "rpc/RequestResult.h" +#include "../utils/Utils.h" -using json = nlohmann::json; +class RequestHandler; +typedef RequestResult(RequestHandler::*RequestMethodHandler)(const Request&); class RequestHandler { public: - enum RequestStatus: uint16_t { - Unknown = 0, - - // For internal use to signify a successful parameter check - NoError = 10, - - Success = 100, - - // The request is denied because the client is not authenticated - AuthenticationMissing = 200, - // Connection has already been authenticated (for modules utilizing a request to provide authentication) - AlreadyAuthenticated = 201, - // Authentication request was denied (for modules utilizing a request to provide authentication) - AuthenticationDenied = 202, - // The `requestType` field is missing from the request data - RequestTypeMissing = 203, - // The request type is invalid (does not exist) - InvalidRequestType = 204, - // Generic error code (comment is expected to be provided) - GenericError = 205, - - // A required request parameter is missing - MissingRequestParameter = 300, - - // Generic invalid request parameter message - InvalidRequestParameter = 400, - // A request parameter has the wrong data type - InvalidRequestParameterDataType = 401, - // A request parameter (float or int) is out of valid range - RequestParameterOutOfRange = 402, - // A request parameter (string or array) is empty and cannot be - RequestParameterEmpty = 403, - - // An output is running and cannot be in order to perform the request (generic) - OutputRunning = 500, - // An output is not running and should be - OutputNotRunning = 501, - // Stream is running and cannot be - StreamRunning = 502, - // Stream is not running and should be - StreamNotRunning = 503, - // Record is running and cannot be - RecordRunning = 504, - // Record is not running and should be - RecordNotRunning = 505, - // Record is paused and cannot be - RecordPaused = 506, - // Replay buffer is running and cannot be - ReplayBufferRunning = 507, - // Replay buffer is not running and should be - ReplayBufferNotRunning = 508, - // Replay buffer is disabled and cannot be - ReplayBufferDisabled = 509, - // Studio mode is active and cannot be - StudioModeActive = 510, - // Studio mode is not active and should be - StudioModeNotActive = 511, - - // The specified source (obs_source_t) was of the invalid type (Eg. input instead of scene) - InvalidSourceType = 600, - // The specified source (obs_source_t) was not found (generic for input, filter, transition, scene) - SourceNotFound = 601, - // The specified source (obs_source_t) already exists. Applicable to inputs, filters, transitions, scenes - SourceAlreadyExists = 602, - // The specified input (obs_source_t-OBS_SOURCE_TYPE_FILTER) was not found - InputNotFound = 603, - // The specified input (obs_source_t-OBS_SOURCE_TYPE_INPUT) had the wrong kind - InvalidInputKind = 604, - // The specified filter (obs_source_t-OBS_SOURCE_TYPE_FILTER) was not found - FilterNotFound = 605, - // The specified transition (obs_source_t-OBS_SOURCE_TYPE_TRANSITION) was not found - TransitionNotFound = 606, - // The specified transition (obs_source_t-OBS_SOURCE_TYPE_TRANSITION) does not support setting its position (transition is of fixed type) - TransitionDurationFixed = 607, - // The specified scene (obs_source_t-OBS_SOURCE_TYPE_SCENE), (obs_scene_t) was not found - SceneNotFound = 608, - // The specified scene item (obs_sceneitem_t) was not found - SceneItemNotFound = 609, - // The specified scene collection was not found - SceneCollectionNotFound = 610, - // The specified profile was not found - ProfileNotFound = 611, - // The specified output (obs_output_t) was not found - OutputNotFound = 612, - // The specified encoder (obs_encoder_t) was not found - EncoderNotFound = 613, - // The specified service (obs_service_t) was not found - ServiceNotFound = 614, - // The specified hotkey was not found - HotkeyNotFound = 615, - // The specified directory was not found - DirectoryNotFound = 616, - // The specified config item (obs_config_t) was not found. Could be section or parameter name. - ConfigParameterNotFound = 617, - // The specified property (obs_properties_t) was not found - PropertyNotFound = 618, - - // Processing the request failed unexpectedly - RequestProcessingFailed = 700, - // Starting the Output failed - OutputStartFailed = 701, - // Duplicating the scene item failed - SceneItemDuplicationFailed = 702, - // Rendering the screenshot failed - ScreenshotRenderFailed = 703, - // Encoding the screenshot failed - ScreenshotEncodeFailed = 704, - // Saving the screenshot failed - ScreenshotSaveFailed = 705, - // Creating the directory failed - DirectoryCreationFailed = 706, - }; - - struct RequestResult { - RequestStatus statusCode; - json responseData = nullptr; - std::string comment; - }; - - RequestHandler(bool ignoreNonFatalRequestChecks, uint8_t rpcVersion); - RequestHandler(SessionPtr session); - - RequestResult ProcessRequest(std::string requestType, json requestData = json::object()); + RequestResult ProcessRequest(const Request& request); private: - bool _ignoreNonFatalRequestChecks; - uint8_t _rpcVersion; -}; \ No newline at end of file + RequestResult GetVersion(const Request&); + + static const std::map _handlerMap; +}; diff --git a/src/requesthandler/RequestHandler_General.cpp b/src/requesthandler/RequestHandler_General.cpp new file mode 100644 index 00000000..a0159f87 --- /dev/null +++ b/src/requesthandler/RequestHandler_General.cpp @@ -0,0 +1,9 @@ +#include "RequestHandler.h" + +#include "../plugin-macros.generated.h" + +RequestResult RequestHandler::GetVersion(const Request& request) +{ + json ret{{"test", "pp"}}; + return RequestResult::Success(ret); +} diff --git a/src/requesthandler/rpc/Request.cpp b/src/requesthandler/rpc/Request.cpp index d68b3564..28948d17 100644 --- a/src/requesthandler/rpc/Request.cpp +++ b/src/requesthandler/rpc/Request.cpp @@ -1,3 +1,14 @@ #include "Request.h" #include "../../plugin-macros.generated.h" + +Request::Request(uint8_t rpcVersion, bool ignoreNonFatalRequestChecks, std::string requestType, json requestData) : + RpcVersion(rpcVersion), + IgnoreNonFatalRequestChecks(ignoreNonFatalRequestChecks), + RequestType(requestType) +{ + if (!requestData.is_object()) + RequestData = json::object(); + else + RequestData = requestData; +} \ No newline at end of file diff --git a/src/requesthandler/rpc/Request.h b/src/requesthandler/rpc/Request.h index 94e3c265..650a8bb3 100644 --- a/src/requesthandler/rpc/Request.h +++ b/src/requesthandler/rpc/Request.h @@ -1,13 +1,13 @@ #pragma once -#include +#include "../../utils/Utils.h" -#include "../RequestHandler.h" - -class Request +struct Request { - public: - ; - private: - ; + Request(uint8_t rpcVersion, bool ignoreNonFatalRequestChecks, std::string requestType, json requestData = nullptr); + + uint8_t RpcVersion; + bool IgnoreNonFatalRequestChecks; + std::string RequestType; + json RequestData; }; \ No newline at end of file diff --git a/src/requesthandler/rpc/RequestResult.cpp b/src/requesthandler/rpc/RequestResult.cpp new file mode 100644 index 00000000..ba249d2f --- /dev/null +++ b/src/requesthandler/rpc/RequestResult.cpp @@ -0,0 +1,18 @@ +#include "RequestResult.h" + +RequestResult::RequestResult(RequestStatus statusCode, json responseData, std::string comment) : + StatusCode(statusCode), + ResponseData(responseData), + Comment(comment) +{ +} + +RequestResult RequestResult::Success(json responseData) +{ + return RequestResult(RequestStatus::Success, responseData, ""); +} + +RequestResult RequestResult::Error(RequestStatus statusCode, std::string comment) +{ + return RequestResult(statusCode, nullptr, comment); +} diff --git a/src/requesthandler/rpc/RequestResult.h b/src/requesthandler/rpc/RequestResult.h new file mode 100644 index 00000000..2d24d5e5 --- /dev/null +++ b/src/requesthandler/rpc/RequestResult.h @@ -0,0 +1,14 @@ +#pragma once + +#include "RequestStatus.h" +#include "../../utils/Utils.h" + +struct RequestResult +{ + RequestResult(RequestStatus statusCode = RequestStatus::Success, json responseData = nullptr, std::string comment = ""); + static RequestResult Success(json responseData = nullptr); + static RequestResult Error(RequestStatus statusCode, std::string comment = ""); + RequestStatus StatusCode; + json ResponseData; + std::string Comment; +}; diff --git a/src/requesthandler/rpc/RequestStatus.h b/src/requesthandler/rpc/RequestStatus.h new file mode 100644 index 00000000..83a38914 --- /dev/null +++ b/src/requesthandler/rpc/RequestStatus.h @@ -0,0 +1,106 @@ +#pragma once + +enum RequestStatus { + Unknown = 0, + + // For internal use to signify a successful parameter check + NoError = 10, + + Success = 100, + + // The request type is invalid (does not exist) + UnknownRequestType = 204, + // Generic error code (comment is expected to be provided) + GenericError = 205, + + // A required request parameter is missing + MissingRequestParameter = 300, + + // Generic invalid request parameter message + InvalidRequestParameter = 400, + // A request parameter has the wrong data type + InvalidRequestParameterDataType = 401, + // A request parameter (float or int) is out of valid range + RequestParameterOutOfRange = 402, + // A request parameter (string or array) is empty and cannot be + RequestParameterEmpty = 403, + + // An output is running and cannot be in order to perform the request (generic) + OutputRunning = 500, + // An output is not running and should be + OutputNotRunning = 501, + // Stream is running and cannot be + StreamRunning = 502, + // Stream is not running and should be + StreamNotRunning = 503, + // Record is running and cannot be + RecordRunning = 504, + // Record is not running and should be + RecordNotRunning = 505, + // Record is paused and cannot be + RecordPaused = 506, + // Replay buffer is running and cannot be + ReplayBufferRunning = 507, + // Replay buffer is not running and should be + ReplayBufferNotRunning = 508, + // Replay buffer is disabled and cannot be + ReplayBufferDisabled = 509, + // Studio mode is active and cannot be + StudioModeActive = 510, + // Studio mode is not active and should be + StudioModeNotActive = 511, + + // The specified source (obs_source_t) was of the invalid type (Eg. input instead of scene) + InvalidSourceType = 600, + // The specified source (obs_source_t) was not found (generic for input, filter, transition, scene) + SourceNotFound = 601, + // The specified source (obs_source_t) already exists. Applicable to inputs, filters, transitions, scenes + SourceAlreadyExists = 602, + // The specified input (obs_source_t-OBS_SOURCE_TYPE_FILTER) was not found + InputNotFound = 603, + // The specified input (obs_source_t-OBS_SOURCE_TYPE_INPUT) had the wrong kind + InvalidInputKind = 604, + // The specified filter (obs_source_t-OBS_SOURCE_TYPE_FILTER) was not found + FilterNotFound = 605, + // The specified transition (obs_source_t-OBS_SOURCE_TYPE_TRANSITION) was not found + TransitionNotFound = 606, + // The specified transition (obs_source_t-OBS_SOURCE_TYPE_TRANSITION) does not support setting its position (transition is of fixed type) + TransitionDurationFixed = 607, + // The specified scene (obs_source_t-OBS_SOURCE_TYPE_SCENE), (obs_scene_t) was not found + SceneNotFound = 608, + // The specified scene item (obs_sceneitem_t) was not found + SceneItemNotFound = 609, + // The specified scene collection was not found + SceneCollectionNotFound = 610, + // The specified profile was not found + ProfileNotFound = 611, + // The specified output (obs_output_t) was not found + OutputNotFound = 612, + // The specified encoder (obs_encoder_t) was not found + EncoderNotFound = 613, + // The specified service (obs_service_t) was not found + ServiceNotFound = 614, + // The specified hotkey was not found + HotkeyNotFound = 615, + // The specified directory was not found + DirectoryNotFound = 616, + // The specified config item (obs_config_t) was not found. Could be section or parameter name. + ConfigParameterNotFound = 617, + // The specified property (obs_properties_t) was not found + PropertyNotFound = 618, + + // Processing the request failed unexpectedly + RequestProcessingFailed = 700, + // Starting the Output failed + OutputStartFailed = 701, + // Duplicating the scene item failed + SceneItemDuplicationFailed = 702, + // Rendering the screenshot failed + ScreenshotRenderFailed = 703, + // Encoding the screenshot failed + ScreenshotEncodeFailed = 704, + // Saving the screenshot failed + ScreenshotSaveFailed = 705, + // Creating the directory failed + DirectoryCreationFailed = 706, +};