obs-websocket/WSRequestHandler.cpp
2017-03-19 23:57:01 +01:00

736 lines
20 KiB
C++

/*
obs-websocket
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
Copyright (C) 2017 Mikhail Swift <https://github.com/mikhailswift>
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 "WSRequestHandler.h"
#include "obs-websocket.h"
#include "Config.h"
#include "Utils.h"
WSRequestHandler::WSRequestHandler(QWebSocket *client) :
_messageId(0),
_requestType(""),
_requestData(nullptr)
{
_client = client;
messageMap["GetVersion"] = WSRequestHandler::HandleGetVersion;
messageMap["GetAuthRequired"] = WSRequestHandler::HandleGetAuthRequired;
messageMap["Authenticate"] = WSRequestHandler::HandleAuthenticate;
messageMap["SetCurrentScene"] = WSRequestHandler::HandleSetCurrentScene;
messageMap["GetCurrentScene"] = WSRequestHandler::HandleGetCurrentScene;
messageMap["GetSceneList"] = WSRequestHandler::HandleGetSceneList;
messageMap["SetSourceRender"] = WSRequestHandler::HandleSetSceneItemRender; // Retrocompat
messageMap["SetSceneItemRender"] = WSRequestHandler::HandleSetSceneItemRender;
messageMap["SetSceneItemPosition"] = WSRequestHandler::HandleSetSceneItemPosition;
messageMap["SetSceneItemTransform"] = WSRequestHandler::HandleSetSceneItemTransform;
messageMap["SetSceneItemCrop"] = WSRequestHandler::HandleSetSceneItemCrop;
messageMap["GetStreamingStatus"] = WSRequestHandler::HandleGetStreamingStatus;
messageMap["StartStopStreaming"] = WSRequestHandler::HandleStartStopStreaming;
messageMap["StartStopRecording"] = WSRequestHandler::HandleStartStopRecording;
messageMap["StartStreaming"] = WSRequestHandler::HandleStartStreaming;
messageMap["StopStreaming"] = WSRequestHandler::HandleStopStreaming;
messageMap["StartRecording"] = WSRequestHandler::HandleStartRecording;
messageMap["StopRecording"] = WSRequestHandler::HandleStopRecording;
messageMap["GetTransitionList"] = WSRequestHandler::HandleGetTransitionList;
messageMap["GetCurrentTransition"] = WSRequestHandler::HandleGetCurrentTransition;
messageMap["SetCurrentTransition"] = WSRequestHandler::HandleSetCurrentTransition;
messageMap["SetTransitionDuration"] = WSRequestHandler::HandleSetTransitionDuration;
messageMap["GetTransitionDuration"] = WSRequestHandler::HandleGetTransitionDuration;
messageMap["SetVolume"] = WSRequestHandler::HandleSetVolume;
messageMap["GetVolume"] = WSRequestHandler::HandleGetVolume;
messageMap["ToggleMute"] = WSRequestHandler::HandleToggleMute;
messageMap["SetMute"] = WSRequestHandler::HandleSetMute;
messageMap["SetCurrentSceneCollection"] = WSRequestHandler::HandleSetCurrentSceneCollection;
messageMap["GetCurrentSceneCollection"] = WSRequestHandler::HandleGetCurrentSceneCollection;
messageMap["ListSceneCollections"] = WSRequestHandler::HandleListSceneCollections;
messageMap["SetCurrentProfile"] = WSRequestHandler::HandleSetCurrentProfile;
messageMap["GetCurrentProfile"] = WSRequestHandler::HandleGetCurrentProfile;
messageMap["ListProfiles"] = WSRequestHandler::HandleListProfiles;
authNotRequired.insert("GetVersion");
authNotRequired.insert("GetAuthRequired");
authNotRequired.insert("Authenticate");
}
void WSRequestHandler::processIncomingMessage(QString textMessage)
{
QByteArray msgData = textMessage.toUtf8();
const char *msg = msgData;
_requestData = obs_data_create_from_json(msg);
if (!_requestData)
{
if (!msg)
{
msg = "<null pointer>";
}
blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg);
SendErrorResponse("invalid JSON payload");
return;
}
_requestType = obs_data_get_string(_requestData, "request-type");
_messageId = obs_data_get_string(_requestData, "message-id");
if (Config::Current()->AuthRequired
&& (_client->property(PROP_AUTHENTICATED).toBool() == false)
&& (authNotRequired.find(_requestType) == authNotRequired.end()))
{
SendErrorResponse("Not Authenticated");
return;
}
void (*handlerFunc)(WSRequestHandler*) = (messageMap[_requestType]);
if (handlerFunc != NULL)
{
handlerFunc(this);
}
else
{
SendErrorResponse("invalid request type");
}
obs_data_release(_requestData);
}
WSRequestHandler::~WSRequestHandler()
{
if (_requestData != NULL)
{
obs_data_release(_requestData);
}
}
void WSRequestHandler::SendOKResponse(obs_data_t *additionalFields)
{
obs_data_t *response = obs_data_create();
obs_data_set_string(response, "status", "ok");
obs_data_set_string(response, "message-id", _messageId);
if (additionalFields != NULL)
{
obs_data_apply(response, additionalFields);
}
_client->sendTextMessage(obs_data_get_json(response));
obs_data_release(response);
}
void WSRequestHandler::SendErrorResponse(const char *errorMessage)
{
obs_data_t *response = obs_data_create();
obs_data_set_string(response, "status", "error");
obs_data_set_string(response, "error", errorMessage);
obs_data_set_string(response, "message-id", _messageId);
_client->sendTextMessage(obs_data_get_json(response));
obs_data_release(response);
}
void WSRequestHandler::HandleGetVersion(WSRequestHandler *owner)
{
const char* obs_version = Utils::OBSVersionString();
obs_data_t *data = obs_data_create();
obs_data_set_double(data, "version", 1.2);
obs_data_set_string(data, "obs-websocket-version", OBS_WEBSOCKET_VERSION);
obs_data_set_string(data, "obs-studio-version", obs_version);
owner->SendOKResponse(data);
obs_data_release(data);
bfree((void*)obs_version);
}
void WSRequestHandler::HandleGetAuthRequired(WSRequestHandler *owner)
{
bool authRequired = Config::Current()->AuthRequired;
obs_data_t *data = obs_data_create();
obs_data_set_bool(data, "authRequired", authRequired);
if (authRequired)
{
obs_data_set_string(data, "challenge", Config::Current()->SessionChallenge);
obs_data_set_string(data, "salt", Config::Current()->Salt);
}
owner->SendOKResponse(data);
obs_data_release(data);
}
void WSRequestHandler::HandleAuthenticate(WSRequestHandler *owner)
{
const char *auth = obs_data_get_string(owner->_requestData, "auth");
if (!auth || strlen(auth) < 1)
{
owner->SendErrorResponse("auth not specified!");
return;
}
if ((owner->_client->property(PROP_AUTHENTICATED).toBool() == false) && Config::Current()->CheckAuth(auth))
{
owner->_client->setProperty(PROP_AUTHENTICATED, true);
owner->SendOKResponse();
}
else
{
owner->SendErrorResponse("Authentication Failed.");
}
}
void WSRequestHandler::HandleSetCurrentScene(WSRequestHandler *owner)
{
const char *sceneName = obs_data_get_string(owner->_requestData, "scene-name");
obs_source_t *source = obs_get_source_by_name(sceneName);
if (source)
{
obs_frontend_set_current_scene(source);
owner->SendOKResponse();
}
else
{
owner->SendErrorResponse("requested scene does not exist");
}
obs_source_release(source);
}
void WSRequestHandler::HandleGetCurrentScene(WSRequestHandler *owner)
{
obs_source_t *current_scene = obs_frontend_get_current_scene();
const char *name = obs_source_get_name(current_scene);
obs_data_array_t *scene_items = Utils::GetSceneItems(current_scene);
obs_data_t *data = obs_data_create();
obs_data_set_string(data, "name", name);
obs_data_set_array(data, "sources", scene_items);
owner->SendOKResponse(data);
obs_data_release(data);
obs_data_array_release(scene_items);
obs_source_release(current_scene);
}
void WSRequestHandler::HandleGetSceneList(WSRequestHandler *owner)
{
obs_source_t *current_scene = obs_frontend_get_current_scene();
obs_data_array_t *scenes = Utils::GetScenes();
obs_data_t *data = obs_data_create();
obs_data_set_string(data, "current-scene", obs_source_get_name(current_scene));
obs_data_set_array(data, "scenes", scenes);
owner->SendOKResponse(data);
obs_data_release(data);
obs_data_array_release(scenes);
obs_source_release(current_scene);
}
void WSRequestHandler::HandleSetSceneItemRender(WSRequestHandler *owner)
{
const char *itemName = obs_data_get_string(owner->_requestData, "source");
bool isVisible = obs_data_get_bool(owner->_requestData, "render");
if (itemName == NULL)
{
owner->SendErrorResponse("invalid request parameters");
return;
}
const char *sceneName = obs_data_get_string(owner->_requestData, "scene-name");
obs_source_t *scene = Utils::GetSceneFromNameOrCurrent(sceneName);
if (scene == NULL) {
owner->SendErrorResponse("requested scene doesn't exist");
return;
}
obs_sceneitem_t *sceneItem = Utils::GetSceneItemFromName(scene, itemName);
if (sceneItem != NULL)
{
obs_sceneitem_set_visible(sceneItem, isVisible);
obs_sceneitem_release(sceneItem);
owner->SendOKResponse();
}
else
{
owner->SendErrorResponse("specified scene item doesn't exist");
}
obs_source_release(scene);
}
void WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler *owner)
{
obs_data_t *data = obs_data_create();
obs_data_set_bool(data, "streaming", obs_frontend_streaming_active());
obs_data_set_bool(data, "recording", obs_frontend_recording_active());
obs_data_set_bool(data, "preview-only", false);
owner->SendOKResponse(data);
obs_data_release(data);
}
void WSRequestHandler::HandleStartStopStreaming(WSRequestHandler *owner)
{
if (obs_frontend_streaming_active())
{
obs_frontend_streaming_stop();
}
else
{
obs_frontend_streaming_start();
}
owner->SendOKResponse();
}
void WSRequestHandler::HandleStartStopRecording(WSRequestHandler *owner)
{
if (obs_frontend_recording_active())
{
obs_frontend_recording_stop();
}
else
{
obs_frontend_recording_start();
}
owner->SendOKResponse();
}
void WSRequestHandler::HandleStartStreaming(WSRequestHandler *owner)
{
if (obs_frontend_streaming_active() == false)
obs_frontend_streaming_start();
owner->SendOKResponse();
}
void WSRequestHandler::HandleStopStreaming(WSRequestHandler *owner)
{
if (obs_frontend_streaming_active() == true)
obs_frontend_streaming_stop();
owner->SendOKResponse();
}
void WSRequestHandler::HandleStartRecording(WSRequestHandler *owner)
{
if (obs_frontend_recording_active() == false)
obs_frontend_recording_start();
owner->SendOKResponse();
}
void WSRequestHandler::HandleStopRecording(WSRequestHandler *owner)
{
if (obs_frontend_recording_active() == true)
obs_frontend_recording_stop();
owner->SendOKResponse();
}
void WSRequestHandler::HandleGetTransitionList(WSRequestHandler *owner)
{
obs_source_t *current_transition = obs_frontend_get_current_transition();
obs_frontend_source_list transitionList = {};
obs_frontend_get_transitions(&transitionList);
obs_data_array_t* transitions = obs_data_array_create();
for (size_t i = 0; i < transitionList.sources.num; i++)
{
obs_source_t* transition = transitionList.sources.array[i];
obs_data_t *obj = obs_data_create();
obs_data_set_string(obj, "name", obs_source_get_name(transition));
obs_data_array_push_back(transitions, obj);
obs_data_release(obj);
}
obs_frontend_source_list_free(&transitionList);
obs_data_t *response = obs_data_create();
obs_data_set_string(response, "current-transition", obs_source_get_name(current_transition));
obs_data_set_array(response, "transitions", transitions);
owner->SendOKResponse(response);
obs_data_release(response);
obs_data_array_release(transitions);
obs_source_release(current_transition);
}
void WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler *owner)
{
obs_source_t *current_transition = obs_frontend_get_current_transition();
obs_data_t *response = obs_data_create();
obs_data_set_string(response, "name", obs_source_get_name(current_transition));
if (!obs_transition_fixed(current_transition))
{
obs_data_set_int(response, "duration", Utils::GetTransitionDuration());
}
owner->SendOKResponse(response);
obs_data_release(response);
obs_source_release(current_transition);
}
void WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler *owner)
{
const char *name = obs_data_get_string(owner->_requestData, "transition-name");
obs_source_t *transition = Utils::GetTransitionFromName(name);
if (transition)
{
obs_frontend_set_current_transition(transition);
owner->SendOKResponse();
obs_source_release(transition);
}
else
{
owner->SendErrorResponse("requested transition does not exist");
}
}
void WSRequestHandler::HandleSetTransitionDuration(WSRequestHandler *owner)
{
int ms = obs_data_get_int(owner->_requestData, "duration");
Utils::SetTransitionDuration(ms);
owner->SendOKResponse();
}
void WSRequestHandler::HandleGetTransitionDuration(WSRequestHandler *owner)
{
obs_data_t* response = obs_data_create();
obs_data_set_int(response, "transition-duration", Utils::GetTransitionDuration());
owner->SendOKResponse(response);
obs_data_release(response);
}
void WSRequestHandler::HandleSetVolume(WSRequestHandler *owner)
{
const char *item_name = obs_data_get_string(owner->_requestData, "source");
float item_volume = obs_data_get_double(owner->_requestData, "volume");
if (item_name == NULL || item_volume < 0.0 || item_volume > 1.0)
{
owner->SendErrorResponse("invalid request parameters");
return;
}
obs_source_t* item = obs_get_source_by_name(item_name);
if (!item)
{
owner->SendErrorResponse("specified source doesn't exist");
return;
}
obs_source_set_volume(item, item_volume);
owner->SendOKResponse();
obs_source_release(item);
}
void WSRequestHandler::HandleGetVolume(WSRequestHandler *owner)
{
const char *item_name = obs_data_get_string(owner->_requestData, "source");
if (item_name)
{
obs_source_t* item = obs_get_source_by_name(item_name);
obs_data_t* response = obs_data_create();
obs_data_set_string(response, "name", item_name);
obs_data_set_double(response, "volume", obs_source_get_volume(item));
obs_data_set_bool(response, "muted", obs_source_muted(item));
owner->SendOKResponse(response);
obs_data_release(response);
obs_source_release(item);
}
else
{
owner->SendErrorResponse("invalid request parameters");
}
}
void WSRequestHandler::HandleToggleMute(WSRequestHandler *owner) {
const char *item_name = obs_data_get_string(owner->_requestData, "source");
if (item_name == NULL)
{
owner->SendErrorResponse("invalid request parameters");
return;
}
obs_source_t* item = obs_get_source_by_name(item_name);
if (!item)
{
owner->SendErrorResponse("invalid request parameters");
return;
}
obs_source_set_muted(item, !obs_source_muted(item));
owner->SendOKResponse();
obs_source_release(item);
}
void WSRequestHandler::HandleSetMute(WSRequestHandler *owner)
{
const char *item_name = obs_data_get_string(owner->_requestData, "source");
bool mute = obs_data_get_bool(owner->_requestData, "mute");
if (item_name == NULL)
{
owner->SendErrorResponse("invalid request parameters");
return;
}
obs_source_t* item = obs_get_source_by_name(item_name);
if (!item)
{
owner->SendErrorResponse("specified source doesn't exist");
return;
}
obs_source_set_muted(item, mute);
owner->SendOKResponse();
obs_source_release(item);
}
void WSRequestHandler::HandleSetSceneItemPosition(WSRequestHandler *owner)
{
const char *item_name = obs_data_get_string(owner->_requestData, "item");
if (!item_name)
{
owner->SendErrorResponse("invalid request parameters");
return;
}
const char *scene_name = obs_data_get_string(owner->_requestData, "scene-name");
obs_source_t *scene = Utils::GetSceneFromNameOrCurrent(scene_name);
if (scene == NULL) {
owner->SendErrorResponse("requested scene could not be found");
return;
}
vec2 item_position = {0};
item_position.x = obs_data_get_double(owner->_requestData, "x");
item_position.y = obs_data_get_double(owner->_requestData, "y");
obs_sceneitem_t *scene_item = Utils::GetSceneItemFromName(scene, item_name);
if (scene_item)
{
obs_sceneitem_set_pos(scene_item, &item_position);
obs_sceneitem_release(scene_item);
owner->SendOKResponse();
}
else
{
owner->SendErrorResponse("specified scene item doesn't exist");
}
obs_source_release(scene);
}
void WSRequestHandler::HandleSetSceneItemTransform(WSRequestHandler *owner)
{
const char *item_name = obs_data_get_string(owner->_requestData, "item");
if (!item_name)
{
owner->SendErrorResponse("invalid request parameters");
return;
}
const char *scene_name = obs_data_get_string(owner->_requestData, "scene-name");
obs_source_t* scene = Utils::GetSceneFromNameOrCurrent(scene_name);
if (scene == NULL) {
owner->SendErrorResponse("requested scene doesn't exist");
return;
}
vec2 scale;
scale.x = obs_data_get_double(owner->_requestData, "x-scale");
scale.y = obs_data_get_double(owner->_requestData, "y-scale");
float rotation = obs_data_get_double(owner->_requestData, "rotation");
obs_sceneitem_t *scene_item = Utils::GetSceneItemFromName(scene, item_name);
if (scene_item)
{
obs_sceneitem_set_scale(scene_item, &scale);
obs_sceneitem_set_rot(scene_item, rotation);
obs_sceneitem_release(scene_item);
owner->SendOKResponse();
}
else
{
owner->SendErrorResponse("specified scene item doesn't exist");
}
obs_source_release(scene);
}
void WSRequestHandler::HandleSetSceneItemCrop(WSRequestHandler *owner)
{
const char *item_name = obs_data_get_string(owner->_requestData, "item");
if (!item_name)
{
owner->SendErrorResponse("invalid request parameters");
return;
}
const char *scene_name = obs_data_get_string(owner->_requestData, "scene-name");
obs_source_t* scene = Utils::GetSceneFromNameOrCurrent(scene_name);
if (scene == NULL) {
owner->SendErrorResponse("requested scene doesn't exist");
return;
}
obs_sceneitem_t *scene_item = Utils::GetSceneItemFromName(scene, item_name);
if (scene_item)
{
struct obs_sceneitem_crop crop = { 0 };
crop.top = obs_data_get_int(owner->_requestData, "top");
crop.bottom = obs_data_get_int(owner->_requestData, "bottom");;
crop.left = obs_data_get_int(owner->_requestData, "left");;
crop.right = obs_data_get_int(owner->_requestData, "right");
obs_sceneitem_set_crop(scene_item, &crop);
obs_sceneitem_release(scene_item);
owner->SendOKResponse();
}
else
{
owner->SendErrorResponse("specified scene item doesn't exist");
}
obs_source_release(scene);
}
void WSRequestHandler::HandleSetCurrentSceneCollection(WSRequestHandler *owner)
{
const char* scene_collection = obs_data_get_string(owner->_requestData, "sc-name");
if (scene_collection)
{
// TODO : Check if specified profile exists and if changing is allowed
obs_frontend_set_current_scene_collection(scene_collection);
owner->SendOKResponse();
}
else
{
owner->SendErrorResponse("invalid request parameters");
}
}
void WSRequestHandler::HandleGetCurrentSceneCollection(WSRequestHandler *owner)
{
obs_data_t *response = obs_data_create();
obs_data_set_string(response, "sc-name", obs_frontend_get_current_scene_collection());
owner->SendOKResponse(response);
obs_data_release(response);
}
void WSRequestHandler::HandleListSceneCollections(WSRequestHandler *owner)
{
obs_data_array_t *scene_collections = Utils::GetSceneCollections();
obs_data_t *response = obs_data_create();
obs_data_set_array(response, "scene-collections", scene_collections);
owner->SendOKResponse(response);
obs_data_release(response);
obs_data_array_release(scene_collections);
}
void WSRequestHandler::HandleSetCurrentProfile(WSRequestHandler *owner)
{
const char* profile_name = obs_data_get_string(owner->_requestData, "profile-name");
if (profile_name)
{
// TODO : check if profile exists
obs_frontend_set_current_profile(profile_name);
owner->SendOKResponse();
}
else
{
owner->SendErrorResponse("invalid request parameters");
}
}
void WSRequestHandler::HandleGetCurrentProfile(WSRequestHandler *owner)
{
obs_data_t *response = obs_data_create();
obs_data_set_string(response, "profile-name", obs_frontend_get_current_profile());
owner->SendOKResponse(response);
obs_data_release(response);
}
void WSRequestHandler::HandleListProfiles(WSRequestHandler *owner)
{
obs_data_array_t *profiles = Utils::GetProfiles();
obs_data_t *response = obs_data_create();
obs_data_set_array(response, "profiles", profiles);
owner->SendOKResponse(response);
obs_data_release(response);
obs_data_array_release(profiles);
}
void WSRequestHandler::ErrNotImplemented(WSRequestHandler *owner)
{
owner->SendErrorResponse("not implemented");
}